First commit, phased 1 and 2 created but untested
This commit is contained in:
+81
@@ -0,0 +1,81 @@
|
||||
const express = require('express');
|
||||
const helmet = require('helmet');
|
||||
const { createSessionMiddleware, requireAuth } = require('./middleware/session');
|
||||
const { registerLimiter, loginLimiter, routes: authRoutes } = require('./routes/auth');
|
||||
const { routes: characterRoutes } = require('./routes/character');
|
||||
const { AppError } = require('./utils/errors');
|
||||
|
||||
function createApp() {
|
||||
const app = express();
|
||||
|
||||
// Security headers
|
||||
app.use(helmet());
|
||||
|
||||
// Trust proxy (for rate limiting behind reverse proxy)
|
||||
app.set('trust proxy', 1);
|
||||
|
||||
// JSON body parser with small limit
|
||||
app.use(express.json({ limit: '10kb' }));
|
||||
|
||||
// Session middleware
|
||||
app.use(createSessionMiddleware());
|
||||
|
||||
// Static files
|
||||
app.use(express.static('public'));
|
||||
|
||||
// Health endpoint (no auth)
|
||||
app.get('/api/health', (_req, res) => {
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// Auth routes (public)
|
||||
app.post('/api/auth/register', registerLimiter, authRoutes.postRegister);
|
||||
app.post('/api/auth/login', loginLimiter, authRoutes.postLogin);
|
||||
|
||||
// Auth routes (require session)
|
||||
app.post('/api/auth/logout', requireAuth, authRoutes.postLogout);
|
||||
app.get('/api/auth/me', requireAuth, authRoutes.getMe);
|
||||
|
||||
// Character routes (require session)
|
||||
app.post('/api/character', requireAuth, characterRoutes.postCharacter);
|
||||
app.get('/api/character', requireAuth, characterRoutes.getCharacter);
|
||||
|
||||
// Central error handler
|
||||
app.use((err, _req, res, _next) => {
|
||||
// Log stack traces server-side only
|
||||
if (err.stack) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
|
||||
if (err instanceof AppError) {
|
||||
return res.status(err.status).json({
|
||||
error: {
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Check for Zod-like validation errors
|
||||
if (err.name === 'ZodError' || (err.errors && Array.isArray(err.errors))) {
|
||||
return res.status(400).json({
|
||||
error: {
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Request validation failed.',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Generic error
|
||||
res.status(500).json({
|
||||
error: {
|
||||
code: 'INTERNAL_ERROR',
|
||||
message: 'Something went wrong.',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
module.exports = { createApp };
|
||||
Reference in New Issue
Block a user