mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: upscale times out behind Cloudflare Tunnel due to blocking HTTP request
The upscale route held the HTTP connection open for the full duration of Python sidecar processing (30-300s). Behind proxies with connection timeouts (Cloudflare Tunnel: 100s), this caused HTTP 524 errors. The route now returns 202 Accepted immediately after upload validation and processes in the background. The result (downloadUrl, sizes, etc.) is delivered via the existing SSE progress channel. The frontend detects the 202 and waits for the SSE completion event instead of reading the XHR response body. A reconnect-safe completion store ensures results survive brief SSE disconnects. Closes #106
This commit is contained in:
@@ -32,11 +32,15 @@ export interface SingleFileProgress {
|
||||
stage?: string;
|
||||
percent: number;
|
||||
error?: string;
|
||||
result?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** In-memory store of job progress, keyed by jobId. */
|
||||
const jobProgressStore = new Map<string, JobProgress>();
|
||||
|
||||
/** Terminal single-file events kept for SSE reconnect replay. */
|
||||
const singleFileCompletions = new Map<string, SingleFileProgress>();
|
||||
|
||||
/** SSE listeners waiting for updates, keyed by jobId. */
|
||||
const listeners = new Map<string, Set<(data: JobProgress | SingleFileProgress) => void>>();
|
||||
|
||||
@@ -183,6 +187,16 @@ export function updateJobProgress(progress: JobProgress): void {
|
||||
export function updateSingleFileProgress(progress: Omit<SingleFileProgress, "type">): void {
|
||||
const event: SingleFileProgress = { ...progress, type: "single" };
|
||||
persistSingleFileProgress(progress);
|
||||
|
||||
if (progress.phase === "complete" || progress.phase === "failed") {
|
||||
if (singleFileCompletions.size >= 10_000) {
|
||||
const oldest = singleFileCompletions.keys().next().value;
|
||||
if (oldest) singleFileCompletions.delete(oldest);
|
||||
}
|
||||
singleFileCompletions.set(progress.jobId, event);
|
||||
setTimeout(() => singleFileCompletions.delete(progress.jobId), 120_000);
|
||||
}
|
||||
|
||||
const subs = listeners.get(progress.jobId);
|
||||
if (subs) {
|
||||
for (const cb of subs) {
|
||||
@@ -228,6 +242,13 @@ export async function registerProgressRoutes(app: FastifyInstance): Promise<void
|
||||
}
|
||||
}
|
||||
|
||||
const existingSingle = singleFileCompletions.get(jobId);
|
||||
if (existingSingle) {
|
||||
sendEvent(existingSingle);
|
||||
reply.raw.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to updates
|
||||
if (!listeners.has(jobId)) {
|
||||
listeners.set(jobId, new Set());
|
||||
|
||||
Reference in New Issue
Block a user