Skip to content

Commit c0615f1

Browse files
authored
Merge pull request #694 from Sadeequ/strl-lit
issue: 570,571,685,690
2 parents 2464990 + f1fad05 commit c0615f1

16 files changed

Lines changed: 1888 additions & 662 deletions

BackendAcademy/.env.example

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,43 @@ PORT=3000
33
NODE_ENV=development
44

55
# Database
6+
# REQUIRED in production. Local/development uses the default below when omitted.
67
DATABASE_URL=postgresql://postgres:password@localhost:5432/rustacademy
78

89
# Redis
10+
# REQUIRED in production. Defaults to localhost in development/test.
911
REDIS_HOST=localhost
1012
REDIS_PORT=6379
1113
REDIS_PASSWORD=
1214

1315
# Auth
16+
# REQUIRED in production with >= 32 chars and NOT a placeholder value.
17+
# Development/test use explicit non-production defaults when omitted.
1418
JWT_SECRET=change_me_in_production
1519

20+
# Signing secret for signed asset download URLs.
21+
# REQUIRED in production; an empty value makes signed URLs forgeable.
22+
ASSET_SIGNING_SECRET=change_me_in_production
23+
1624
# API Keys
1725
API_KEY_SECRET=change_me_in_production
1826

1927
# CORS
28+
# "*" or a comma-separated list of origins.
2029
CORS_ORIGIN=http://localhost:3000
2130

2231
# AI Provider
2332
AI_PROVIDER=mock # claude | openai | mock
24-
ANTHROPIC_API_KEY= # Your Anthropic API key
25-
OPENAI_API_KEY= # Your OpenAI API key
26-
AI_MODEL= # Model override (optional)
33+
ANTHROPIC_API_KEY= # Required when AI_PROVIDER=claude
34+
OPENAI_API_KEY= # Required when AI_PROVIDER=openai
35+
AI_MODEL= # Model override (optional)
2736
AI_MAX_TOKENS=4096
2837
AI_TEMPERATURE=0.7
2938

3039
# Static & uploaded assets
3140
ASSETS_UPLOAD_DIR=./data/uploads # Where uploaded assets are persisted on disk
32-
ASSETS_MAX_SIZE_MB=10 # Max upload size in megabytes
41+
ASSETS_MAX_SIZE_MB=10 # Per-file upload size limit in megabytes
42+
ASSETS_MAX_TOTAL_MB=1024 # Aggregate byte quota across all stored assets (MB)
43+
ASSETS_MAX_COUNT=10000 # Maximum number of assets retained by the registry
3344
ASSETS_BASE_URL=/api/v1/assets # Base URL advertised inside asset metadata
3445
ASSETS_STATIC_DIR=./public # Read-only static asset directory served at /static

BackendAcademy/envcheck.tmp.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import * as dotenv from 'dotenv';
2+
import * as fs from 'fs';
3+
import { envValidationSchema, ENV_VALIDATION_OPTIONS, NON_PRODUCTION_DEFAULTS } from './src/config/env.schema';
4+
5+
function run(label: string, env: Record<string, unknown>) {
6+
const { error, value } = envValidationSchema.validate(env, ENV_VALIDATION_OPTIONS);
7+
console.log('---', label);
8+
if (error) console.log(' ERROR:', error.message);
9+
else console.log(' OK ', JSON.stringify({
10+
NODE_ENV: value.NODE_ENV, PORT: value.PORT, CORS_ORIGIN: value.CORS_ORIGIN,
11+
DATABASE_URL: value.DATABASE_URL, REDIS_HOST: value.REDIS_HOST, REDIS_PORT: value.REDIS_PORT,
12+
JWT_SECRET: value.JWT_SECRET, ASSET_SIGNING_SECRET: value.ASSET_SIGNING_SECRET,
13+
AI_PROVIDER: value.AI_PROVIDER, ASSETS_MAX_SIZE_MB: value.ASSETS_MAX_SIZE_MB,
14+
}));
15+
}
16+
17+
run('empty (development)', {});
18+
run('test', { NODE_ENV: 'test' });
19+
run('example env file', dotenv.parse(fs.readFileSync('.env.example')));
20+
run('production empty', { NODE_ENV: 'production' });
21+
run('production with placeholder secrets', {
22+
NODE_ENV: 'production',
23+
DATABASE_URL: 'postgresql://u:supersecretpw@db:5432/app',
24+
REDIS_HOST: 'redis',
25+
JWT_SECRET: 'change_me_in_production',
26+
ASSET_SIGNING_SECRET: NON_PRODUCTION_DEFAULTS.development.ASSET_SIGNING_SECRET,
27+
});
28+
run('production short secret', {
29+
NODE_ENV: 'production',
30+
DATABASE_URL: 'postgresql://u:supersecretpw@db:5432/app',
31+
REDIS_HOST: 'redis',
32+
JWT_SECRET: 'tooshort-abc',
33+
ASSET_SIGNING_SECRET: 'x'.repeat(40),
34+
});
35+
run('production bad db url', {
36+
NODE_ENV: 'production',
37+
DATABASE_URL: 'ftp://user:supersecretpw@db/app',
38+
REDIS_HOST: 'redis',
39+
JWT_SECRET: 'a'.repeat(40),
40+
ASSET_SIGNING_SECRET: 'b'.repeat(40),
41+
});
42+
run('production valid', {
43+
NODE_ENV: 'production',
44+
DATABASE_URL: 'postgresql://u:pw@db:5432/app',
45+
REDIS_HOST: 'redis',
46+
JWT_SECRET: 'a'.repeat(40),
47+
ASSET_SIGNING_SECRET: 'b'.repeat(40),
48+
CORS_ORIGIN: 'https://a.example, https://b.example',
49+
});
50+
run('claude without key', { AI_PROVIDER: 'claude' });
51+
run('unknown vars', { PATH: '/usr/bin', SOME_RANDOM: 'x' });
52+
run('bad node env', { NODE_ENV: 'staging' });
53+
run('bad redis port', { REDIS_PORT: -1 });

0 commit comments

Comments
 (0)