Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
- Added tests for invalid request bodies in price and recipe routes. - Improved type safety in request handlers using Zod schemas. - Introduced a consistent validation pattern for empty request bodies. - Enhanced error messages for invalid query parameters in stats and user routes. - Implemented middleware to inject mock logging for tests. - Created a custom type for validated requests to streamline type inference.
75 lines
3.2 KiB
TypeScript
75 lines
3.2 KiB
TypeScript
// src/routes/system.ts
|
|
import { Router, Request, Response, NextFunction } from 'express';
|
|
import { exec } from 'child_process';
|
|
import { z } from 'zod';
|
|
import { logger } from '../services/logger.server';
|
|
import { geocodingService } from '../services/geocodingService.server';
|
|
import { validateRequest } from '../middleware/validation.middleware';
|
|
|
|
const router = Router();
|
|
|
|
const geocodeSchema = z.object({
|
|
body: z.object({
|
|
address: z.string().min(1, 'An address string is required.'),
|
|
}),
|
|
});
|
|
|
|
// An empty schema for routes that do not expect any input, to maintain a consistent validation pattern.
|
|
const emptySchema = z.object({});
|
|
|
|
/**
|
|
* Checks the status of the 'flyer-crawler-api' process managed by PM2.
|
|
* This is intended for development and diagnostic purposes.
|
|
*/
|
|
router.get('/pm2-status', validateRequest(emptySchema), (req: Request, res: Response, next: NextFunction) => {
|
|
// The name 'flyer-crawler-api' comes from your ecosystem.config.cjs file.
|
|
exec('pm2 describe flyer-crawler-api', (error, stdout, stderr) => {
|
|
if (error) {
|
|
// 'pm2 describe' exits with an error if the process is not found.
|
|
// We can treat this as a "fail" status for our check.
|
|
if (stdout && stdout.includes("doesn't exist")) {
|
|
logger.warn('[API /pm2-status] PM2 process "flyer-crawler-api" not found.');
|
|
return res.json({ success: false, message: 'Application process is not running under PM2.' });
|
|
}
|
|
logger.error({ error: stderr || error.message }, '[API /pm2-status] Error executing pm2 describe:');
|
|
return next(error);
|
|
}
|
|
|
|
// Check if there was output to stderr, even if the exit code was 0 (success).
|
|
// This handles warnings or non-fatal errors that should arguably be treated as failures in this context.
|
|
if (stderr && stderr.trim().length > 0) {
|
|
logger.error({ stderr }, '[API /pm2-status] PM2 executed but produced stderr:');
|
|
return next(new Error(`PM2 command produced an error: ${stderr}`));
|
|
}
|
|
|
|
// If the command succeeds, we can parse stdout to check the status.
|
|
const isOnline = /│ status\s+│ online\s+│/m.test(stdout);
|
|
const message = isOnline ? 'Application is online and running under PM2.' : 'Application process exists but is not online.';
|
|
res.json({ success: isOnline, message });
|
|
});
|
|
});
|
|
|
|
/**
|
|
* POST /api/system/geocode - Geocodes a given address string.
|
|
* This acts as a secure proxy to the Google Maps Geocoding API.
|
|
*/
|
|
router.post('/geocode', validateRequest(geocodeSchema), async (req: Request, res: Response, next: NextFunction) => {
|
|
// Infer type and cast request object as per ADR-003
|
|
type GeocodeRequest = z.infer<typeof geocodeSchema>;
|
|
const { body: { address } } = req as unknown as GeocodeRequest;
|
|
|
|
try {
|
|
const coordinates = await geocodingService.geocodeAddress(address, req.log);
|
|
|
|
if (!coordinates) { // This check remains, but now it only fails if BOTH services fail.
|
|
return res.status(404).json({ message: 'Could not geocode the provided address.' });
|
|
}
|
|
|
|
res.json(coordinates);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
|
|
export default router; |