Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 1m21s
95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
// src/routes/system.routes.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';
|
|
import { requiredString } from '../utils/zodUtils';
|
|
|
|
const router = Router();
|
|
|
|
const geocodeSchema = z.object({
|
|
body: z.object({
|
|
address: requiredString('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).
|
|
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) {
|
|
logger.error({ error }, 'Error geocoding address');
|
|
next(error);
|
|
}
|
|
},
|
|
);
|
|
|
|
export default router;
|