Init Stream — Per-Source Progress, Status Bar & SSE Error Details
Version: 3.1.0 Date: 2026-06-12
Goal: Give operators real-time visibility into which repos and paths succeed or fail during source initialization, without waiting for the full report — and surface real underlying errors over the SSE boundary.
Solution: Refactored the SSE init stream to yield one chunk per project/path (instead of one per source provider), carrying sourceName, progress { current, total }, and the action entries. The UI renders a live, fixed-shape status badge per source. Per-source start markers make a source appear the moment the backend starts it, and serializeError() flattens non-serializable Error objects before emission.
Features:
- Init report header shows a pill-shaped status bar (
⏳ Npending →✓ Nsuccess /✗ Nerrors) per source row, aligned consistently, with reserved width (no layout shift) - Source rows appear as soon as the backend starts them (start markers), no blank gap
- Reload clears the report immediately, then rebuilds progressively from the stream
Read config/Init configrows use the same pill style and status colors- Detailed entries grouped by repo/path with zebra striping, prominent repo chip, code-styled URLs (tooltip), and full error blocks (real FTP/HTTP error stack)
Implementation Details:
WebhookCapacityService.webhooksInitStream()andFtpCapacityService.ftpInitStream()— newasync*generators, one yield per project/path, plus an initial empty chunk as a start marker- All five provider
initSources()methods converted toasync*generators (yield*delegation) SourceService.initStream()forwardssourceName+progressper chunk and no longer filters empty chunksapp.controller.ts—res.flush()after each SSE write to defeat response bufferingserializeError()flattens errors to{ message, stack, code }(Errormessage/stackare non-enumerable, soJSON.stringifyturned them into{})- UI:
Status.tsxtracksperSourceProgress+streamingSourceOrder;SourceGroupReportrendersStatusBadge+ sharedErrorBlock+ grouping
Also includes: AWS S3 endpoint optional for standard S3 — backend Joi allows empty endpoint; frontend requires it only when the "S3 Compatible service" toggle is active.
Files Modified:
src/protocol/capacities/webhook-capacity/webhook-capacity.service.ts—webhooksInitStream(), start marker,serializeError()src/protocol/capacities/ftp-capacity/ftp-capacity.service.ts—ftpInitStream(), start marker, serialize FTP errorssrc/protocol/source/{gitlab,github,bitbucket,gitea,ftp}.service.ts— async generatorinitSources()src/protocol/source/source.service.ts—initStream()return type + forwardingsrc/app.controller.ts— flush SSE eventssrc/core/configuration/config.schema.ts—endpointJoi.string().optional().allow('')ui/src/pages/settings/Status.tsx—perSourceProgressstate, clear on reload, source orderui/src/pages/settings/report/InitReport.tsx— forwardperSourceProgress, placeholder sourcesui/src/pages/settings/report/SourceGroupReport.tsx—StatusBar/StatusBadge,ErrorBlock, grouping, zebraui/src/pages/settings/report/UnitActionReport.tsx—StatusPill/ErrorBlock, no lingering spinnerui/src/pages/settings/report/ValidationReport.tsx— status-coloredRead configtitleui/src/pages/storages/StorageEditor.tsx— conditionalendpointvalidationui/src/protocol/tower.api.ts—onSourcecallback typeui/src/protocol/tower.mock.ts— mock stream updated