Gate /routes and route_details to staging only via NODE_ENV. Production hides both. /health still works everywhere but redacts route metadata when NODE_ENV=production. Pushed to test branch so it deploys to the staging proxy via Drone.
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
a26107fefd
commit
6e30f832ee
@ -19,6 +19,11 @@
|
|||||||
* (default: https://appwrite.bsidesolutions.net/v1)
|
* (default: https://appwrite.bsidesolutions.net/v1)
|
||||||
* PORT Port to listen on (default: 3012)
|
* PORT Port to listen on (default: 3012)
|
||||||
* LOG_LEVEL quiet | normal | verbose (default: normal)
|
* LOG_LEVEL quiet | normal | verbose (default: normal)
|
||||||
|
* NODE_ENV production | staging (default: production).
|
||||||
|
* When set to anything other than "production", the
|
||||||
|
* /routes diagnostic endpoint is exposed. In production
|
||||||
|
* builds, /routes returns 404 to avoid leaking route
|
||||||
|
* metadata.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import express, { Request, Response } from 'express';
|
import express, { Request, Response } from 'express';
|
||||||
@ -120,6 +125,12 @@ const PORT = parseInt(process.env.PORT ?? '3012', 10);
|
|||||||
const APPWRITE_BASE_URL = (process.env.APPWRITE_BASE_URL ?? 'https://appwrite.bsidesolutions.net/v1').replace(/\/$/, '');
|
const APPWRITE_BASE_URL = (process.env.APPWRITE_BASE_URL ?? 'https://appwrite.bsidesolutions.net/v1').replace(/\/$/, '');
|
||||||
const LOG_LEVEL = (process.env.LOG_LEVEL ?? 'normal').toLowerCase();
|
const LOG_LEVEL = (process.env.LOG_LEVEL ?? 'normal').toLowerCase();
|
||||||
|
|
||||||
|
// Whether /routes diagnostic endpoint is exposed.
|
||||||
|
// Set NODE_ENV=staging in the staging service env vars. Production services
|
||||||
|
// leave NODE_ENV unset (or set it to "production") and the endpoint is hidden.
|
||||||
|
const NODE_ENV = (process.env.NODE_ENV ?? 'production').toLowerCase();
|
||||||
|
const DIAGNOSTICS_ENABLED = NODE_ENV !== 'production';
|
||||||
|
|
||||||
function log(level: 'info' | 'warn' | 'error', message: string): void {
|
function log(level: 'info' | 'warn' | 'error', message: string): void {
|
||||||
if (LOG_LEVEL === 'quiet' && level !== 'error') return;
|
if (LOG_LEVEL === 'quiet' && level !== 'error') return;
|
||||||
const prefix = level === 'error' ? '[error]' : level === 'warn' ? '[warn]' : '[info]';
|
const prefix = level === 'error' ? '[error]' : level === 'warn' ? '[warn]' : '[info]';
|
||||||
@ -168,6 +179,7 @@ if (process.env.ROUTE_MAP) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log('info', `LOG_LEVEL: ${LOG_LEVEL}`);
|
log('info', `LOG_LEVEL: ${LOG_LEVEL}`);
|
||||||
|
log('info', `NODE_ENV: ${NODE_ENV} (diagnostics endpoint ${DIAGNOSTICS_ENABLED ? 'ENABLED' : 'DISABLED'})`);
|
||||||
log('info', `Listening on port ${PORT}`);
|
log('info', `Listening on port ${PORT}`);
|
||||||
log('info', `Appwrite base URL: ${APPWRITE_BASE_URL}`);
|
log('info', `Appwrite base URL: ${APPWRITE_BASE_URL}`);
|
||||||
log('info', `Loaded ${Object.keys(PROJECT_CREDENTIALS).length} project credential slot(s):`);
|
log('info', `Loaded ${Object.keys(PROJECT_CREDENTIALS).length} project credential slot(s):`);
|
||||||
@ -207,19 +219,29 @@ app.get('/health', (_req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
|
node_env: NODE_ENV,
|
||||||
|
diagnostics_enabled: DIAGNOSTICS_ENABLED,
|
||||||
projects: Object.keys(PROJECT_CREDENTIALS),
|
projects: Object.keys(PROJECT_CREDENTIALS),
|
||||||
routes: Object.keys(ROUTE_MAP),
|
routes: Object.keys(ROUTE_MAP),
|
||||||
project_credentials: credsForDisplay,
|
project_credentials: credsForDisplay,
|
||||||
route_details: ROUTE_MAP,
|
route_details: DIAGNOSTICS_ENABLED ? ROUTE_MAP : '[hidden - production mode]',
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ----- Diagnostic: list routes (POST + GET for easy debugging) ---------------
|
// ----- Diagnostic: list routes (STAGING ONLY) --------------------------------
|
||||||
|
// Exposed only when NODE_ENV !== 'production'. Production deployments get a
|
||||||
|
// 404 so route metadata and credential slot names aren't leaked.
|
||||||
|
|
||||||
app.get('/routes', (_req: Request, res: Response) => {
|
app.get('/routes', (_req: Request, res: Response) => {
|
||||||
|
if (!DIAGNOSTICS_ENABLED) {
|
||||||
|
log('warn', `/routes requested in production - returning 404`);
|
||||||
|
res.status(404).json({ error: 'Not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
res.json({
|
res.json({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
|
node_env: NODE_ENV,
|
||||||
routes: Object.entries(ROUTE_MAP).map(([path, entry]) => ({
|
routes: Object.entries(ROUTE_MAP).map(([path, entry]) => ({
|
||||||
path,
|
path,
|
||||||
apiKeySlot: entry.apiKeySlot,
|
apiKeySlot: entry.apiKeySlot,
|
||||||
@ -245,7 +267,7 @@ app.post('*', async (req: Request, res: Response) => {
|
|||||||
log('info', `[${incomingAt}] POST ${pathRoute} project=${projectParam ?? '(none)'} function=${functionParam ?? '(none)'} apiKey=${apiKeyParam ?? '(none)'}`);
|
log('info', `[${incomingAt}] POST ${pathRoute} project=${projectParam ?? '(none)'} function=${functionParam ?? '(none)'} apiKey=${apiKeyParam ?? '(none)'}`);
|
||||||
|
|
||||||
// Resolve project/function/apiKey. Explicit querystring params take priority,
|
// Resolve project/function/apiKey. Explicit querystring params take priority,
|
||||||
// then short-path route map (e.g. POST /square).
|
// then short-path route map (e.g. POST /crown-x-ms-monet__verify_square_webhook).
|
||||||
let resolvedProject: string | null = projectParam;
|
let resolvedProject: string | null = projectParam;
|
||||||
let resolvedFunction: string | null = functionParam;
|
let resolvedFunction: string | null = functionParam;
|
||||||
let resolvedSlot: string | null = apiKeyParam;
|
let resolvedSlot: string | null = apiKeyParam;
|
||||||
@ -333,7 +355,7 @@ app.use((req: Request, res: Response) => {
|
|||||||
error: 'Not found',
|
error: 'Not found',
|
||||||
method: req.method,
|
method: req.method,
|
||||||
path: req.path,
|
path: req.path,
|
||||||
hint: 'Visit /health or /routes for diagnostic info.',
|
hint: 'Visit /health for diagnostic info.' + (DIAGNOSTICS_ENABLED ? ' In staging, /routes also lists registered routes.' : ''),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user