fix: resolve Elysia framework and Vite URI error bugs
- Fix onResponse error by using onAfterHandle for Elysia framework - Fix URI malformed errors from browser extensions in Vite dev server - Update middleware plugin to run before SvelteKit with enforce: 'pre' - Insert middleware at beginning of stack to catch malformed URIs early Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Elysia, t } from 'elysia';
|
import { Elysia, t } from 'elysia';
|
||||||
import { cors } from '@elysiajs/cors';
|
import { cors } from '@elysiajs/cors';
|
||||||
import { jwt } from '@elysiajs/jwt';
|
import { jwt } from '@elysiajs/jwt';
|
||||||
import { JWT_SECRET, logger, LOG_LEVEL } from './config.js';
|
import { JWT_SECRET, logger, LOG_LEVEL, logToFrontend } from './config.js';
|
||||||
import {
|
import {
|
||||||
registerUser,
|
registerUser,
|
||||||
authenticateUser,
|
authenticateUser,
|
||||||
@@ -79,6 +79,44 @@ const app = new Elysia()
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Request logging middleware
|
||||||
|
.onRequest(({ request, params }) => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const method = request.method;
|
||||||
|
const path = url.pathname;
|
||||||
|
const query = url.search;
|
||||||
|
|
||||||
|
// Skip logging for health checks in development to reduce noise
|
||||||
|
if (path === '/api/health' && process.env.NODE_ENV === 'development') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('Incoming request', {
|
||||||
|
method,
|
||||||
|
path,
|
||||||
|
query: query || undefined,
|
||||||
|
ip: request.headers.get('x-forwarded-for') || 'unknown',
|
||||||
|
userAgent: request.headers.get('user-agent')?.substring(0, 100),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.onAfterHandle(({ request, set }) => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const path = url.pathname;
|
||||||
|
|
||||||
|
// Skip logging for health checks in development
|
||||||
|
if (path === '/api/health' && process.env.NODE_ENV === 'development') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log error responses
|
||||||
|
if (set.status >= 400) {
|
||||||
|
logger.warn('Request failed', {
|
||||||
|
path,
|
||||||
|
status: set.status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/auth/register
|
* POST /api/auth/register
|
||||||
* Register a new user
|
* Register a new user
|
||||||
@@ -706,6 +744,47 @@ const app = new Elysia()
|
|||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/logs
|
||||||
|
* Receive frontend logs and write them to frontend.log file
|
||||||
|
*/
|
||||||
|
.post(
|
||||||
|
'/api/logs',
|
||||||
|
async ({ body, user, headers }) => {
|
||||||
|
// Extract context from headers (Elysia provides headers as a plain object)
|
||||||
|
const userAgent = headers['user-agent'] || 'unknown';
|
||||||
|
const context = {
|
||||||
|
userId: user?.id,
|
||||||
|
userAgent,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log each entry
|
||||||
|
const entries = Array.isArray(body) ? body : [body];
|
||||||
|
for (const entry of entries) {
|
||||||
|
logToFrontend(entry.level || 'info', entry.message || 'No message', entry.data || null, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: t.Union([
|
||||||
|
t.Object({
|
||||||
|
level: t.Optional(t.Union([t.Literal('debug'), t.Literal('info'), t.Literal('warn'), t.Literal('error')])),
|
||||||
|
message: t.String(),
|
||||||
|
data: t.Optional(t.Any()),
|
||||||
|
}),
|
||||||
|
t.Array(
|
||||||
|
t.Object({
|
||||||
|
level: t.Optional(t.Union([t.Literal('debug'), t.Literal('info'), t.Literal('warn'), t.Literal('error')])),
|
||||||
|
message: t.String(),
|
||||||
|
data: t.Optional(t.Any()),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Serve static files and SPA fallback for all non-API routes
|
// Serve static files and SPA fallback for all non-API routes
|
||||||
.get('/*', ({ request }) => {
|
.get('/*', ({ request }) => {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
|
|||||||
@@ -5,29 +5,42 @@ import { defineConfig } from 'vite';
|
|||||||
function suppressURIErrorPlugin() {
|
function suppressURIErrorPlugin() {
|
||||||
return {
|
return {
|
||||||
name: 'suppress-uri-error',
|
name: 'suppress-uri-error',
|
||||||
|
enforce: 'pre', // Run this plugin before others
|
||||||
configureServer(server) {
|
configureServer(server) {
|
||||||
server.middlewares.use((req, res, next) => {
|
// Return a function that will be called after all plugins are configured
|
||||||
// Intercept malformed requests before they reach Vite's middleware
|
// This ensures our middleware is added at the correct time
|
||||||
|
return () => {
|
||||||
|
// Add middleware BEFORE all other middlewares
|
||||||
|
// We insert it at position 0 to ensure it runs first
|
||||||
|
server.middlewares.stack.unshift({
|
||||||
|
route: '',
|
||||||
|
handle: (req, res, next) => {
|
||||||
|
// Intercept malformed requests before they reach SvelteKit
|
||||||
try {
|
try {
|
||||||
// Try to decode the URL to catch malformed URIs early
|
// Try to decode the URL to catch malformed URIs early
|
||||||
if (req.url) {
|
if (req.url) {
|
||||||
decodeURI(req.url);
|
decodeURI(req.url);
|
||||||
|
// Also try the full URL construction that SvelteKit does
|
||||||
|
const base = `${server.config.server.https ? 'https' : 'http'}://${
|
||||||
|
req.headers[':authority'] || req.headers.host || 'localhost'
|
||||||
|
}`;
|
||||||
|
decodeURI(new URL(base + req.url).pathname);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Silently ignore malformed URIs from browser extensions
|
// Silently ignore malformed URIs from browser extensions
|
||||||
// Don't call next(), just end the response
|
|
||||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
res.end('OK');
|
res.end('OK');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
});
|
}});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [sveltekit(), suppressURIErrorPlugin()],
|
plugins: [suppressURIErrorPlugin(), sveltekit()],
|
||||||
server: {
|
server: {
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
Reference in New Issue
Block a user