101 lines
3.5 KiB
Dart

import 'dart:io';
import 'alpaca/alpaca_env.dart';
import 'package:dotenv/dotenv.dart';
class ServerEnv {
ServerEnv._({
required this.databaseUrl,
required this.port,
required this.firebaseWebApiKey,
required this.questionWorkerEnabled,
required this.questionWorkerIntervalSeconds,
required this.questionPipelineTestMode,
required this.tradingEnabled,
required this.tradingWorkerIngestEnabled,
required this.tradingWorkerEvalEnabled,
required this.tradingDevEndpointsEnabled,
required this.alpaca,
});
final String databaseUrl;
final int port;
final String firebaseWebApiKey;
final bool questionWorkerEnabled;
final int questionWorkerIntervalSeconds;
final bool questionPipelineTestMode;
final bool tradingEnabled;
final bool tradingWorkerIngestEnabled;
final bool tradingWorkerEvalEnabled;
/// Mounts dev-only endpoints under `/v1/me/trading/dev/*` (e.g. `force-fire`).
/// Default false — never enable in production.
final bool tradingDevEndpointsEnabled;
final AlpacaEnv alpaca;
static ServerEnv load() {
final DotEnv env = DotEnv(includePlatformEnvironment: true)
..load(['.env']);
// Build a sanitized snapshot of the Alpaca-relevant keys for AlpacaEnv.
const List<String> alpacaKeys = <String>[
'ALPACA_API_KEY_ID',
'ALPACA_API_SECRET_KEY',
'ALPACA_TRADING_BASE_URL',
'ALPACA_DATA_BASE_URL',
'ALPACA_DATA_FEED',
'ALPACA_ALLOW_LIVE',
];
final Map<String, String> envMap = <String, String>{
for (final String key in alpacaKeys)
if (env[key] != null && env[key]!.isNotEmpty) key: env[key]!,
};
final String? databaseUrl = env['DATABASE_URL'];
if (databaseUrl == null || databaseUrl.isEmpty) {
stderr.writeln('DATABASE_URL is required in server/.env');
exit(1);
}
final String? apiKey = env['FIREBASE_WEB_API_KEY'];
if (apiKey == null || apiKey.isEmpty) {
stderr.writeln('FIREBASE_WEB_API_KEY is required in server/.env');
exit(1);
}
final int port = int.tryParse(env['PORT'] ?? '3000') ?? 3000;
final bool workerEnabled =
(env['QUESTION_WORKER_ENABLED'] ?? 'true').toLowerCase() != 'false';
final int workerIntervalSeconds =
int.tryParse(env['QUESTION_WORKER_INTERVAL_SECONDS'] ?? '60') ?? 60;
final bool pipelineTestMode =
(env['QUESTION_PIPELINE_TEST_MODE'] ?? 'false').toLowerCase() == 'true';
final bool tradingEnabled =
(env['TRADING_ENABLED'] ?? 'false').toLowerCase() == 'true';
final bool tradingWorkerIngestEnabled =
(env['TRADING_WORKER_INGEST_ENABLED'] ?? 'true').toLowerCase() !=
'false';
final bool tradingWorkerEvalEnabled =
(env['TRADING_WORKER_EVAL_ENABLED'] ?? 'true').toLowerCase() != 'false';
final bool tradingDevEndpointsEnabled =
(env['TRADING_DEV_ENDPOINTS_ENABLED'] ?? 'false').toLowerCase() ==
'true';
final AlpacaEnv alpaca = AlpacaEnv.fromMap(envMap)..assertPaperOnly();
return ServerEnv._(
databaseUrl: databaseUrl,
port: port,
firebaseWebApiKey: apiKey,
questionWorkerEnabled: workerEnabled,
questionWorkerIntervalSeconds: workerIntervalSeconds,
questionPipelineTestMode: pipelineTestMode,
tradingEnabled: tradingEnabled,
tradingWorkerIngestEnabled: tradingWorkerIngestEnabled,
tradingWorkerEvalEnabled: tradingWorkerEvalEnabled,
tradingDevEndpointsEnabled: tradingDevEndpointsEnabled,
alpaca: alpaca,
);
}
}