xworkmate-app/lib/runtime/secure_config_store.dart

176 lines
8.2 KiB
Dart

import 'package:flutter/foundation.dart';
import 'dart:convert';
import 'dart:io';
import 'file_store_support.dart';
import 'runtime_models.dart';
import 'secret_store.dart';
import 'settings_store.dart';
class SecureConfigStore {
SecureConfigStore({
Future<String?> Function()? secretRootPathResolver,
Future<String?> Function()? appDataRootPathResolver,
Future<String?> Function()? supportRootPathResolver,
StoreLayoutResolver? layoutResolver,
SettingsStore? settingsStore,
SecretStore? secretStore,
bool enableSecureStorage = true,
}) : _layoutResolver = layoutResolver ?? StoreLayoutResolver(
secretRootPathResolver: secretRootPathResolver,
appDataRootPathResolver: appDataRootPathResolver,
supportRootPathResolver: supportRootPathResolver,
),
_settingsStore = settingsStore ?? SettingsStore(layoutResolver ?? StoreLayoutResolver(
appDataRootPathResolver: appDataRootPathResolver,
supportRootPathResolver: supportRootPathResolver,
)),
_secretStore = secretStore ?? SecretStore(
layoutResolver: layoutResolver ?? StoreLayoutResolver(
secretRootPathResolver: secretRootPathResolver,
appDataRootPathResolver: appDataRootPathResolver,
supportRootPathResolver: supportRootPathResolver,
),
enableSecureStorage: enableSecureStorage,
);
final StoreLayoutResolver _layoutResolver;
final SettingsStore _settingsStore;
final SecretStore _secretStore;
Future<void> initialize() async {
await _settingsStore.initialize();
await _secretStore.initialize();
}
Future<SettingsSnapshot> loadSettingsSnapshot() => _settingsStore.loadSnapshot();
Future<void> saveSettingsSnapshot(SettingsSnapshot snapshot) => _settingsStore.saveSnapshot(snapshot);
Future<SettingsSnapshotReloadResult> reloadSettingsSnapshotResult() => _settingsStore.reloadSnapshotResult();
Future<String?> loadSecretValueByRef(String refName) => _secretStore.loadSecretValueByRef(refName);
Future<void> saveSecretValueByRef(String refName, String value) => _secretStore.saveSecretValueByRef(refName, value);
Future<void> clearSecretValueByRef(String refName) => _secretStore.clearSecretValueByRef(refName);
Future<Map<String, String>> loadAccountManagedSecrets() => _secretStore.loadAccountManagedSecrets();
Future<String?> loadAccountManagedSecret({required String target}) => _secretStore.loadAccountManagedSecret(target: target);
Future<void> saveAccountManagedSecret({required String target, required String value}) => _secretStore.saveAccountManagedSecret(target: target, value: value);
Future<void> clearAccountManagedSecret({required String target}) => _secretStore.clearAccountManagedSecret(target: target);
Future<void> clearAccountManagedSecrets() => _secretStore.clearAccountManagedSecrets();
Future<AccountSyncState?> loadAccountSyncState() => _secretStore.loadAccountSyncState();
Future<void> saveAccountSyncState(AccountSyncState value) => _secretStore.saveAccountSyncState(value);
Future<void> clearAccountSyncState() => _secretStore.clearAccountSyncState();
Future<List<SecretAuditEntry>> loadAuditTrail() => _settingsStore.loadAuditTrail();
Future<void> appendAudit(SecretAuditEntry entry) => _settingsStore.appendAudit(entry);
Future<LocalDeviceIdentity?> loadDeviceIdentity() => _secretStore.loadDeviceIdentity();
Future<void> saveDeviceIdentity(LocalDeviceIdentity identity) => _secretStore.saveDeviceIdentity(identity);
Future<String?> loadDeviceToken({required String deviceId, required String role}) => _secretStore.loadDeviceToken(deviceId: deviceId, role: role);
Future<void> saveDeviceToken({required String deviceId, required String role, required String token}) => _secretStore.saveDeviceToken(deviceId: deviceId, role: role, token: token);
Future<void> clearDeviceToken({required String deviceId, required String role}) => _secretStore.clearDeviceToken(deviceId: deviceId, role: role);
Future<Map<String, String>> loadSecureRefs() => _secretStore.loadSecureRefs();
Future<String?> loadAccountSessionToken() => _secretStore.loadAccountSessionToken();
Future<void> saveAccountSessionToken(String value) => _secretStore.saveAccountSessionToken(value);
Future<void> clearAccountSessionToken() => _secretStore.clearAccountSessionToken();
Future<int?> loadAccountSessionExpiresAtMs() => _secretStore.loadAccountSessionExpiresAtMs().then((v) => v == 0 ? null : v);
Future<void> saveAccountSessionExpiresAtMs(int value) => _secretStore.saveAccountSessionExpiresAtMs(value);
Future<void> clearAccountSessionExpiresAtMs() => _secretStore.clearAccountSessionExpiresAtMs();
Future<String?> loadAccountSessionUserId() => _secretStore.loadAccountSessionUserId();
Future<void> saveAccountSessionUserId(String value) => _secretStore.saveAccountSessionUserId(value);
Future<void> clearAccountSessionUserId() => _secretStore.clearAccountSessionUserId();
Future<String?> loadAccountSessionIdentifier() => _secretStore.loadAccountSessionIdentifier();
Future<void> saveAccountSessionIdentifier(String value) => _secretStore.saveAccountSessionIdentifier(value);
Future<void> clearAccountSessionIdentifier() => _secretStore.clearAccountSessionIdentifier();
Future<AccountSessionSummary?> loadAccountSessionSummary() => _secretStore.loadAccountSessionSummary();
Future<void> saveAccountSessionSummary(AccountSessionSummary value) => _secretStore.saveAccountSessionSummary(value);
Future<void> clearAccountSessionSummary() => _secretStore.clearAccountSessionSummary();
Future<List<TaskThread>> loadTaskThreads() => _settingsStore.loadTaskThreads();
Future<void> saveTaskThreads(List<TaskThread> threads) => _settingsStore.saveTaskThreads(threads);
List<SkippedTaskThreadRecord> get lastSkippedInvalidTaskThreadRecords => _settingsStore.lastSkippedInvalidTaskThreadRecords;
Future<void> clearAssistantLocalState() => _settingsStore.clearAssistantLocalState();
Future<Map<String, dynamic>?> loadSupportJson(String relativePath) async {
final file = await supportFile(relativePath);
if (file == null || !await file.exists()) {
return null;
}
final raw = await file.readAsString();
final decoded = jsonDecode(raw);
return decoded is Map<String, dynamic> ? decoded : null;
}
Future<void> saveSupportJson(
String relativePath,
Map<String, dynamic> payload,
) async {
final file = await supportFile(relativePath);
if (file == null) {
return;
}
await atomicWriteString(file, jsonEncode(payload), ownerOnly: true);
}
Future<File?> supportFile(String relativePath) async {
final normalized = relativePath.trim();
if (normalized.isEmpty) {
return null;
}
final layout = await _layoutResolver.resolve();
return File('${layout.rootDirectory.path}/$normalized');
}
Future<AppUiState> loadAppUiState() async {
final payload = await loadSupportJson('ui/state.json');
if (payload == null) {
return AppUiState.defaults();
}
try {
return AppUiState.fromJson(payload);
} catch (e, stackTrace) { debugPrint('Error: $e\n$stackTrace');
return AppUiState.defaults();
}
}
Future<void> saveAppUiState(AppUiState value) =>
saveSupportJson('ui/state.json', value.toJson());
Future<void> clearAppUiState() async {
final file = await supportFile('ui/state.json');
if (file == null) {
return;
}
await deleteIfExists(file);
}
PersistentWriteFailures get persistentWriteFailures =>
PersistentWriteFailures(
settings: _settingsStore.auditWriteFailure,
tasks: _settingsStore.tasksWriteFailure,
secrets: _secretStore.secretsWriteFailure,
audit: _settingsStore.auditWriteFailure,
);
Future<File?> resolvedSettingsFile() => _layoutResolver.resolve().then((l) => File('${l.configDirectory.path}/settings.yaml'));
Future<Directory?> resolvedSettingsWatchDirectory() => _layoutResolver.resolve().then((l) => l.configDirectory);
Map<String, String> get secureRefs => _secretStore.secureRefs;
PersistentWriteFailure? get settingsWriteFailure => _settingsStore.settingsWriteFailure;
void dispose() {
_settingsStore.dispose();
_secretStore.dispose();
}
static String maskValue(String value) => SecretStore.maskValue(value);
}