API: Phoenix railing proxy, API key auth for /api/v1/*, schema export, docs, migrations, tests

- Phoenix API Railing: proxy to PHOENIX_RAILING_URL, tenant me routes
- Tenant-auth: X-API-Key support for /api/v1/* (api_keys table)
- Migration 026: api_keys table; 025 sovereign stack marketplace
- GET /graphql/schema, GET /graphql-playground, api/docs OpenAPI
- Integration tests: phoenix-railing.test.ts
- docs/api/API_VERSIONING: /api/v1/ railing alignment
- docs/phoenix/PORTAL_RAILING_WIRING

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-11 12:57:41 -07:00
parent 33b02b636b
commit 8436e22f4c
45 changed files with 4308 additions and 17 deletions

View File

@@ -213,15 +213,51 @@ export function requireJWTSecret(): string {
/**
* Validates database password specifically
* Relaxed requirements for development mode
*/
export function requireDatabasePassword(): string {
return requireProductionSecret(
process.env.DB_PASSWORD,
'DB_PASSWORD',
{
minLength: 32,
const isProduction = process.env.NODE_ENV === 'production' ||
process.env.ENVIRONMENT === 'production' ||
process.env.PRODUCTION === 'true'
if (isProduction) {
return requireProductionSecret(
process.env.DB_PASSWORD,
'DB_PASSWORD',
{
minLength: 32,
}
)
} else {
// Development mode: relaxed requirements
// Still validate but allow shorter passwords for local development
const password = process.env.DB_PASSWORD
if (!password) {
throw new SecretValidationError(
'DB_PASSWORD is required but not provided. Please set it in your .env file.',
'MISSING_SECRET',
{ minLength: 8, requireUppercase: false, requireLowercase: false, requireNumbers: false, requireSpecialChars: false }
)
}
)
// Basic validation for dev (just check it's not empty and not insecure)
if (password.length < 8) {
throw new SecretValidationError(
'DB_PASSWORD must be at least 8 characters long for development',
'INSUFFICIENT_LENGTH',
{ minLength: 8 }
)
}
if (INSECURE_SECRETS.includes(password.toLowerCase().trim())) {
throw new SecretValidationError(
'DB_PASSWORD uses an insecure default value',
'INSECURE_DEFAULT'
)
}
return password
}
}
/**