Apply the same env-value normalization to per-route JSON fragments in ROUTE_MAP
This commit is contained in:
parent
bc09e3a0cf
commit
5a43802a8e
@ -40,6 +40,26 @@ type RouteMap = Record<string, RouteEntry>;
|
|||||||
|
|
||||||
// ----- Helpers --------------------------------------------------------------
|
// ----- Helpers --------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to parse a string as JSON. If parsing fails, run the string through
|
||||||
|
* normalizeEnvValue (in case the host shell escaped/quoted it) and retry.
|
||||||
|
* Returns the parsed JSON value, or null if both attempts fail.
|
||||||
|
*/
|
||||||
|
function safeJsonParse<T>(raw: string): T | null {
|
||||||
|
const trimmed = raw.trim();
|
||||||
|
try {
|
||||||
|
return JSON.parse(trimmed) as T;
|
||||||
|
} catch {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
const normalized = normalizeEnvValue(trimmed);
|
||||||
|
try {
|
||||||
|
return JSON.parse(normalized) as T;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize a raw env-var value. Different hosting platforms apply different
|
* Normalize a raw env-var value. Different hosting platforms apply different
|
||||||
* (and surprising) transformations when you paste a JSON value into an
|
* (and surprising) transformations when you paste a JSON value into an
|
||||||
@ -61,11 +81,8 @@ function normalizeEnvValue(raw: string): string {
|
|||||||
|
|
||||||
const candidates: string[] = [
|
const candidates: string[] = [
|
||||||
value,
|
value,
|
||||||
// Strip a single surrounding layer of `"` or `'`
|
|
||||||
stripWrappingQuotes(value),
|
stripWrappingQuotes(value),
|
||||||
// Unescape every \" -> " (handles Coolify double-escape)
|
|
||||||
value.replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, '\\'),
|
value.replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, '\\'),
|
||||||
// Combined: strip wrapping quotes AND unescape inner escapes
|
|
||||||
stripWrappingQuotes(value).replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, '\\'),
|
stripWrappingQuotes(value).replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, '\\'),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -103,11 +120,11 @@ const APPWRITE_BASE_URL = (process.env.APPWRITE_BASE_URL ?? 'https://appwrite.bs
|
|||||||
|
|
||||||
let PROJECT_CREDENTIALS: CredentialsMap = {};
|
let PROJECT_CREDENTIALS: CredentialsMap = {};
|
||||||
try {
|
try {
|
||||||
const normalized = normalizeEnvValue(process.env.PROJECT_CREDENTIALS ?? '');
|
const parsed = safeJsonParse<CredentialsMap>(process.env.PROJECT_CREDENTIALS ?? '');
|
||||||
if (!normalized) {
|
if (!parsed) {
|
||||||
throw new Error('PROJECT_CREDENTIALS is empty');
|
throw new Error('PROJECT_CREDENTIALS is empty or unparseable');
|
||||||
}
|
}
|
||||||
PROJECT_CREDENTIALS = JSON.parse(normalized) as CredentialsMap;
|
PROJECT_CREDENTIALS = parsed;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
const raw = (process.env.PROJECT_CREDENTIALS ?? '').slice(0, 300);
|
const raw = (process.env.PROJECT_CREDENTIALS ?? '').slice(0, 300);
|
||||||
@ -118,6 +135,9 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse ROUTE_MAP. Format: "/path=>{json};/path2=>{json2}"
|
// Parse ROUTE_MAP. Format: "/path=>{json};/path2=>{json2}"
|
||||||
|
// The inner {json} fragment can have the same escaping problems as top-level
|
||||||
|
// env vars, so we normalize the full route map as a raw string first, then
|
||||||
|
// also try safeJsonParse on each per-route fragment as a defensive fallback.
|
||||||
const ROUTE_MAP: RouteMap = {};
|
const ROUTE_MAP: RouteMap = {};
|
||||||
if (process.env.ROUTE_MAP) {
|
if (process.env.ROUTE_MAP) {
|
||||||
const normalizedRouteMap = normalizeEnvValue(process.env.ROUTE_MAP);
|
const normalizedRouteMap = normalizeEnvValue(process.env.ROUTE_MAP);
|
||||||
@ -128,10 +148,15 @@ if (process.env.ROUTE_MAP) {
|
|||||||
if (arrowIdx === -1) continue;
|
if (arrowIdx === -1) continue;
|
||||||
const path = trimmed.slice(0, arrowIdx).trim();
|
const path = trimmed.slice(0, arrowIdx).trim();
|
||||||
const cfgStr = trimmed.slice(arrowIdx + 2).trim();
|
const cfgStr = trimmed.slice(arrowIdx + 2).trim();
|
||||||
try {
|
|
||||||
ROUTE_MAP[path] = JSON.parse(cfgStr) as RouteEntry;
|
// Try direct parse first, then fall back to normalized parse (handles
|
||||||
} catch (err) {
|
// cases where the outer wrapper was stripped but inner quotes are still
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
// escaped).
|
||||||
|
const parsed = safeJsonParse<RouteEntry>(cfgStr);
|
||||||
|
if (parsed) {
|
||||||
|
ROUTE_MAP[path] = parsed;
|
||||||
|
} else {
|
||||||
|
const message = `expected valid JSON, got "${cfgStr.slice(0, 80)}"`;
|
||||||
console.error(`[startup] Invalid ROUTE_MAP entry for "${path}":`, message);
|
console.error(`[startup] Invalid ROUTE_MAP entry for "${path}":`, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user