oc-front/lib/core/services/api_service.dart
2024-11-08 13:59:22 +01:00

166 lines
7.5 KiB
Dart

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:alert_banner/exports.dart';
import 'package:localstorage/localstorage.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/widgets/dialog/alert.dart';
import 'package:oc_front/core/services/html.dart' if (kIsWeb) 'dart:html' as http;
class APIService<T extends SerializerDeserializer> {
static bool forceRequest = false;
static Map<String, APIResponse<dynamic>> cache = <String, APIResponse<dynamic>>{};
Dio _dio = Dio(
BaseOptions(
baseUrl: const String.fromEnvironment('HOST', defaultValue: 'http://localhost:8080'), // you can keep this blank
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
),
)..interceptors.add(LogInterceptor( requestHeader: true, ));
APIService({ required String baseURL}) {
_dio = Dio(
BaseOptions(
baseUrl: baseURL, // you can keep this blank
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
),
)..interceptors.add(LogInterceptor( requestHeader: true, ),);
}
Future<APIResponse<T>> call(
String url, String method, Map<String, dynamic>? body, bool force, BuildContext? context) async {
switch (method.toLowerCase()) {
case 'get' : return await get(url, force, context);
case 'post' : return await post(url, body!, context);
case 'put' : return await put(url, body!, context);
case 'delete' : return await delete(url, context);
default : return await get(url, force, context);
}
}
Future<Response> _request(String url, String method, dynamic body, Options? options) async {
switch (method.toLowerCase()) {
case 'get' : return await _dio.get(url, options: options);
case 'post' : return await _dio.post(url, data:body, options: options);
case 'put' : return await _dio.put(url, data: body!, options: options);
case 'delete' : return await _dio.delete(url, options: options);
default : return await _dio.get(url, options: options);
}
}
ValueNotifier downloadProgressNotifier = ValueNotifier(0);
Future _mainDownload(String url, String method, bool isFilter, String? extend, String savePath, bool isWeb, BuildContext context) async {
try {
downloadProgressNotifier.value = 0;
if (isWeb) {
_dio.get("$url${extend ?? ""}").then((value) {
var url = http.Url.createObjectUrlFromBlob(http.Blob([value.data]));
http.AnchorElement(href: url)..setAttribute('download', savePath.split("/").last)..click();
downloadProgressNotifier.value = 100;
Future.delayed(const Duration(seconds: 1), () { Navigator.of(context).pop(); });
});
} else {
_dio.download("$url${extend ?? ""}", savePath, onReceiveProgress: (actualBytes, int totalBytes) {
Future.delayed(const Duration(seconds: 1), () {
downloadProgressNotifier.value = (actualBytes / totalBytes * 100).floor();
if (downloadProgressNotifier.value == 100) { Navigator.of(context).pop(); }
});
});
}
} catch (e) { /* */ }
}
Future<APIResponse<T>> _main(String url, dynamic body, String method, String succeed, bool force,
BuildContext? context, Options? options) async {
var err = "";
try {
var type = localStorage.getItem('tokenType') ?? "bearer";
_dio.options.headers["Authorization"] = "${type[0].toUpperCase() + type.substring(1)} ${localStorage.getItem('accessToken') ?? ""}";
_dio.interceptors.clear();
var response = await _request(url, method, body, options);
if (response.statusCode != null && response.statusCode! < 400) {
if (method == "delete") { cache.remove(url); return APIResponse<T>(); }
APIResponse<T> resp = APIResponse<T>().deserialize(response.data);
if (resp.error == "") {
if (context != null && succeed != "") {
// ignore: use_build_context_synchronously
showAlertBanner(context, () {}, InfoAlertBannerChild(text: succeed), // <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
}
return resp;
}
err = resp.error ?? "internal error";
}
if (response.statusCode == 401) { err = "not authorized"; }
} catch(e, s) {
print(e);
print(s);
err = e.toString();
}
//if (err.contains("token") && err.contains("expired")) { AuthService().unAuthenticate(); }
if (context != null) {
// ignore: use_build_context_synchronously
showAlertBanner( context, () {}, AlertAlertBannerChild(text: err),// <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
}
throw Exception(err);
}
Future<APIResponse<RawData>> raw(String url, dynamic body, String method) async {
var err = "";
if (url != "") {
try {
var type = localStorage.getItem('tokenType') ?? "bearer";
_dio.options.headers["Authorization"] = "${type[0].toUpperCase() + type.substring(1)} ${localStorage.getItem('accessToken') ?? ""}";
_dio.interceptors.clear();
var response = await _request(url, method, body, null);
if (response.statusCode != null && response.statusCode! < 400) {
if (method == "delete") { cache.remove(url); return APIResponse<RawData>(); }
APIResponse<RawData> resp = APIResponse<RawData>().deserialize(response.data);
if (resp.error == "") { return resp; }
err = resp.error ?? "internal error";
}
if (response.statusCode == 401) { err = "not authorized"; }
} catch(e, s) { print(e); print(s);
err = "${e.toString()} ${const String.fromEnvironment('HOST', defaultValue: 'http://localhost:8080')}"; }
} else { err = "no url"; }
throw Exception(err);
}
Future<APIResponse<T>> sendFile(String url, File file, BuildContext context) async {
FormData formData = FormData.fromMap({
"file": await MultipartFile.fromFile(file.path, filename:file.path.split("/").last),
});
// ignore: use_build_context_synchronously
return _main(url, formData, "post", "send succeed", true, context, Options(contentType: 'multipart/form-data'));
}
Future getWithDownload(String url, String format, Map<String,dynamic> cache, String savePath, bool isWeb, BuildContext context) async {
String asLabel = "";
for (var key in cache.keys) {
if (!asLabel.contains(key)) { asLabel += "&${key}_aslabel=${cache[key]!}"; }
}
try { _mainDownload(url, "get", true, "&export=$format$asLabel", savePath, isWeb, context);
} catch (e) { /* */ }
}
Future<APIResponse<T>> getWithOffset(String url, bool force, BuildContext? context) async {
return _main(url, null, "get", "", force, context, null);
}
Future<APIResponse<T>> get(String url, bool force, BuildContext? context) async {
return _main(url, null, "get", "", force, context, null);
}
Future<APIResponse<T>> post(String url, Map<String, dynamic> values, BuildContext? context) async {
return _main(url, values, "post", "send succeed", true, context, null);
}
Future<APIResponse<T>> put(String url, Map<String, dynamic> values, BuildContext? context) async {
return _main(url, values, "put", "save succeed", true, context, null);
}
Future<APIResponse<T>> delete(String url, BuildContext? context) async {
return _main(url, null, "delete", "deletion succeed", true, context, null);
}
}