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
-
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