Skip to content

Commit 947eb05

Browse files
committed
feat: introduce SERVER_ENV variable and enhance environment validation for startup requirements
1 parent c8f04c6 commit 947eb05

File tree

6 files changed

+65
-6
lines changed

6 files changed

+65
-6
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#####
1212

1313
NODE_ENV=development
14+
SERVER_ENV=local # prod, beta, alpha, local
1415
LOG_LEVEL=info
1516

1617
#####
@@ -20,7 +21,7 @@ LOG_LEVEL=info
2021
API_PORT=1234
2122
API_PREFIX=/api
2223
API_VERSION=/v2
23-
API_ALLOWED_CORS_ORIGINS=http://localhost:3000,https://api.podverse.fm,https://podverse.fm
24+
API_ALLOWED_CORS_ORIGINS=http://localhost:3000
2425

2526
# REQUIRED: Must be a valid UUID (the API will not start without a valid UUID)
2627
# Generate one using: uuidgen (macOS/Linux) or https://www.uuidgenerator.net/

ENV.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ These variables are **always required** regardless of configuration:
4949
- **`COOKIE_DOMAIN`** (Required) - Domain for cookies
5050
- **`API_ALLOWED_CORS_ORIGINS`** (Required) - Comma-separated list of allowed CORS origins (must contain at least one origin)
5151

52+
### App / General
53+
54+
- **`SERVER_ENV`** (Required) - Server environment
55+
- Must be one of: `prod`, `beta`, `alpha`, `local`
56+
- Controls environment-specific behavior (e.g., bypassing free trial restrictions in non-production environments)
57+
5258
### Web
5359

5460
- **`WEB_PROTOCOL`** (Required) - Web protocol (`http` or `https`)

src/config/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type SocialConfig = {
77

88
type Config = {
99
nodeEnv: string;
10+
serverEnv: string;
1011
userAgent: string;
1112
log: {
1213
level: string;
@@ -97,6 +98,7 @@ type Config = {
9798

9899
export const config: Config = {
99100
nodeEnv: process.env.NODE_ENV!,
101+
serverEnv: process.env.SERVER_ENV!,
100102
userAgent: process.env.USER_AGENT!,
101103
log: {
102104
level: process.env.LOG_LEVEL!

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { loggerService } from './factories/loggerService';
1919
import { activeMQArtemisService } from './factories/activeMQArtemisService';
2020
import { testKeyvaldbConnection, keyvaldb } from './lib/keyvaldb/keyvaldb';
2121
import { config } from './config';
22+
import { validateStartupRequirements } from './lib/startup/validation';
2223

2324
let serverInstance: import('http').Server | null = null;
2425

@@ -66,6 +67,9 @@ process.on('SIGTERM', () => void shutdown('SIGTERM'));
6667

6768
(async () => {
6869
try {
70+
// Validate podverse-api environment variables first
71+
validateStartupRequirements();
72+
6973
// Build module configs from app config
7074
const ormConfig = {
7175
nodeEnv: config.nodeEnv,

src/lib/auth/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,13 @@ const verifyTokenAndMembership = async (
180180
return res.status(403).json({ message: 'Membership expired' });
181181
}
182182

183-
if (options.noFreeTrial) {
183+
if (options.noFreeTrial && config.serverEnv === 'prod') {
184184
const accountMembership = membershipStatus.account_membership;
185185
if (accountMembership && accountMembership.id === AccountMembershipEnum.Trial) {
186-
return res.status(403).json({ message: 'This feature is only available to premium accounts and is not available to free trials' });
186+
return res.status(403).json({
187+
message: 'This feature is only available to premium accounts and is not available to free trials',
188+
i18nKey: 'membership.free_trial_not_allowed'
189+
});
187190
}
188191
}
189192
}

src/lib/startup/validation.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isValidUUID, AccountSignupMode, ValidationResult, ValidationSummary, validateRequired, validateOptional, validateConditionalOptional } from 'podverse-helpers';
1+
import { isValidUUID, AccountSignupMode, ValidationResult, ValidationSummary, validateRequired, validateOptional, validateConditionalOptional, SERVER_ENV_VALUES, isValidServerEnv } from 'podverse-helpers';
22
import { loggerService } from '@api/factories/loggerService';
33

44
/**
@@ -165,15 +165,16 @@ const validateAllEnvironmentVariables = (): ValidationSummary => {
165165

166166
// General
167167
results.push(validateOptional('NODE_ENV', 'General'));
168+
results.push(validateServerEnv());
168169
results.push(validateOptional('LOG_LEVEL', 'General'));
169170

170171
// Calculate summary
171172
const total = results.length;
172173
const passed = results.filter(r => r.isValid && r.isSet).length;
173174
const failed = results.filter(r => !r.isValid).length;
174175
const requiredMissing = results.filter(r => r.isRequired && !r.isValid).length;
175-
// Count as skipped only if not set and message is "Skipped" (exclude "Use Default" and "Blank")
176-
const skipped = results.filter(r => !r.isRequired && !r.isSet && r.message === 'Skipped').length;
176+
// Count as skipped all optional variables that are not set (regardless of message)
177+
const skipped = results.filter(r => !r.isRequired && !r.isSet).length;
177178
// Count defaults used (passed validations with "Use Default" or "Blank" messages)
178179
const defaultsUsed = results.filter(r => r.isValid && r.isSet && (r.message.includes('Use Default') || r.message === 'Blank')).length;
179180

@@ -283,6 +284,48 @@ const validateUserAgent = (): ValidationResult => {
283284
};
284285
};
285286

287+
/**
288+
* Validates SERVER_ENV
289+
*/
290+
const validateServerEnv = (): ValidationResult => {
291+
const serverEnv = process.env.SERVER_ENV || '';
292+
293+
// Fallback values in case import fails (should match podverse-helpers)
294+
const validEnvs = SERVER_ENV_VALUES || ['prod', 'beta', 'alpha', 'local'];
295+
const validateEnv = isValidServerEnv || ((value: string) => validEnvs.includes(value));
296+
297+
if (!serverEnv) {
298+
return {
299+
name: 'SERVER_ENV',
300+
isSet: false,
301+
isValid: false,
302+
isRequired: true,
303+
message: `Missing - must be one of: ${validEnvs.join(', ')}`,
304+
category: 'General'
305+
};
306+
}
307+
308+
if (!validateEnv(serverEnv)) {
309+
return {
310+
name: 'SERVER_ENV',
311+
isSet: true,
312+
isValid: false,
313+
isRequired: true,
314+
message: `Invalid value: "${serverEnv}" - must be one of: ${validEnvs.join(', ')}`,
315+
category: 'General'
316+
};
317+
}
318+
319+
return {
320+
name: 'SERVER_ENV',
321+
isSet: true,
322+
isValid: true,
323+
isRequired: true,
324+
message: `Set to "${serverEnv}"`,
325+
category: 'General'
326+
};
327+
};
328+
286329
/**
287330
* Validates ACCOUNT_SIGNUP_MODE
288331
*/

0 commit comments

Comments
 (0)