Files
yogibook_aury_app/lib/services/vanguard_api.dart
T
2026-08-11 07:54:22 +02:00

233 lines
6.8 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';
import '../config/api_config.dart';
class VanguardApi {
static Map<String, String> authHeaders(String token) => {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
};
static Future<String> login({
required String username,
required String password,
}) async {
final url = Uri.parse('${ApiConfig.laravelApiBase}/login');
final res = await http.post(
url,
headers: const {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: jsonEncode({
'username': username,
'password': password,
'device_name': ApiConfig.deviceName,
}),
);
if (res.statusCode != 200) {
throw Exception('Login failed (${res.statusCode}): ${res.body}');
}
final data = jsonDecode(res.body) as Map<String, dynamic>;
final token = data['token'];
if (token == null || token.toString().isEmpty) {
throw Exception('Missing token in login response.');
}
return token.toString();
}
static Future<void> requestPasswordResetEmail({required String email}) async {
final url = Uri.parse('${ApiConfig.laravelApiBase}/password/remind');
final res = await http.post(
url,
headers: const {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: jsonEncode({'email': email}),
);
if (res.statusCode != 200) {
throw Exception(
'Password remind failed (${res.statusCode}): ${res.body}',
);
}
}
// ---------- Your custom APIs ----------
static Future<Map<String, dynamic>> getUserSchools({
required String token,
}) async {
final url = Uri.parse('${ApiConfig.phpApiBase}/api_user_schools.php');
final res = await http.get(
url,
headers: {'Accept': 'application/json', 'Authorization': 'Bearer $token'},
);
if (res.statusCode != 200) {
throw Exception('User schools failed (${res.statusCode}): ${res.body}');
}
final data = jsonDecode(res.body) as Map<String, dynamic>;
if (data['success'] != true) {
throw Exception(data['message'] ?? 'Unknown error (user schools).');
}
return data;
}
static Future<Map<String, dynamic>> getMyLessons({
required String token,
required int schoolId,
required String month, // YYYY-MM
}) async {
final url = Uri.parse(
'${ApiConfig.phpApiBase}/api_my_lessons.php',
).replace(queryParameters: {'school_id': '$schoolId', 'month': month});
final res = await http.get(
url,
headers: {'Accept': 'application/json', 'Authorization': 'Bearer $token'},
);
if (res.statusCode != 200) {
throw Exception('My lessons failed (${res.statusCode}): ${res.body}');
}
final data = jsonDecode(res.body) as Map<String, dynamic>;
if (data['success'] != true) {
throw Exception(data['message'] ?? 'Unknown error (my lessons).');
}
return data;
}
static Future<void> logout({required String token}) async {
// Vanguard / Laravel: quasi sempre POST /logout con Bearer token (Sanctum)
final url = Uri.parse('${ApiConfig.laravelApiBase}/logout');
final res = await http.post(url, headers: authHeaders(token));
// 200 o 204 ok. Alcuni backend rispondono 401 se token già scaduto:
// in app lo consideriamo comunque "logout riuscito".
if (res.statusCode == 200 ||
res.statusCode == 204 ||
res.statusCode == 401) {
return;
}
throw Exception('Logout failed (${res.statusCode}): ${res.body}');
}
// ---------- Profilo (Vanguard native) ----------
/// GET /me — dati profilo. Ritorna il contenuto di "data".
static Future<Map<String, dynamic>> getMe({required String token}) async {
final url = Uri.parse('${ApiConfig.laravelApiBase}/me');
final res = await http.get(url, headers: authHeaders(token));
if (res.statusCode != 200) {
throw Exception('Get profile failed (${res.statusCode}): ${res.body}');
}
final body = jsonDecode(res.body) as Map<String, dynamic>;
final data = body['data'];
if (data is! Map<String, dynamic>) {
throw Exception('Formato risposta /me non valido.');
}
return data;
}
/// PATCH /me/details — aggiorna nome e cognome.
static Future<Map<String, dynamic>> updateDetails({
required String token,
String? firstName,
String? lastName,
}) async {
final url = Uri.parse('${ApiConfig.laravelApiBase}/me/details');
final payload = <String, dynamic>{};
if (firstName != null) payload['first_name'] = firstName;
if (lastName != null) payload['last_name'] = lastName;
final res = await http.patch(
url,
headers: authHeaders(token),
body: jsonEncode(payload),
);
if (res.statusCode != 200) {
throw Exception('Update details failed (${res.statusCode}): ${res.body}');
}
final body = jsonDecode(res.body) as Map<String, dynamic>;
return (body['data'] as Map<String, dynamic>?) ?? {};
}
/// PATCH /me/details/auth — aggiorna la password (nuova + conferma).
static Future<void> updatePassword({
required String token,
required String password,
required String passwordConfirmation,
}) async {
final url = Uri.parse('${ApiConfig.laravelApiBase}/me/details/auth');
final res = await http.patch(
url,
headers: authHeaders(token),
body: jsonEncode({
'password': password,
'password_confirmation': passwordConfirmation,
}),
);
if (res.statusCode != 200) {
throw Exception(
'Update password failed (${res.statusCode}): ${res.body}',
);
}
}
/// POST /me/avatar — upload avatar (multipart). Ritorna l'URL avatar aggiornato.
static Future<String?> uploadAvatar({
required String token,
required String filePath,
}) async {
final url = Uri.parse('${ApiConfig.laravelApiBase}/me/avatar');
final mimeType = lookupMimeType(filePath) ?? 'image/jpeg';
final parts = mimeType.split('/');
final request = http.MultipartRequest('POST', url)
..headers['Accept'] = 'application/json'
..headers['Authorization'] = 'Bearer $token'
..files.add(
await http.MultipartFile.fromPath(
'file',
filePath,
contentType: MediaType(parts[0], parts[1]),
),
);
final streamed = await request.send();
final res = await http.Response.fromStream(streamed);
if (res.statusCode != 200) {
throw Exception('Upload avatar failed (${res.statusCode}): ${res.body}');
}
final body = jsonDecode(res.body) as Map<String, dynamic>;
final data = body['data'] as Map<String, dynamic>?;
return data?['avatar']?.toString();
}
}