integrating tus.io server with server hooks #12234
Unanswered
bartcuijpers
asked this question in
Q&A
Replies: 2 comments
-
I am in the same boat as you. Been trying to get a TUS endpoint working without starting it in its own node server and it's been...quite painful to say the least. Haven't succeeded yet. |
Beta Was this translation helpful? Give feedback.
0 replies
-
I actually just got it working in dev without using a custom server (with help from LLMs), hopefully that's helpful. Logs are for debugging, feel free to remove them. Here's what I put in my /**
* Setup the TUS server
*/
const tusServer = new Server({
path: '/api/tus/files',
datastore: new FileStore({ directory: './uploads' }),
maxSize: 500 * 1024 * 1024, // Set max size to 500MB
respectForwardedHeaders: true // ✅ Important when behind a reverse proxy
});
// 🚀 Disable CORS Handling
tusServer.on('OPTIONS', (req, res) => {
res.writeHead(204);
res.end();
});
tusServer.on(EVENTS.POST_CREATE, async (req, res, upload) => {
try {
console.log('📥 New file upload started:', upload);
return { res }; // Just return res, metadata will be saved with the upload object
} catch (error) {
console.error('❌ Error in POST_CREATE hook:', error);
if (!res.headersSent) {
res.writeHead(500);
res.end('Internal Server Error');
}
}
});
tusServer.on(EVENTS.POST_FINISH, async (req, res, upload) => {
try {
// Process file if needed
} catch (error) {
console.error('❌ Error in POST_FINISH hook:', error);
}
});
async function toNodeRequest(request: Request): Promise<IncomingMessage> {
console.log('📏 Creating Node.js-compatible request object');
// Create readable stream directly from request body
const readable = new Readable({
read() {} // No-op to prevent auto-consumption
});
const req = Object.assign(readable, {
method: request.method,
url: new URL(request.url).pathname,
headers: Object.fromEntries(request.headers),
connection: { remoteAddress: '127.0.0.1' },
httpVersion: '1.1',
httpVersionMajor: 1,
httpVersionMinor: 1,
rawHeaders: [],
rawTrailers: [],
socket: new EventEmitter() as any
});
// ✅ Stream body instead of reading it upfront
if (request.body) {
console.log('📥 Streaming request body to TUS server...');
for await (const chunk of request.body as any) {
readable.push(chunk);
}
}
readable.push(null); // ✅ End the stream properly
console.log('✅ Node.js request object created:', {
method: req.method,
url: req.url,
headers: req.headers
});
return req as IncomingMessage;
}
/**
* Middleware to intercept TUS uploads before SvelteKit
*/
const tusHandler: Handle = async ({ event, resolve }) => {
if (event.url.pathname.startsWith('/api/tus/files')) {
console.log('🔹 TUS upload request detected');
console.log('🔹 Request Method:', event.request.method);
console.log('🔹 Request URL:', event.url.href);
console.log('🔹 Incoming Headers:', Object.fromEntries(event.request.headers));
console.log('🔹 Upload-Length:', event.request.headers.get('upload-length'));
// 📝 Clone the request before reading the body
const clonedRequest = event.request.clone();
// 🔍 Log body size without consuming the stream
try {
const rawBody = await clonedRequest.arrayBuffer();
console.log('📏 Request body size:', rawBody.byteLength, 'bytes');
// 🔥 Check for unexpectedly empty PATCH body
if (event.request.method === 'PATCH' && rawBody.byteLength === 0) {
console.warn('⚠️ PATCH request has an empty body, which may cause failures.');
}
} catch (err) {
console.error('❌ Error reading request body:', err);
}
// Do auth checks if needed here
const req = await toNodeRequest(event.request);
const res = new ServerResponse(req);
// ✅ Handle the request with TUS and return its response
return new Promise((resolveResponse) => {
tusServer
.handle(req, res)
.then(() => {
const headers = new Headers();
Object.entries(res.getHeaders()).forEach(([key, value]) => {
if (value) headers.set(key, String(value));
});
console.log('✅ TUS Request Handled - Response:', {
status: res.statusCode,
headers: Object.fromEntries(headers)
});
resolveResponse(
new Response(res.statusCode === 204 ? null : res.outputData[0]?.data, {
status: res.statusCode,
headers
})
);
})
.catch((err) => {
console.error('❌ Error handling TUS request:', err);
resolveResponse(new Response('Internal Server Error', { status: 500 }));
});
});
}
return resolve(event);
};
/**
* Middleware for authentication
*/
const authHandler: Handle = async ({ event, resolve }) => {
...
};
// 🔹 Compose all handlers using `sequence`
export const handle: Handle = sequence(
authHandler, // ✅ Ensure authentication & session management
tusHandler // ✅ Handle TUS uploads before SvelteKit
); |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Hi,
I'm trying to integrate a tus server into sveltekit using a hook in hooks.server.ts:
The hook seems to be working, except that tus compains about missing elements in the request object, like:
Type 'Request' is missing the following properties from type 'IncomingMessage': aborted, httpVersion, httpVersionMajor, httpVersionMinor, and 65 more.ts(2345)
and
TypeError: req.on is not a function at Server.createContext (D:\Data\Documents\GitHub\daisyui-svelte-tailwind-template\node_modules\@tus\server\dist\server.js:201:13)
Is the request object processed or parsed in any way causing this?
Any other suggestions to do this integration without a custom server?
Beta Was this translation helpful? Give feedback.
All reactions