110 lines
3.5 KiB
Dart
110 lines
3.5 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
class AuthService extends ChangeNotifier {
|
|
final _storage = const FlutterSecureStorage();
|
|
String? token;
|
|
Map<String, dynamic>? profile;
|
|
final _base = 'https://menuapi.devbeni.lol/api';
|
|
|
|
AuthService() {
|
|
_loadToken();
|
|
}
|
|
|
|
/// Convenience wrapper that returns the Data array for schedule or empty list
|
|
Future<List<dynamic>> fetchSchedule(int year, int month) async {
|
|
final resp = await _httpGet('/@me/schedule?year=$year&month=$month');
|
|
if (resp == null) return [];
|
|
try {
|
|
if (resp['data'] != null && resp['data']['Data'] is List) {
|
|
return List<dynamic>.from(resp['data']['Data']);
|
|
}
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
return [];
|
|
}
|
|
|
|
Future<void> loadProfile() async {
|
|
if (token == null) return;
|
|
try {
|
|
final res = await http.get(
|
|
Uri.parse('https://menuapi.devbeni.lol/api/me'),
|
|
headers: {'Authorization': 'Bearer $token'},
|
|
);
|
|
if (res.statusCode == 200) {
|
|
final body = jsonDecode(res.body);
|
|
// Try common shapes: { data: {...} } or { user: {...} } or the whole body
|
|
if (body is Map && body['data'] is Map) profile = Map<String, dynamic>.from(body['data']);
|
|
else if (body is Map && body['user'] is Map) profile = Map<String, dynamic>.from(body['user']);
|
|
else if (body is Map && body['FullName'] != null) profile = Map<String, dynamic>.from(body);
|
|
else profile = {'raw': body};
|
|
notifyListeners();
|
|
} else {
|
|
if (kDebugMode) print('[AuthService] loadProfile failed ${res.statusCode}: ${res.body}');
|
|
}
|
|
} catch (e) {
|
|
if (kDebugMode) print('[AuthService] loadProfile error: $e');
|
|
}
|
|
}
|
|
|
|
Future<bool> login(String username, String password) async {
|
|
try {
|
|
final res = await http.post(
|
|
Uri.parse('$_base/login'),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: jsonEncode({'username': username, 'password': password}),
|
|
);
|
|
if (res.statusCode != 200) return false;
|
|
final data = jsonDecode(res.body);
|
|
// token might be in different places; try common keys
|
|
token = data['token'] ?? data['access_token'] ?? (data['data'] != null ? data['data']['token'] : null);
|
|
if (token == null) return false;
|
|
await _storage.write(key: 'token', value: token);
|
|
await loadProfile();
|
|
notifyListeners();
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<void> logout() async {
|
|
token = null;
|
|
profile = null;
|
|
await _storage.delete(key: 'token');
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<dynamic> _httpGet(String path) async {
|
|
if (token == null) {
|
|
if (kDebugMode) print('[AuthService] _httpGet called without token for $path');
|
|
return null;
|
|
}
|
|
try {
|
|
final res = await http.get(Uri.parse('$_base$path'), headers: {'Authorization': 'Bearer $token'});
|
|
if (res.statusCode == 200) {
|
|
return jsonDecode(res.body);
|
|
} else {
|
|
if (kDebugMode) {
|
|
print('[AuthService] GET $path -> ${res.statusCode}');
|
|
print('[AuthService] response body: ${res.body}');
|
|
}
|
|
throw Exception('HTTP ${res.statusCode}');
|
|
}
|
|
} catch (e) {
|
|
if (kDebugMode) print('[AuthService] _httpGet error: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
Future<void> _loadToken() async {
|
|
token = await _storage.read(key: 'token');
|
|
if (token != null) await loadProfile();
|
|
notifyListeners();
|
|
}
|
|
}
|