Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 1m21s
121 lines
5.1 KiB
TypeScript
121 lines
5.1 KiB
TypeScript
import express, { Request, Response, NextFunction } from 'express';
|
|
import cookieParser from 'cookie-parser';
|
|
import { getPool } from './src/services/db/connection';
|
|
|
|
import passport from './src/routes/passport';
|
|
import { logger } from './src/services/logger';
|
|
|
|
// Import routers
|
|
import authRouter from './src/routes/auth';
|
|
import publicRouter from './src/routes/public';
|
|
import userRouter from './src/routes/user';
|
|
import adminRouter from './src/routes/admin';
|
|
import aiRouter from './src/routes/ai';
|
|
|
|
// Environment variables are now loaded by the `tsx` command in package.json scripts.
|
|
// This ensures the correct .env file is used for development vs. testing.
|
|
|
|
// --- START DEBUG LOGGING ---
|
|
// Log the database connection details as seen by the SERVER PROCESS.
|
|
// This will confirm if the `--env-file` flag is working as expected.
|
|
logger.info('--- [SERVER PROCESS LOG] DATABASE CONNECTION ---');
|
|
logger.info(` NODE_ENV: ${process.env.NODE_ENV}`);
|
|
logger.info(` Host: ${process.env.DB_HOST}`);
|
|
logger.info(` Port: ${process.env.DB_PORT}`);
|
|
logger.info(` User: ${process.env.DB_USER}`);
|
|
logger.info(` Database: ${process.env.DB_DATABASE}`);
|
|
|
|
// Query the users table to see what the server process sees on startup.
|
|
// Corrected the query to be unambiguous by specifying the table alias for each column.
|
|
// `id` and `email` come from the `users` table (u), and `role` comes from the `profiles` table (p).
|
|
getPool().query('SELECT u.id, u.email, p.role FROM public.users u JOIN public.profiles p ON u.id = p.id')
|
|
.then(res => {
|
|
logger.debug('[SERVER PROCESS] Users found in DB on startup:');
|
|
console.table(res.rows);
|
|
})
|
|
.catch(err => {
|
|
logger.error('[SERVER PROCESS] Could not query users table on startup.', { err });
|
|
});
|
|
|
|
logger.info('-----------------------------------------------\n');
|
|
|
|
const app = express();
|
|
|
|
// --- Core Middleware ---
|
|
// Increase the limit for JSON and URL-encoded bodies. This is crucial for handling large file uploads
|
|
// that are part of multipart/form-data requests, as the overall request size is checked.
|
|
// Setting a 50MB limit to accommodate large flyer images.
|
|
app.use(express.json({ limit: '100mb' }));
|
|
app.use(express.urlencoded({ limit: '100mb', extended: true }));
|
|
app.use(cookieParser()); // Middleware to parse cookies
|
|
app.use(passport.initialize()); // Initialize Passport
|
|
|
|
// --- Logging Middleware ---
|
|
|
|
const getDurationInMilliseconds = (start: [number, number]): number => {
|
|
const NS_PER_SEC = 1e9;
|
|
const NS_TO_MS = 1e6;
|
|
const diff = process.hrtime(start);
|
|
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS;
|
|
};
|
|
|
|
const requestLogger = (req: Request, res: Response, next: NextFunction) => {
|
|
const start = process.hrtime();
|
|
const { method, originalUrl } = req;
|
|
logger.debug(`[Request Logger] INCOMING: ${method} ${originalUrl}`);
|
|
|
|
res.on('finish', () => {
|
|
const user = req.user as { id?: string } | undefined;
|
|
const durationInMilliseconds = getDurationInMilliseconds(start);
|
|
const { statusCode } = res;
|
|
const userIdentifier = user?.id ? ` (User: ${user.id})` : '';
|
|
|
|
const logMessage = `${method} ${originalUrl} ${statusCode} ${durationInMilliseconds.toFixed(2)}ms${userIdentifier}`;
|
|
|
|
if (statusCode >= 500) logger.error(logMessage);
|
|
else if (statusCode >= 400) logger.warn(logMessage);
|
|
else logger.info(logMessage);
|
|
});
|
|
|
|
next();
|
|
};
|
|
|
|
app.use(requestLogger); // Use the logging middleware for all requests
|
|
|
|
// --- Security Warning ---
|
|
if ((process.env.JWT_SECRET || 'your_super_secret_jwt_key_change_this') === 'your_super_secret_jwt_key_change_this') {
|
|
logger.warn('Security Warning: JWT_SECRET is using a default, insecure value. Please set a strong secret in your .env file.');
|
|
}
|
|
|
|
// --- API Routes ---
|
|
|
|
// The order of route registration is critical.
|
|
// More specific routes should be registered before more general ones.
|
|
// 1. Public routes that require no authentication.
|
|
app.use('/api', publicRouter);
|
|
// 2. Authentication routes for login, registration, etc.
|
|
app.use('/api/auth', authRouter);
|
|
// 3. AI routes, some of which use optional authentication.
|
|
app.use('/api/ai', aiRouter);
|
|
// 4. Admin routes, which are all protected by admin-level checks.
|
|
app.use('/api/admin', adminRouter);
|
|
// 5. General authenticated user routes. Mount this on a specific path to avoid ambiguity.
|
|
app.use('/api/users', userRouter);
|
|
|
|
// --- Error Handling and Server Startup ---
|
|
|
|
// Basic error handling middleware
|
|
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
logger.error('Unhandled application error:', { error: err.stack });
|
|
// The 'next' parameter is required for Express to identify this as an error-handling middleware.
|
|
// We log it here to satisfy the 'no-unused-vars' lint rule, as it's not called in this terminal handler.
|
|
logger.info('Terminal error handler invoked. The "next" function is part of the required signature.', { next: String(next) });
|
|
if (!res.headersSent) {
|
|
res.status(500).json({ message: 'Something broke!' });
|
|
}
|
|
});
|
|
|
|
const PORT = process.env.PORT || 3001;
|
|
app.listen(PORT, () => {
|
|
logger.info(`Authentication server started on port ${PORT}`);
|
|
}); |