Skip to content

Commit f2bc152

Browse files
committed
Fix SSE streaming for import log: disable compression and flush events
Next.js compress middleware buffers the entire response, preventing SSE events from reaching the client in real-time. Disable compress globally (reverse proxy handles compression in production), add explicit flush() after each write, and set anti-buffering headers (X-Accel-Buffering, Content-Encoding, no-transform). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019wMSFyaUDFVGr31qqm7tk7
1 parent d220087 commit f2bc152

3 files changed

Lines changed: 23 additions & 4 deletions

File tree

next.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ await jiti.import('./src/env');
1414
/** @type {import("next").NextConfig} */
1515
const nextConfig = {
1616
reactStrictMode: true,
17+
compress: false,
1718
output: process.env.DOCKER_OUTPUT ? 'standalone' : undefined,
1819
transpilePackages: ['@t3-oss/env-nextjs', '@t3-oss/env-core'],
1920
/**

src/pages/api/import-splitpro-stream.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { authOptions } from '~/server/auth';
66
import { importSplitProData, restoreSplitProData } from '~/server/api/services/splitService';
77

88
export const config = {
9-
api: { bodyParser: false },
9+
api: { bodyParser: false, responseLimit: false },
1010
};
1111

1212
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -20,12 +20,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
2020
}
2121

2222
res.setHeader('Content-Type', 'text/event-stream');
23-
res.setHeader('Cache-Control', 'no-cache');
23+
res.setHeader('Cache-Control', 'no-cache, no-transform');
2424
res.setHeader('Connection', 'keep-alive');
25+
res.setHeader('X-Accel-Buffering', 'no');
26+
res.setHeader('Content-Encoding', 'none');
2527
res.flushHeaders();
2628

29+
const flush = () => {
30+
if ('flush' in res && typeof res.flush === 'function') {
31+
(res as unknown as { flush: () => void }).flush();
32+
}
33+
};
34+
2735
const send = (type: string, payload: object) => {
2836
res.write(`data: ${JSON.stringify({ type, ...payload })}\n\n`);
37+
flush();
2938
};
3039

3140
let uploadedFile: File | undefined;

src/pages/api/import-splitwise-stream.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { authOptions } from '~/server/auth';
66
import { importFromSplitwisePro } from '~/server/api/services/splitService';
77

88
export const config = {
9-
api: { bodyParser: false },
9+
api: { bodyParser: false, responseLimit: false },
1010
};
1111

1212
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -20,12 +20,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
2020
}
2121

2222
res.setHeader('Content-Type', 'text/event-stream');
23-
res.setHeader('Cache-Control', 'no-cache');
23+
res.setHeader('Cache-Control', 'no-cache, no-transform');
2424
res.setHeader('Connection', 'keep-alive');
25+
res.setHeader('X-Accel-Buffering', 'no');
26+
res.setHeader('Content-Encoding', 'none');
2527
res.flushHeaders();
2628

29+
const flush = () => {
30+
if ('flush' in res && typeof res.flush === 'function') {
31+
(res as unknown as { flush: () => void }).flush();
32+
}
33+
};
34+
2735
const send = (type: string, payload: object) => {
2836
res.write(`data: ${JSON.stringify({ type, ...payload })}\n\n`);
37+
flush();
2938
};
3039

3140
let uploadedFile: File | undefined;

0 commit comments

Comments
 (0)