Flips SESSION_COOKIE_NAME from 'token' to 'nanodrop_session' per the
family per-app naming convention (<app>_session). fastify-jwt's
cookieName in server.ts is now sourced from the constant so a future
rename only needs to touch constants.ts.
Hard-cut migration with no dual-cookie shim: the existing 'token'
cookie has no Max-Age so it dies on browser close anyway, and this
is a single-user deployment per CLAUDE.md. Users re-log in once
after deploy.
Test files updated mechanically: cookies: { token } → cookies: {
nanodrop_session: token } (variable name 'token' kept locally),
clearCookie regex updated, login response now also asserts
Max-Age=2592000 from the family TTL.
63 lines
2.1 KiB
TypeScript
63 lines
2.1 KiB
TypeScript
import Fastify from 'fastify';
|
|
import fastifyCookie from '@fastify/cookie';
|
|
import fastifyJwt from '@fastify/jwt';
|
|
import fastifyMultipart from '@fastify/multipart';
|
|
import fastifyFormbody from '@fastify/formbody';
|
|
import fastifyStatic from '@fastify/static';
|
|
import fastifyRateLimit from '@fastify/rate-limit';
|
|
import { join } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import type Database from 'better-sqlite3';
|
|
import type { Config } from './config.ts';
|
|
import { SESSION_COOKIE_NAME } from './constants.ts';
|
|
import { createLogger } from './middleware/logging.ts';
|
|
import { createLockoutService } from './services/lockout.ts';
|
|
import { authApiRoutes } from './routes/api/v1/auth.ts';
|
|
import { filesApiRoutes } from './routes/api/v1/files.ts';
|
|
import { pageRoutes } from './routes/pages.ts';
|
|
|
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
|
|
interface ServerDeps {
|
|
config: Config;
|
|
db: Database.Database;
|
|
}
|
|
|
|
export function createServer({ config, db }: ServerDeps) {
|
|
const app = Fastify({ logger: false, trustProxy: config.trustProxy });
|
|
const logger = createLogger(config.logFile);
|
|
const lockout = createLockoutService({ db, config });
|
|
|
|
app.register(fastifyCookie);
|
|
app.register(fastifyJwt, {
|
|
secret: config.jwtSecret,
|
|
cookie: { cookieName: SESSION_COOKIE_NAME, signed: false },
|
|
});
|
|
app.register(fastifyFormbody);
|
|
app.register(fastifyMultipart, { limits: { fileSize: config.maxFileSize } });
|
|
app.register(fastifyStatic, {
|
|
root: join(__dirname, '..', 'public'),
|
|
prefix: '/public/',
|
|
});
|
|
app.register(fastifyRateLimit, {
|
|
global: false,
|
|
keyGenerator: (req) => req.ip,
|
|
errorResponseBuilder: (req) => {
|
|
void logger.authRateLimited({
|
|
ip: req.ip,
|
|
userAgent: req.headers['user-agent'] ?? '',
|
|
route: req.url,
|
|
});
|
|
return { statusCode: 429, error: 'Too many requests' };
|
|
},
|
|
});
|
|
|
|
const deps = { db, config, logger, lockout };
|
|
|
|
app.register(authApiRoutes, { prefix: '/api/v1/auth', deps });
|
|
app.register(filesApiRoutes, { prefix: '/api/v1/files', deps });
|
|
app.register(pageRoutes, { prefix: '/', deps });
|
|
|
|
return app;
|
|
}
|