feat: secure home credentials
This commit is contained in:
@@ -1,30 +1,40 @@
|
||||
import 'dart:convert';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/home_config.dart';
|
||||
import 'credentials_storage.dart';
|
||||
|
||||
/// Сервис для хранения списка "домов" и текущего выбранного.
|
||||
/// Данные лежат в SharedPreferences как JSON-массив.
|
||||
/// Несекретные данные лежат в SharedPreferences, API-ключи -- отдельно.
|
||||
class SettingsService {
|
||||
static const String _homesKey = 'ignis_homes';
|
||||
static const String _currentHomeKey = 'ignis_current_home_id';
|
||||
|
||||
final CredentialsStorage _credentialsStorage;
|
||||
|
||||
SettingsService({CredentialsStorage? credentialsStorage})
|
||||
: _credentialsStorage = credentialsStorage ?? SecureCredentialsStorage();
|
||||
|
||||
/// Загрузить все дома
|
||||
Future<List<HomeConfig>> getHomes() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final raw = prefs.getString(_homesKey);
|
||||
if (raw == null || raw.isEmpty) return [];
|
||||
final list = jsonDecode(raw) as List<dynamic>;
|
||||
return list.map((e) => HomeConfig.fromJson(e as Map<String, dynamic>)).toList();
|
||||
final migrated = await _migrateApiKeysIfNeeded(prefs, list);
|
||||
return migrated.map(HomeConfig.fromJson).toList();
|
||||
}
|
||||
|
||||
/// Сохранить весь список домов
|
||||
Future<void> saveHomes(List<HomeConfig> homes) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_homesKey, jsonEncode(homes.map((h) => h.toJson()).toList()));
|
||||
await prefs.setString(
|
||||
_homesKey,
|
||||
jsonEncode(homes.map((h) => h.toJson()).toList()),
|
||||
);
|
||||
}
|
||||
|
||||
/// Добавить или обновить дом
|
||||
Future<void> upsertHome(HomeConfig home) async {
|
||||
Future<void> upsertHome(HomeConfig home, {String? apiKey}) async {
|
||||
final homes = await getHomes();
|
||||
final idx = homes.indexWhere((h) => h.id == home.id);
|
||||
if (idx >= 0) {
|
||||
@@ -32,6 +42,9 @@ class SettingsService {
|
||||
} else {
|
||||
homes.add(home);
|
||||
}
|
||||
if (apiKey != null) {
|
||||
await setHomeApiKey(home.id, apiKey);
|
||||
}
|
||||
await saveHomes(homes);
|
||||
}
|
||||
|
||||
@@ -40,6 +53,7 @@ class SettingsService {
|
||||
final homes = await getHomes();
|
||||
homes.removeWhere((h) => h.id == id);
|
||||
await saveHomes(homes);
|
||||
await deleteHomeApiKey(id);
|
||||
|
||||
// Если удалили текущий -- сбрасываем выбор
|
||||
final currentId = await getCurrentHomeId();
|
||||
@@ -75,4 +89,53 @@ class SettingsService {
|
||||
return homes.isNotEmpty ? homes.first : null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> getHomeApiKey(String homeId) =>
|
||||
_credentialsStorage.getApiKey(homeId);
|
||||
|
||||
Future<String> requireHomeApiKey(String homeId) async {
|
||||
final key = await getHomeApiKey(homeId);
|
||||
if (key == null || key.isEmpty) {
|
||||
throw StateError('API key is missing for home $homeId');
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
Future<void> setHomeApiKey(String homeId, String apiKey) =>
|
||||
_credentialsStorage.setApiKey(homeId, apiKey);
|
||||
|
||||
Future<void> deleteHomeApiKey(String homeId) =>
|
||||
_credentialsStorage.deleteApiKey(homeId);
|
||||
|
||||
Future<List<Map<String, dynamic>>> _migrateApiKeysIfNeeded(
|
||||
SharedPreferences prefs,
|
||||
List<dynamic> rawList,
|
||||
) async {
|
||||
var changed = false;
|
||||
final result = <Map<String, dynamic>>[];
|
||||
|
||||
for (final item in rawList) {
|
||||
final map = Map<String, dynamic>.from(item as Map);
|
||||
final id = map['id']?.toString();
|
||||
final legacyApiKey = map['apiKey']?.toString();
|
||||
|
||||
if (id != null && legacyApiKey != null && legacyApiKey.isNotEmpty) {
|
||||
final existingKey = await getHomeApiKey(id);
|
||||
if (existingKey == null || existingKey.isEmpty) {
|
||||
await setHomeApiKey(id, legacyApiKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (map.remove('apiKey') != null) {
|
||||
changed = true;
|
||||
}
|
||||
result.add(map);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
await prefs.setString(_homesKey, jsonEncode(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user