File Deposit Monitoring

Summary

The admin deposit dashboard exposes a paginated view of all FileDeposit records, filterable by partner and status. Each deposit tracks the full lifecycle of an SFTP XML file from detection through ingestion. When a deposit fails, admins can retry it (reset + re-enqueue through the normal pipeline). Successfully ingested deposits can be re-ingested to reapply updated mappings — this creates a new FileDeposit record pointing to the archived file.

State Diagram

stateDiagram-v2
    state "Fichier détecté" as Detected
    state "Ingestion en cours" as Ingesting
    state "Ingéré avec succès" as Ingested
    state "Erreur d'ingestion" as Error
    state "Doublon ignoré" as Duplicate
    [*] --> Detected: chokidar detects file
    Detected --> Ingesting: enqueueIngestion() called
    Ingesting --> Ingested: ingestXmlFeed() succeeds
    Ingesting --> Error: ingestXmlFeed() throws
    Detected --> Duplicate: same checksum already ingested
    Error --> Detected: admin retries deposit
    Detected --> Ingesting: re-enqueued
    Ingested --> Detected: admin re-ingests deposit (new record)

Steps

1. List All Deposits (Actor: admin)

GET /admin/deposits — cursor-based pagination using compound (createdAt, id) cursor. Query parameters:

  • partnerId: filter by partner UUID
  • status: filter by status (detected, ingesting, ingested, error, duplicate)
  • cursor: base64url-encoded cursor from previous response
  • limit: page size, default 20, max 100

Returns { deposits: [...], nextCursor: string | null }. Each deposit includes partner name for display.

Outcome: Paginated list of deposits ordered newest-first

2. List Partner-Scoped Deposits (Actor: admin)

GET /admin/partners/:id/deposits — same cursor pagination but automatically scoped to one partner. Registered as a sub-route under /admin/partners/:id/deposits.

Outcome: Deposits for a specific partner

3. Retry Failed Deposit (Actor: admin)

POST /admin/deposits/:id/retry:

Pre-conditions:

  • Deposit must exist → 404 if not.
  • deposit.status must be "error" → 400 if not.
  • deposit.partner.status must not be "paused" → 400 if paused.

Steps:

  1. Reconstruct file path: /home/{sftpUsername}/feeds/{filename}.
  2. Reset fileDeposit.status → "detected", errorMessage → null.
  3. Call enqueueIngestion(depositId, filePath, partnerId, partnerName, filename, log) — uses the per-partner mutex from lib/file-watcher.ts.

Outcome: Deposit reset and re-enqueued, returns { success: true, message: "Retry launched" }

4. Re-ingest Completed Deposit (Actor: admin)

POST /admin/deposits/:id/reingest:

Pre-conditions:

  • Deposit must be "ingested" → 400 if not.
  • Partner must not be paused → 400 if paused.

Steps:

  1. List archive directory /home/{sftpUsername}/feeds/archive/.
  2. Find files matching {baseName}.* sorted descending (newest first).
  3. Create a new FileDeposit record (with checksum suffixed -reingest to bypass deduplication).
  4. Enqueue ingestion of the archive path.

Use case: admin corrected mappings and wants to reapply them to already-ingested data without waiting for the partner to resend the file.

Outcome: New deposit record created, archive file re-ingested

Error States

  • Deposit not found → 404 NOT_FOUND
  • Retry on non-error status → 400 BAD_REQUEST
  • Retry while partner is paused → 400 BAD_REQUEST
  • Reingest on non-ingested status → 400 BAD_REQUEST
  • No archived file found for reingest → 404 NOT_FOUND
  • Archive directory missing → 404 NOT_FOUND