363 lines
9.9 KiB
Dart

// ignore_for_file: avoid_print, use_build_context_synchronously
import 'dart:convert';
import 'dart:io';
// import 'package:refilc/api/login.dart';
// import 'package:refilc/api/nonce.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/status_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/models/user.dart';
// import 'package:refilc/utils/jwt.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;
import 'dart:async';
class KretaClient {
String? accessToken;
String? refreshToken;
String? idToken;
String? userAgent;
late http.Client client;
late final SettingsProvider _settings;
late final UserProvider _user;
late final DatabaseProvider _database;
late final StatusProvider _status;
// bool _loginRefreshing = false;
KretaClient({
this.accessToken,
required SettingsProvider settings,
required UserProvider user,
required DatabaseProvider database,
required StatusProvider status,
}) : _settings = settings,
_user = user,
_database = database,
_status = status,
userAgent = settings.config.userAgent {
var ioclient = HttpClient();
ioclient.badCertificateCallback = _checkCerts;
client = http.IOClient(ioclient);
}
bool _checkCerts(X509Certificate cert, String host, int port) {
return _settings.developerMode;
}
Future<dynamic> getAPI(
String url, {
Map<String, String>? headers,
bool autoHeader = true,
bool json = true,
bool rawResponse = false,
}) async {
Map<String, String> headerMap;
if (rawResponse) json = false;
if (headers != null) {
headerMap = headers;
} else {
headerMap = {};
}
if (accessToken == null || accessToken == '') {
accessToken = _user.user?.accessToken;
}
try {
http.Response? res;
for (int i = 0; i < 2; i++) {
if (autoHeader) {
if (!headerMap.containsKey("authorization") && accessToken != null) {
headerMap["authorization"] = "Bearer $accessToken";
}
if (!headerMap.containsKey("user-agent") && userAgent != null) {
headerMap["user-agent"] = "$userAgent";
}
}
res = await client.get(Uri.parse(url), headers: headerMap);
_status.triggerRequest(res);
if (res.statusCode == 401) {
headerMap.remove("authorization");
print("DEBUG: 401 error, refreshing login");
print("DEBUG: 401 error, URL: $url");
//await refreshLogin();
} else {
break;
}
// Wait before retrying
await Future.delayed(const Duration(milliseconds: 1500));
}
if (res == null) throw "Login error";
if (res.body == 'invalid_grant' || res.body.replaceAll(' ', '') == '') {
throw "Auth error";
}
if (json) {
return jsonDecode(res.body);
} else if (rawResponse) {
return res.bodyBytes;
} else {
return res.body;
}
} on http.ClientException catch (error) {
print(
"ERROR: KretaClient.getAPI ($url) ClientException: ${error.message}");
} catch (error) {
print("ERROR: KretaClient.getAPI ($url) ${error.runtimeType}: $error");
}
}
Future<dynamic> postAPI(
String url, {
Map<String, String>? headers,
bool autoHeader = true,
bool json = true,
Object? body,
}) async {
Map<String, String> headerMap;
if (headers != null) {
headerMap = headers;
} else {
headerMap = {};
}
if (accessToken == null || accessToken == '') {
accessToken = _user.user?.accessToken;
}
try {
http.Response? res;
for (int i = 0; i < 2; i++) {
if (autoHeader) {
if (!headerMap.containsKey("authorization") && accessToken != null) {
headerMap["authorization"] = "Bearer $accessToken";
}
if (!headerMap.containsKey("user-agent") && userAgent != null) {
headerMap["user-agent"] = "$userAgent";
}
if (!headerMap.containsKey("content-type")) {
headerMap["content-type"] = "application/json";
}
if (url.contains('kommunikacio/uzenetek')) {
headerMap["X-Uzenet-Lokalizacio"] = "hu-HU";
}
}
res = await client.post(Uri.parse(url), headers: headerMap, body: body);
if (res.statusCode == 401) {
//await refreshLogin();
headerMap.remove("authorization");
} else {
break;
}
// Wait before retrying
await Future.delayed(const Duration(milliseconds: 1500));
}
if (res == null) throw "Login error";
if (json) {
print(jsonDecode(res.body));
return jsonDecode(res.body);
} else {
return res.body;
}
} on http.ClientException catch (error) {
print(
"ERROR: KretaClient.postAPI ($url) ClientException: ${error.message}");
} catch (error) {
print("ERROR: KretaClient.postAPI ($url) ${error.runtimeType}: $error");
}
}
Future<dynamic> sendFilesAPI(
String url, {
Map<String, String>? headers,
bool autoHeader = true,
Map<String, String>? body,
}) async {
Map<String, String> headerMap;
if (headers != null) {
headerMap = headers;
} else {
headerMap = {};
}
if (accessToken == null || accessToken == '') {
accessToken = _user.user?.accessToken;
}
try {
http.StreamedResponse? res;
for (int i = 0; i < 3; i++) {
if (autoHeader) {
if (!headerMap.containsKey("authorization") && accessToken != null) {
headerMap["authorization"] = "Bearer $accessToken";
}
if (!headerMap.containsKey("user-agent") && userAgent != null) {
headerMap["user-agent"] = "$userAgent";
}
if (!headerMap.containsKey("content-type")) {
headerMap["content-type"] = "multipart/form-data";
}
if (url.contains('kommunikacio/uzenetek')) {
headerMap["X-Uzenet-Lokalizacio"] = "hu-HU";
}
}
var request = http.MultipartRequest("POST", Uri.parse(url));
// request.files.add(value)
request.fields.addAll(body ?? {});
request.headers.addAll(headers ?? {});
res = await request.send();
if (res.statusCode == 401) {
headerMap.remove("authorization");
//await refreshLogin();
} else {
break;
}
}
if (res == null) throw "Login error";
print(res.statusCode);
return res.statusCode;
} on http.ClientException catch (error) {
print(
"ERROR: KretaClient.postAPI ($url) ClientException: ${error.message}");
} catch (error) {
print("ERROR: KretaClient.postAPI ($url) ${error.runtimeType}: $error");
}
}
Future<String?> refreshLogin() async {
// if (_loginRefreshing) return null;
// _loginRefreshing = true;
User? loginUser = _user.user;
if (loginUser == null) return null;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"accept": "*/*",
"user-agent": "eKretaStudent/264745 CFNetwork/1494.0.7 Darwin/23.4.0",
};
if (_settings.presentationMode) {
print("DEBUG: refreshLogin: ${loginUser.id}");
} else {
print("DEBUG: refreshLogin: ${loginUser.id} ${loginUser.name}");
}
refreshToken ??= loginUser.refreshToken;
print("REFRESH TOKEN BELOW");
print(refreshToken);
print(loginUser.accessTokenExpire);
print(DateTime.now().toIso8601String());
if (!DateTime.now().isAfter(loginUser.accessTokenExpire)) {
return 'success';
}
if (refreshToken != null) {
// print("REFRESHING LOGIN");
Map? res = await postAPI(KretaAPI.login,
headers: headers,
body: User.refreshBody(
refreshToken: loginUser.refreshToken,
instituteCode: loginUser.instituteCode,
));
print("REFRESH RESPONSE BELOW");
print(res);
if (res != null) {
if (res.containsKey("error")) {
// remove user if refresh token expired
if (res["error"] == "invalid_grant") {
// remove user from app
// _user.removeUser(loginUser.id);
// await _database.store.removeUser(loginUser.id);
print("invalid refresh token (invalid_grant)");
// return error
return "refresh_token_expired";
}
}
if (res.containsKey("access_token")) {
accessToken = res["access_token"];
loginUser.accessToken = res["access_token"];
loginUser.accessTokenExpire =
DateTime.now().add(Duration(seconds: (res["expires_in"] - 30)));
_database.store.storeUser(loginUser);
_user.refresh();
}
if (res.containsKey("refresh_token")) {
refreshToken = res["refresh_token"];
loginUser.refreshToken = res["refresh_token"];
_database.store.storeUser(loginUser);
_user.refresh();
}
if (res.containsKey("id_token")) {
idToken = res["id_token"];
}
// _loginRefreshing = false;
print('successful refresh');
return 'success';
} else {
// _loginRefreshing = false;
return null;
}
} else {
// _loginRefreshing = false;
return null;
}
// return null;
}
Future<void> logout() async {
User? loginUser = _user.user;
if (loginUser == null) return;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
};
await postAPI(
KretaAPI.logout,
headers: headers,
body: User.logoutBody(
refreshToken: refreshToken!,
),
json: false,
);
}
}