serverConfigFromEnvironment function

ServerConfig serverConfigFromEnvironment(
  1. Map<String, String> env
)

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),
    ),
  );
}