199 lines
4.8 KiB
Dart
199 lines
4.8 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import '../models/device.dart';
|
|
import '../models/user_info.dart';
|
|
import '../models/static_talkgroup.dart';
|
|
import '../models/device_profile.dart';
|
|
import 'brandmeister_client.dart';
|
|
|
|
class AuthenticationManager extends ChangeNotifier {
|
|
static const String _tokenKey = 'apiToken';
|
|
|
|
late final FlutterSecureStorage _storage;
|
|
|
|
AuthenticationManager() {
|
|
_initStorage();
|
|
_loadToken();
|
|
}
|
|
|
|
void _initStorage() {
|
|
_storage = const FlutterSecureStorage(
|
|
aOptions: AndroidOptions(
|
|
encryptedSharedPreferences: true,
|
|
),
|
|
iOptions: IOSOptions(
|
|
accessibility: KeychainAccessibility.first_unlock,
|
|
),
|
|
webOptions: WebOptions(
|
|
dbName: 'bmmanager',
|
|
publicKey: 'bmmanager_public_key',
|
|
),
|
|
);
|
|
}
|
|
|
|
bool _isAuthenticated = false;
|
|
String _apiToken = '';
|
|
UserInfo? _userInfo;
|
|
BrandmeisterClient? _client;
|
|
|
|
bool get isAuthenticated => _isAuthenticated;
|
|
String get apiToken => _apiToken;
|
|
UserInfo? get userInfo => _userInfo;
|
|
|
|
Future<void> _loadToken() async {
|
|
try {
|
|
final token = await _storage.read(key: _tokenKey);
|
|
if (token != null && token.isNotEmpty) {
|
|
_apiToken = token;
|
|
_client = BrandmeisterClient(apiToken: token);
|
|
// Try to verify the token is still valid
|
|
try {
|
|
_userInfo = await _client!.whoami();
|
|
_isAuthenticated = true;
|
|
notifyListeners();
|
|
} catch (e) {
|
|
// Token is invalid, clear it
|
|
await logout();
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error loading token: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> _saveToken(String token) async {
|
|
try {
|
|
await _storage.write(key: _tokenKey, value: token);
|
|
_apiToken = token;
|
|
_client = BrandmeisterClient(apiToken: token);
|
|
} catch (e) {
|
|
throw BrandmeisterError('Failed to save token: $e');
|
|
}
|
|
}
|
|
|
|
Future<UserInfo> verifyAndSaveToken(String token) async {
|
|
if (token.isEmpty) {
|
|
throw BrandmeisterError('Token cannot be empty');
|
|
}
|
|
|
|
final tempClient = BrandmeisterClient(apiToken: token);
|
|
final user = await tempClient.whoami();
|
|
|
|
await _saveToken(token);
|
|
_userInfo = user;
|
|
_isAuthenticated = true;
|
|
notifyListeners();
|
|
|
|
return user;
|
|
}
|
|
|
|
Future<void> logout() async {
|
|
try {
|
|
await _storage.delete(key: _tokenKey);
|
|
_apiToken = '';
|
|
_userInfo = null;
|
|
_isAuthenticated = false;
|
|
_client = null;
|
|
notifyListeners();
|
|
} catch (e) {
|
|
debugPrint('Error during logout: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> refreshUserInfo() async {
|
|
if (_client == null) return;
|
|
|
|
try {
|
|
_userInfo = await _client!.whoami();
|
|
notifyListeners();
|
|
} catch (e) {
|
|
debugPrint('Error refreshing user info: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
// Device Operations
|
|
Future<List<Device>> getDevices() async {
|
|
if (_client == null || _userInfo == null) {
|
|
throw BrandmeisterError('Not authenticated');
|
|
}
|
|
|
|
if (_userInfo!.username == null) {
|
|
throw BrandmeisterError('Username not available');
|
|
}
|
|
|
|
return await _client!.getDevicesByCallsign(_userInfo!.username!);
|
|
}
|
|
|
|
Future<Device> getDevice(int dmrId) async {
|
|
if (_client == null) {
|
|
throw BrandmeisterError('Not authenticated');
|
|
}
|
|
|
|
return await _client!.getDevice(dmrId);
|
|
}
|
|
|
|
Future<DeviceProfile> getDeviceProfile(int dmrId) async {
|
|
if (_client == null) {
|
|
throw BrandmeisterError('Not authenticated');
|
|
}
|
|
|
|
return await _client!.getDeviceProfile(dmrId);
|
|
}
|
|
|
|
Future<List<StaticTalkgroup>> getTalkgroups(int dmrId) async {
|
|
if (_client == null) {
|
|
throw BrandmeisterError('Not authenticated');
|
|
}
|
|
|
|
return await _client!.getDeviceTalkgroups(dmrId);
|
|
}
|
|
|
|
Future<Map<String, String>> getAllTalkgroups() async {
|
|
if (_client == null) {
|
|
throw BrandmeisterError('Not authenticated');
|
|
}
|
|
|
|
return await _client!.getTalkgroups();
|
|
}
|
|
|
|
Future<void> linkTalkgroup({
|
|
required int talkgroupId,
|
|
required int dmrId,
|
|
required int timeslot,
|
|
}) async {
|
|
if (_client == null) {
|
|
throw BrandmeisterError('Not authenticated');
|
|
}
|
|
|
|
await _client!.addTalkgroup(
|
|
dmrId: dmrId,
|
|
talkgroupId: talkgroupId,
|
|
slot: timeslot,
|
|
);
|
|
}
|
|
|
|
Future<void> unlinkTalkgroup({
|
|
required String talkgroupId,
|
|
required int dmrId,
|
|
required String timeslot,
|
|
}) async {
|
|
if (_client == null) {
|
|
throw BrandmeisterError('Not authenticated');
|
|
}
|
|
|
|
final tgId = int.tryParse(talkgroupId);
|
|
final slot = int.tryParse(timeslot);
|
|
|
|
if (tgId == null || slot == null) {
|
|
throw BrandmeisterError('Invalid talkgroup ID or timeslot');
|
|
}
|
|
|
|
await _client!.removeTalkgroup(
|
|
dmrId: dmrId,
|
|
talkgroupId: tgId,
|
|
slot: slot,
|
|
);
|
|
}
|
|
}
|