diff --git a/flutter_client/lib/api/endpoints/admin_invites.dart b/flutter_client/lib/api/endpoints/admin_invites.dart new file mode 100644 index 00000000..e5ab6142 --- /dev/null +++ b/flutter_client/lib/api/endpoints/admin_invites.dart @@ -0,0 +1,32 @@ +import 'package:dio/dio.dart'; + +import '../../models/invite.dart'; + +class AdminInvitesApi { + AdminInvitesApi(this._dio); + final Dio _dio; + + /// GET /api/admin/invites → `{"invites": [...]}`. Envelope unwrapped. + Future> list() async { + final r = await _dio.get>('/api/admin/invites'); + final raw = (r.data?['invites'] as List?) ?? const []; + return raw + .map((e) => Invite.fromJson((e as Map).cast())) + .toList(growable: false); + } + + /// POST /api/admin/invites — body is optional `{"note": "..."}`. + /// Server hardcodes the 24h TTL; admins cannot configure expiry. + /// Returns the bare invite (not enveloped). + Future create({String? note}) async { + final r = await _dio.post>( + '/api/admin/invites', + data: {if (note != null && note.isNotEmpty) 'note': note}, + ); + return Invite.fromJson(r.data ?? const {}); + } + + Future revoke(String token) async { + await _dio.delete('/api/admin/invites/$token'); + } +} diff --git a/flutter_client/lib/api/endpoints/admin_quarantine.dart b/flutter_client/lib/api/endpoints/admin_quarantine.dart new file mode 100644 index 00000000..e72736c0 --- /dev/null +++ b/flutter_client/lib/api/endpoints/admin_quarantine.dart @@ -0,0 +1,31 @@ +import 'package:dio/dio.dart'; + +import '../../models/admin_quarantine_item.dart'; + +class AdminQuarantineApi { + AdminQuarantineApi(this._dio); + final Dio _dio; + + /// GET /api/admin/quarantine — flat list (no envelope) of aggregated + /// quarantine rows. + Future> list() async { + final r = await _dio.get>('/api/admin/quarantine'); + final raw = r.data ?? const []; + return raw + .map((e) => + AdminQuarantineItem.fromJson((e as Map).cast())) + .toList(growable: false); + } + + Future resolve(String trackId) async { + await _dio.post('/api/admin/quarantine/$trackId/resolve'); + } + + Future deleteFile(String trackId) async { + await _dio.post('/api/admin/quarantine/$trackId/delete-file'); + } + + Future deleteViaLidarr(String trackId) async { + await _dio.post('/api/admin/quarantine/$trackId/delete-via-lidarr'); + } +} diff --git a/flutter_client/lib/api/endpoints/admin_requests.dart b/flutter_client/lib/api/endpoints/admin_requests.dart new file mode 100644 index 00000000..519cd7bd --- /dev/null +++ b/flutter_client/lib/api/endpoints/admin_requests.dart @@ -0,0 +1,26 @@ +import 'package:dio/dio.dart'; + +import '../../models/admin_request.dart'; + +class AdminRequestsApi { + AdminRequestsApi(this._dio); + final Dio _dio; + + /// GET /api/admin/requests — flat list of pending requests. + /// Server defaults to ?status=pending; we don't override. + Future> list() async { + final r = await _dio.get>('/api/admin/requests'); + final raw = r.data ?? const []; + return raw + .map((e) => AdminRequest.fromJson((e as Map).cast())) + .toList(growable: false); + } + + Future approve(String id) async { + await _dio.post('/api/admin/requests/$id/approve'); + } + + Future reject(String id) async { + await _dio.post('/api/admin/requests/$id/reject'); + } +} diff --git a/flutter_client/lib/api/endpoints/admin_users.dart b/flutter_client/lib/api/endpoints/admin_users.dart new file mode 100644 index 00000000..92252c57 --- /dev/null +++ b/flutter_client/lib/api/endpoints/admin_users.dart @@ -0,0 +1,47 @@ +import 'package:dio/dio.dart'; + +import '../../models/admin_user.dart'; + +class AdminUsersApi { + AdminUsersApi(this._dio); + final Dio _dio; + + /// GET /api/admin/users → `{"users": [...]}`. Envelope unwrapped here. + Future> list() async { + final r = await _dio.get>('/api/admin/users'); + final raw = (r.data?['users'] as List?) ?? const []; + return raw + .map((e) => AdminUser.fromJson((e as Map).cast())) + .toList(growable: false); + } + + Future setAdmin(String id, bool isAdmin) async { + await _dio.put( + '/api/admin/users/$id/admin', + data: {'is_admin': isAdmin}, + ); + } + + /// Server's body field is `auto_approve` (NOT `auto_approve_requests`) + /// — different from the response field name. Verified against + /// internal/api/admin_users.go `adminAutoApproveReq` (May 2026). + Future setAutoApprove(String id, bool autoApprove) async { + await _dio.put( + '/api/admin/users/$id/auto-approve', + data: {'auto_approve': autoApprove}, + ); + } + + /// Admin supplies the new password; server returns 204. There is no + /// server-generated-password mode. + Future resetPassword(String id, String newPassword) async { + await _dio.post( + '/api/admin/users/$id/reset-password', + data: {'password': newPassword}, + ); + } + + Future delete(String id) async { + await _dio.delete('/api/admin/users/$id'); + } +}