serverConfigFromEnvironment function
Parses a ServerConfig from env (typically Platform.environment).
Pure and unit-testable; throws a ServerConfigException on any missing or
malformed value rather than silently defaulting a secret. Mirrors the
aiClientFromEnv pattern in fluvie_ai.
Implementation
ServerConfig serverConfigFromEnvironment(Map<String, String> env) {
final port = _int(env, 'PORT', 8080);
final apiToken = _required(env, 'API_TOKEN');
final backend = _enum(env, 'STORAGE_BACKEND', StorageBackend.values, StorageBackend.local);
final signingKey = _trimToNull(env['DOWNLOAD_SIGNING_KEY']);
return ServerConfig(
host: env['HOST'] ?? '0.0.0.0',
port: port,
publicBaseUrl: Uri.parse(env['PUBLIC_BASE_URL'] ?? 'http://localhost:$port'),
apiToken: apiToken,
cleanupToken: _required(env, 'CLEANUP_TOKEN'),
downloadSigningKey: signingKey != null
? utf8.encode(signingKey)
: sha256.convert(utf8.encode('fluvie-download:$apiToken')).bytes,
storageBackend: backend,
localStorageDir: env['LOCAL_STORAGE_DIR'] ?? '/data/renders',
s3: backend == StorageBackend.s3 ? _s3(env) : null,
publicByDefault: _bool(env, 'PUBLIC_BY_DEFAULT', fallback: false),
fileTtl: _duration(env, 'FILE_TTL', const Duration(hours: 24)),
downloadUrlTtl: _duration(env, 'DOWNLOAD_URL_TTL', const Duration(minutes: 15)),
cleanupInterval: _duration(env, 'CLEANUP_INTERVAL', const Duration(hours: 1)),
renderConcurrency: _int(env, 'RENDER_CONCURRENCY', 1),
renderProject: _trimToNull(env['RENDER_PROJECT']),
ffmpegPath: _trimToNull(env['FFMPEG_PATH']),
corsAllowOrigins: (env['CORS_ALLOW_ORIGINS'] ?? '')
.split(',')
.map((s) => s.trim())
.where((s) => s.isNotEmpty)
.toList(),
aiEnv: {
for (final key in _aiEnvKeys)
if (_trimToNull(env[key]) != null) key: env[key]!,
},
aiRateLimit: RateLimitConfig(
limit: _int(env, 'FLUVIE_AI_RATE_LIMIT', RateLimitConfig.defaults.limit),
window: _duration(env, 'FLUVIE_AI_RATE_WINDOW', RateLimitConfig.defaults.window),
dailyQuota: _int(env, 'FLUVIE_AI_DAILY_QUOTA', RateLimitConfig.defaults.dailyQuota),
),
);
}