96 lines
2.6 KiB
Dart
96 lines
2.6 KiB
Dart
|
|
import 'dart:async';
|
||
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||
|
|
import '../models/user.dart';
|
||
|
|
import 'api_client.dart';
|
||
|
|
|
||
|
|
class AuthService {
|
||
|
|
final ApiClient _api;
|
||
|
|
final FlutterSecureStorage _storage = const FlutterSecureStorage();
|
||
|
|
Timer? _refreshTimer;
|
||
|
|
|
||
|
|
AuthService(this._api);
|
||
|
|
|
||
|
|
Future<User> login(String serverUrl, String username, String password,
|
||
|
|
{String? fcmToken}) async {
|
||
|
|
await _api.setBaseUrl(serverUrl);
|
||
|
|
|
||
|
|
final response = await _api.dio.post('/auth/login', data: {
|
||
|
|
'username': username,
|
||
|
|
'password': password,
|
||
|
|
if (fcmToken != null) 'fcm_token': fcmToken,
|
||
|
|
});
|
||
|
|
|
||
|
|
final data = response.data;
|
||
|
|
if (data['success'] != true) {
|
||
|
|
throw Exception(data['message'] ?? 'Login failed');
|
||
|
|
}
|
||
|
|
|
||
|
|
await _storage.write(key: 'access_token', value: data['access_token']);
|
||
|
|
await _storage.write(key: 'refresh_token', value: data['refresh_token']);
|
||
|
|
|
||
|
|
_scheduleRefresh(data['expires_in'] as int? ?? 3600);
|
||
|
|
|
||
|
|
return User.fromJson(data['user']);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<bool> tryRestoreSession() async {
|
||
|
|
final token = await _storage.read(key: 'access_token');
|
||
|
|
if (token == null) return false;
|
||
|
|
|
||
|
|
await _api.restoreBaseUrl();
|
||
|
|
if (_api.dio.options.baseUrl.isEmpty) return false;
|
||
|
|
|
||
|
|
try {
|
||
|
|
final response = await _api.dio.get('/agent/status');
|
||
|
|
return response.statusCode == 200;
|
||
|
|
} catch (_) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> refreshToken() async {
|
||
|
|
final refreshToken = await _storage.read(key: 'refresh_token');
|
||
|
|
if (refreshToken == null) throw Exception('No refresh token');
|
||
|
|
|
||
|
|
final response = await _api.dio.post('/auth/refresh', data: {
|
||
|
|
'refresh_token': refreshToken,
|
||
|
|
});
|
||
|
|
|
||
|
|
final data = response.data;
|
||
|
|
if (data['success'] != true) {
|
||
|
|
throw Exception('Token refresh failed');
|
||
|
|
}
|
||
|
|
|
||
|
|
await _storage.write(key: 'access_token', value: data['access_token']);
|
||
|
|
if (data['refresh_token'] != null) {
|
||
|
|
await _storage.write(key: 'refresh_token', value: data['refresh_token']);
|
||
|
|
}
|
||
|
|
|
||
|
|
_scheduleRefresh(data['expires_in'] as int? ?? 3600);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _scheduleRefresh(int expiresInSeconds) {
|
||
|
|
_refreshTimer?.cancel();
|
||
|
|
// Refresh 2 minutes before expiry
|
||
|
|
final refreshIn = Duration(seconds: expiresInSeconds - 120);
|
||
|
|
if (refreshIn.isNegative) return;
|
||
|
|
_refreshTimer = Timer(refreshIn, () async {
|
||
|
|
try {
|
||
|
|
await refreshToken();
|
||
|
|
} catch (_) {}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> logout() async {
|
||
|
|
_refreshTimer?.cancel();
|
||
|
|
try {
|
||
|
|
await _api.dio.post('/auth/logout');
|
||
|
|
} catch (_) {}
|
||
|
|
await _storage.deleteAll();
|
||
|
|
}
|
||
|
|
|
||
|
|
void dispose() {
|
||
|
|
_refreshTimer?.cancel();
|
||
|
|
}
|
||
|
|
}
|