150 lines
4.5 KiB
Dart
150 lines
4.5 KiB
Dart
import 'dart:ui';
|
||
import 'dart:js_interop';
|
||
|
||
import 'package:logging/logging.dart';
|
||
|
||
import 'package:pshared/config/common.dart';
|
||
|
||
/// Bind to the global JS `appConfig` (if it exists).
|
||
@JS()
|
||
external AppConfig? get appConfig;
|
||
|
||
final _runtimeConfigLogger = Logger('config.runtime');
|
||
|
||
const _requiredRuntimeConfigKeys = <String>{
|
||
'apiProto',
|
||
'apiHost',
|
||
'apiEndpoint',
|
||
'clientId',
|
||
'wsProto',
|
||
'wsEndpoint',
|
||
};
|
||
|
||
const _optionalRuntimeConfigKeys = <String>{
|
||
'posthogApiKey',
|
||
'posthogHost',
|
||
'defaultLocale',
|
||
'defaultDimensionLength',
|
||
'themeColor',
|
||
};
|
||
|
||
bool _isMissingRuntimeConfigValue(dynamic value) {
|
||
if (value == null) {
|
||
return true;
|
||
}
|
||
final text = value.toString().trim();
|
||
return text.isEmpty || text == 'undefined' || text == 'null';
|
||
}
|
||
|
||
List<String> _missingRuntimeConfigKeys(
|
||
Iterable<String> keys,
|
||
Map<String, dynamic> configJson,
|
||
) {
|
||
return keys
|
||
.where((key) => _isMissingRuntimeConfigValue(configJson[key]))
|
||
.toList()
|
||
..sort();
|
||
}
|
||
|
||
void _logRuntimeConfigDiagnostics(Map<String, dynamic> configJson) {
|
||
final missingRequired = _missingRuntimeConfigKeys(
|
||
_requiredRuntimeConfigKeys,
|
||
configJson,
|
||
);
|
||
final missingOptional = _missingRuntimeConfigKeys(
|
||
_optionalRuntimeConfigKeys,
|
||
configJson,
|
||
);
|
||
|
||
if (missingRequired.isNotEmpty) {
|
||
_runtimeConfigLogger.severe(
|
||
'Missing required runtime config: ${missingRequired.join(', ')}',
|
||
);
|
||
}
|
||
if (missingOptional.isNotEmpty) {
|
||
_runtimeConfigLogger.warning(
|
||
'Missing runtime config, using built-in defaults for: '
|
||
'${missingOptional.join(', ')}',
|
||
);
|
||
}
|
||
}
|
||
|
||
/// A staticInterop class for the JS object returned by `appConfig`.
|
||
@JS()
|
||
@staticInterop
|
||
class AppConfig {}
|
||
|
||
/// Extension methods to expose each property on `AppConfig`.
|
||
extension AppConfigExtension on AppConfig {
|
||
external String? get apiProto;
|
||
external String? get apiHost;
|
||
external String? get apiEndpoint;
|
||
external String? get posthogApiKey;
|
||
external String? get posthogHost;
|
||
external String? get defaultLocale;
|
||
external String? get wsProto;
|
||
external String? get wsEndpoint;
|
||
external int? get defaultDimensionLength;
|
||
external String? get themeColor;
|
||
external String? get clientId;
|
||
}
|
||
|
||
class Constants extends CommonConstants {
|
||
// Just re-expose these from CommonConstants:
|
||
static Locale get defaultLocale => CommonConstants.defaultLocale;
|
||
static String get clientId => CommonConstants.clientId;
|
||
static String get accessTokenStorageKey =>
|
||
CommonConstants.accessTokenStorageKey;
|
||
static String get refreshTokenStorageKey =>
|
||
CommonConstants.refreshTokenStorageKey;
|
||
static String get currentOrgKey => CommonConstants.currentOrgKey;
|
||
static String get apiUrl => CommonConstants.apiUrl;
|
||
static String get serviceUrl => CommonConstants.serviceUrl;
|
||
static String get posthogApiKey => CommonConstants.posthogApiKey;
|
||
static String get posthogHost => CommonConstants.posthogHost;
|
||
static int get defaultDimensionLength =>
|
||
CommonConstants.defaultDimensionLength;
|
||
static String get deviceIdStorageKey => CommonConstants.deviceIdStorageKey;
|
||
static String get nilObjectRef => CommonConstants.nilObjectRef;
|
||
static Color get themeColor => CommonConstants.themeColor;
|
||
|
||
static Future<void> initialize() async {
|
||
// Try to grab the JS `appConfig` if it exists:
|
||
final config = appConfig;
|
||
|
||
if (config != null) {
|
||
// Build a Dart Map from the JS object’s properties:
|
||
final runtimeConfigJson = <String, dynamic>{
|
||
'apiProto': config.apiProto,
|
||
'apiHost': config.apiHost,
|
||
'apiEndpoint': config.apiEndpoint,
|
||
'clientId': config.clientId,
|
||
'posthogApiKey': config.posthogApiKey,
|
||
'posthogHost': config.posthogHost,
|
||
'defaultLocale': config.defaultLocale,
|
||
'wsProto': config.wsProto,
|
||
'wsEndpoint': config.wsEndpoint,
|
||
'defaultDimensionLength': config.defaultDimensionLength,
|
||
'themeColor': config.themeColor,
|
||
};
|
||
|
||
_logRuntimeConfigDiagnostics(runtimeConfigJson);
|
||
final missingRequired = _missingRuntimeConfigKeys(
|
||
_requiredRuntimeConfigKeys,
|
||
runtimeConfigJson,
|
||
);
|
||
if (missingRequired.isNotEmpty) {
|
||
throw StateError(
|
||
'Missing required runtime config: ${missingRequired.join(', ')}',
|
||
);
|
||
}
|
||
CommonConstants.applyConfiguration(runtimeConfigJson);
|
||
} else {
|
||
_runtimeConfigLogger.severe(
|
||
'window.appConfig is not defined; frontend runtime config is required.',
|
||
);
|
||
throw StateError('window.appConfig is not defined');
|
||
}
|
||
}
|
||
}
|