fix(api): start in production with safe fallbacks instead of crash-looping (502)

A production API with incomplete config threw before listening, so the
container restarted forever and every /api request through nginx was a 502.
The problems are now printed as one loud warning and each gets a safe
fallback:

- JWT_SECRET / JWT_CUSTOMER_SECRET missing, weak or equal: a random key is
  generated once and kept in the Setting table (boot-secrets.ts), so tokens
  survive restarts. A strong value in the environment always wins.
  JwtModule now uses registerAsync so it reads the key after boot fills it.
- OTP_MASTER_CODE: ignored in production unless
  TEST_SERVER_ALLOW_MASTER_CODE=yes.
- CORS_ORIGINS: invalid entries dropped; empty allows every origin.

Tests: boot-secrets.spec, production-checks.spec. 118 tests pass.

سرور با تنظیمات ناقص دیگر مدام ریست نمی‌شود و ورود دوباره کار می‌کند.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
s.esmaeili
2026-10-10 16:19:30 +03:30
co-authored by Cursor
parent 4336ec4a62
commit c34bf80e6f
9 changed files with 359 additions and 94 deletions
+14 -1
View File
@@ -1,4 +1,4 @@
import { productionConfigProblems } from './production-checks';
import { isValidOrigin, masterCodeAllowed, productionConfigProblems } from './production-checks';
describe('productionConfigProblems', () => {
const A = 'a'.repeat(40);
@@ -46,4 +46,17 @@ describe('productionConfigProblems', () => {
it('lists every problem at once', () => {
expect(productionConfigProblems({ NODE_ENV: 'production', OTP_MASTER_CODE: '1234' })).toHaveLength(4);
});
it('allows the fixed test login only outside production or with the test-server flag', () => {
expect(masterCodeAllowed({ NODE_ENV: 'development' })).toBe(true);
expect(masterCodeAllowed({ NODE_ENV: 'production' })).toBe(false);
expect(masterCodeAllowed({ NODE_ENV: 'production', TEST_SERVER_ALLOW_MASTER_CODE: 'yes' })).toBe(true);
});
it('accepts only full origins', () => {
expect(isValidOrigin('https://bnk.example.ir')).toBe(true);
expect(isValidOrigin('*')).toBe(false);
expect(isValidOrigin('bnk.example.ir')).toBe(false);
expect(isValidOrigin('https://bnk.example.ir/app')).toBe(false);
});
});