feat: secure home credentials

This commit is contained in:
Artem Kokos
2026-04-22 23:25:48 +07:00
parent 6a961209cc
commit 7c0a2675c6
22 changed files with 1782 additions and 397 deletions

View File

@@ -6,22 +6,36 @@ class IgnisApi {
final Dio _dio = Dio();
Dio get dioInstance => _dio;
/// Инициализация базового URL и API-ключа
void init(String baseUrl, String apiKey) {
String url = baseUrl.trim();
if (!url.startsWith('http')) {
static String normalizeBaseUrl(String baseUrl) {
var url = baseUrl.trim();
final lowerUrl = url.toLowerCase();
if (!lowerUrl.startsWith('http://') && !lowerUrl.startsWith('https://')) {
url = 'https://$url';
}
// Убираем trailing slash
if (url.endsWith('/')) url = url.substring(0, url.length - 1);
while (url.endsWith('/')) {
url = url.substring(0, url.length - 1);
}
return url;
}
_dio.options.baseUrl = url;
/// Инициализация базового URL и API-ключа
void init(String baseUrl, String apiKey) {
_dio.options.baseUrl = normalizeBaseUrl(baseUrl);
_dio.options.headers['X-API-Key'] = apiKey;
// Бэкенд WiZ ламп тормозит -- даём запас
_dio.options.connectTimeout = const Duration(seconds: 15);
_dio.options.receiveTimeout = const Duration(seconds: 15);
}
Future<void> validateCredentials(String baseUrl, String apiKey) async {
final probe = IgnisApi()..init(baseUrl, apiKey);
try {
await probe.getAuthMe();
} finally {
probe.dioInstance.close();
}
}
// ─── Авторизация ───────────────────────────────────────────
/// Проверка текущего ключа: возвращает {is_admin, name}
@@ -40,7 +54,10 @@ class IgnisApi {
/// Создать группу
Future<Response> createGroup(String id, String name, List<String> macs) =>
_dio.post('/devices/groups', data: {'id': id, 'name': name, 'macs': macs});
_dio.post(
'/devices/groups',
data: {'id': id, 'name': name, 'macs': macs},
);
/// Удалить группу
Future<Response> deleteGroup(String groupId) =>
@@ -85,8 +102,7 @@ class IgnisApi {
Future<Response> getTasks() => _dio.get('/schedules/tasks');
/// Отменить задачу расписания
Future<Response> cancelTask(String jobId) =>
_dio.delete('/schedules/$jobId');
Future<Response> cancelTask(String jobId) => _dio.delete('/schedules/$jobId');
// ─── API-ключи ─────────────────────────────────────────────
@@ -94,11 +110,8 @@ class IgnisApi {
Future<Response> getApiKeys() => _dio.get('/api-keys');
/// Создать гостевой ключ
Future<Response> createApiKey(String name, {bool isAdmin = false}) =>
_dio.post('/api-keys', queryParameters: {
'name': name,
'is_admin': isAdmin,
});
Future<Response> createApiKey(String name, {bool isAdmin = false}) => _dio
.post('/api-keys', queryParameters: {'name': name, 'is_admin': isAdmin});
/// Отозвать ключ (body: {key: ...})
Future<Response> revokeApiKey(String key) =>