← back to AbramsOS
server.js
123 lines
require('dotenv').config();
const express = require('express');
const helmet = require('helmet');
const morgan = require('morgan');
const path = require('path');
const cookieParser = require('cookie-parser');
const { loadSessionMiddleware, requireAuth, requireStepUp } = require('./middleware/auth');
const csrf = require('./middleware/csrf');
const home = require('./routes/home');
const health = require('./routes/health');
const oauth = require('./routes/auth'); // Google OAuth (existing)
const authApp = require('./routes/auth-app'); // password + TOTP signup/signin
const connectors = require('./routes/connectors');
const purchases = require('./routes/purchases');
const importRouter = require('./routes/import');
const plaidRouter = require('./routes/plaid');
const documentsRouter = require('./routes/documents');
const remindersRouter = require('./routes/reminders');
const claimsRouter = require('./routes/claims');
const auditRouter = require('./routes/audit');
const recallsRouter = require('./routes/recalls');
const uploadRouter = require('./routes/upload');
const billsRouter = require('./routes/bills');
const reordersRouter = require('./routes/reorders');
const savingsRouter = require('./routes/savings');
const vitalsRouter = require('./routes/vitals').router;
const assetsRouter = require('./routes/assets');
const neighborhoodRouter = require('./routes/neighborhood');
const biometricsRouter = require('./routes/biometrics');
const withingsRouter = require('./routes/withings');
const warrantiesRouter = require('./routes/warranties');
const peopleRouter = require('./routes/people');
const medicationsRouter = require('./routes/medications');
const prescriptionsRouter = require('./routes/prescriptions');
const app = express();
const PORT = parseInt(process.env.PORT || '9931', 10);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.set('trust proxy', 'loopback');
app.use(helmet({ contentSecurityPolicy: false, crossOriginEmbedderPolicy: false }));
app.use(morgan('tiny'));
app.use(express.json({ limit: '2mb' }));
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser(process.env.SESSION_SECRET || 'dev-only-rotate-me'));
// Load DB-backed session for every request (no-op if cookie missing)
app.use(loadSessionMiddleware);
// CSRF: token mint + verify (HTML POST forms only; /api/* exempted)
app.use(csrf.ensureToken);
app.use(csrf.verifyToken);
// View locals
app.locals.infoEmail = process.env.INFO_EMAIL || 'info@abramsos.agentabrams.com';
app.use((req, res, next) => {
res.locals.infoEmail = app.locals.infoEmail;
res.locals.userId = req.userId || null;
next();
});
// Defensive 404-guard: never serve backup/snapshot files from /public, even if
// one accidentally lands on disk (*.bak, *.bak.<n>, *.pre-*, *.orig, *~).
app.use((req, res, next) => {
if (/\.(bak|orig)(\.[^/]+)?$|\.pre-[^/]+$|~$/i.test(req.path)) {
return res.status(404).type('text/plain').send('Not found');
}
next();
});
app.use(express.static(path.join(__dirname, 'public'), { maxAge: '0' }));
// Public routes
app.use(health); // /healthz
app.use(authApp); // /signup, /signin, /signout, /enroll-totp, /step-up
// Authenticated routes (require login)
app.use(requireAuth);
app.use(home); // /
app.use(oauth); // /auth/google/start, /auth/google/callback
app.use(connectors); // /connectors, /api/connectors, sync
app.use(purchases); // /purchases, /api/purchases
app.use(plaidRouter); // /api/plaid/*
app.use(documentsRouter); // /api/documents, /api/documents/:id/parse
app.use(remindersRouter); // /api/reminders/upcoming, regenerate, dismiss
app.use(claimsRouter); // /api/claims, /api/claims/from-reminder/:id
app.use(auditRouter); // /audit, /api/audit/events
app.use(recallsRouter); // /recalls, /api/recalls
app.use(uploadRouter); // /api/upload/csv
app.use(billsRouter); // /bills, /api/bills*
app.use(reordersRouter); // /reorders, /api/reorders*
app.use(savingsRouter); // /savings, /api/savings*, /api/coupons
app.use(vitalsRouter); // /health, /api/health/readings*
app.use(assetsRouter); // /assets, /api/assets*
app.use(neighborhoodRouter); // /neighborhood, /api/neighborhood/geocode
app.use(biometricsRouter); // /biometrics, /api/biometrics*
app.use(withingsRouter); // /auth/withings, /api/withings/*
app.use(warrantiesRouter); // /warranties, /api/warranties*
app.use(peopleRouter); // /household, /api/people*
app.use(medicationsRouter); // /medications, /api/medications*
app.use(prescriptionsRouter); // /prescriptions, /api/prescriptions
// Step-up-required routes (must re-verify TOTP within 60s)
app.use('/import', requireStepUp, importRouter);
app.use((err, _req, res, _next) => {
console.error('[unhandled]', err);
res.status(500).render('error', { error: err.message });
});
if (require.main === module) {
app.listen(PORT, () => {
console.log(`[abramsos] listening on http://localhost:${PORT}`);
require('./lib/scheduler').start();
});
}
module.exports = app;