2024-09-09 14:36:32 +01:00
|
|
|
import 'dart:io';
|
2024-09-10 14:44:50 +01:00
|
|
|
import 'dart:typed_data';
|
2024-08-21 09:03:03 +01:00
|
|
|
import 'package:flutter/material.dart';
|
2024-09-09 14:36:32 +01:00
|
|
|
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
2024-08-21 11:49:38 +01:00
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
2024-08-21 09:03:03 +01:00
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'dart:convert';
|
2024-09-10 14:44:50 +01:00
|
|
|
import 'package:path/path.dart' as p;
|
2024-08-21 09:03:03 +01:00
|
|
|
|
|
|
|
enum ApiService {
|
|
|
|
auth,
|
|
|
|
app,
|
|
|
|
}
|
|
|
|
|
2024-09-10 14:44:50 +01:00
|
|
|
Future<String?> request(BuildContext? context, ApiService service, String method, String path, dynamic body) async {
|
2024-09-02 10:39:03 +01:00
|
|
|
final messenger = context != null ? ScaffoldMessenger.of(context) : null;
|
2024-08-22 08:17:36 +01:00
|
|
|
final prefs = await SharedPreferences.getInstance();
|
2024-08-21 09:03:03 +01:00
|
|
|
|
|
|
|
final host = switch (service) {
|
|
|
|
ApiService.auth => const String.fromEnvironment('AUTH_SERVICE_HOST'),
|
|
|
|
ApiService.app => const String.fromEnvironment('APP_SERVICE_HOST'),
|
|
|
|
};
|
|
|
|
|
2024-08-22 08:17:36 +01:00
|
|
|
final token = prefs.getString('token');
|
2024-09-11 14:34:03 +01:00
|
|
|
final Map<String, String> headers = {'Accept': 'application/json'};
|
2024-08-22 08:17:36 +01:00
|
|
|
if (token != null) headers.addAll({'Authorization': 'Bearer $token'});
|
|
|
|
|
2024-08-21 09:03:03 +01:00
|
|
|
final http.Response response;
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (method == 'GET') {
|
2024-08-22 08:17:36 +01:00
|
|
|
response = await http.get(Uri.parse(host + path), headers: headers);
|
2024-08-21 09:03:03 +01:00
|
|
|
} else {
|
|
|
|
final function = switch (method) {
|
|
|
|
'POST' => http.post,
|
|
|
|
'PUT' => http.put,
|
|
|
|
'DELETE' => http.delete,
|
|
|
|
_ => throw const FormatException('Invalid method'),
|
|
|
|
};
|
|
|
|
|
2024-08-22 08:17:36 +01:00
|
|
|
headers.addAll({'Content-Type': 'application/json'});
|
|
|
|
|
2024-08-21 09:03:03 +01:00
|
|
|
response = await function(
|
|
|
|
Uri.parse(host + path),
|
2024-08-22 08:17:36 +01:00
|
|
|
headers: headers,
|
2024-09-10 14:44:50 +01:00
|
|
|
body: body is Uint8List ? body : (body is Object ? jsonEncode(body) : null),
|
2024-08-21 09:03:03 +01:00
|
|
|
);
|
|
|
|
}
|
2024-09-02 10:39:03 +01:00
|
|
|
} catch (e) {
|
2024-09-11 14:34:03 +01:00
|
|
|
debugPrint('Can\'t send requst: ' + e.toString());
|
2024-09-02 10:39:03 +01:00
|
|
|
messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server')));
|
2024-08-21 09:03:03 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
|
|
try {
|
|
|
|
final json = jsonDecode(response.body);
|
2024-09-02 12:14:13 +01:00
|
|
|
messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title'])));
|
2024-09-02 10:39:03 +01:00
|
|
|
debugPrint('API error: ' + json['message']);
|
|
|
|
} catch (e) {
|
2024-09-11 14:34:03 +01:00
|
|
|
debugPrint('Can\'t parse response: ' + e.toString());
|
2024-09-02 10:39:03 +01:00
|
|
|
messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})')));
|
2024-08-21 09:03:03 +01:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2024-08-27 11:16:07 +01:00
|
|
|
return utf8.decode(response.bodyBytes);
|
2024-08-21 11:49:38 +01:00
|
|
|
}
|
|
|
|
|
2024-09-09 14:36:32 +01:00
|
|
|
Future<String?> putUser(
|
|
|
|
BuildContext? context,
|
|
|
|
ApiService service,
|
|
|
|
String method,
|
|
|
|
String path,
|
|
|
|
Map<String, dynamic> body,
|
|
|
|
File? profilePicture // The image file
|
|
|
|
) async {
|
|
|
|
final messenger = context != null ? ScaffoldMessenger.of(context) : null;
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
final host = const String.fromEnvironment('AUTH_SERVICE_HOST');
|
|
|
|
|
|
|
|
final token = prefs.getString('token');
|
|
|
|
final Map<String, String> headers = {};
|
|
|
|
if (token != null) headers.addAll({'Authorization': 'Bearer $token'});
|
|
|
|
|
|
|
|
// Create the Uri
|
|
|
|
var uri = Uri.parse(host + path);
|
|
|
|
|
|
|
|
// Create a MultipartRequest
|
|
|
|
var request = http.MultipartRequest('PUT', uri);
|
|
|
|
request.headers.addAll(headers);
|
|
|
|
|
|
|
|
// Add form fields
|
|
|
|
request.fields['id'] = body['id'];
|
|
|
|
request.fields['username'] = body['username'];
|
|
|
|
request.fields['email'] = body['email'];
|
|
|
|
request.fields['password'] = body['password'];
|
|
|
|
|
|
|
|
// Attach the file to the request (if provided)
|
|
|
|
if (profilePicture != null) {
|
|
|
|
var fileStream = http.ByteStream(profilePicture.openRead());
|
|
|
|
var length = await profilePicture.length();
|
|
|
|
var multipartFile = http.MultipartFile(
|
|
|
|
'ProfilePicture', // field name matches your backend DTO
|
|
|
|
fileStream,
|
|
|
|
length,
|
2024-09-10 14:44:50 +01:00
|
|
|
filename: p.basename(profilePicture.path),
|
2024-09-09 14:36:32 +01:00
|
|
|
);
|
|
|
|
request.files.add(multipartFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Send the request
|
|
|
|
var streamedResponse = await request.send();
|
|
|
|
var response = await http.Response.fromStream(streamedResponse);
|
|
|
|
|
|
|
|
// Handle non-success responses
|
|
|
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
|
|
try {
|
|
|
|
final json = jsonDecode(response.body);
|
|
|
|
messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title'])));
|
|
|
|
debugPrint('API error: ' + json['message']);
|
|
|
|
} catch (e) {
|
|
|
|
debugPrint(e.toString());
|
|
|
|
messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})')));
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return utf8.decode(response.bodyBytes);
|
|
|
|
} catch (e) {
|
|
|
|
debugPrint(e.toString());
|
|
|
|
messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server')));
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<File> _compressImage(File file) async {
|
|
|
|
final filePath = file.absolute.path;
|
|
|
|
final lastIndex = filePath.lastIndexOf(RegExp(r'.jp'));
|
|
|
|
final splitted = filePath.substring(0, lastIndex);
|
|
|
|
final outPath = "${splitted}_compressed.jpg";
|
|
|
|
|
|
|
|
var result = await FlutterImageCompress.compressAndGetFile(
|
|
|
|
file.absolute.path,
|
|
|
|
outPath,
|
|
|
|
quality: 80,
|
|
|
|
minWidth: 1024,
|
|
|
|
minHeight: 1024,
|
|
|
|
);
|
|
|
|
|
|
|
|
return File(result!.path);
|
|
|
|
}
|
|
|
|
|
2024-08-21 11:49:38 +01:00
|
|
|
Future<bool> isLoggedIn(BuildContext context) async {
|
|
|
|
final messenger = ScaffoldMessenger.of(context);
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
|
|
final token = prefs.getString('token');
|
2024-08-27 07:21:15 +01:00
|
|
|
if (token == null) {
|
2024-09-02 12:14:13 +01:00
|
|
|
logout();
|
2024-08-23 11:34:38 +01:00
|
|
|
return false;
|
|
|
|
}
|
2024-08-21 11:49:38 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
String base64 = token.split('.')[1];
|
2024-08-22 09:20:59 +01:00
|
|
|
base64 += List.filled(base64.length % 4 == 0 ? 0 : 4 - base64.length % 4, '=').join();
|
2024-08-21 11:49:38 +01:00
|
|
|
|
|
|
|
final payload = jsonDecode(String.fromCharCodes(base64Decode(base64)));
|
|
|
|
|
|
|
|
if (payload['exp'] < DateTime.now().millisecondsSinceEpoch / 1000) {
|
2024-08-22 08:12:50 +01:00
|
|
|
messenger.showSnackBar(const SnackBar(content: Text('Token expired, please sign in again')));
|
2024-09-02 12:14:13 +01:00
|
|
|
|
|
|
|
logout();
|
2024-08-21 11:49:38 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
2024-08-22 08:12:50 +01:00
|
|
|
messenger.showSnackBar(const SnackBar(content: Text('Invalid token, please sign in again')));
|
2024-08-22 11:14:06 +01:00
|
|
|
debugPrint(e.toString());
|
2024-09-02 12:14:13 +01:00
|
|
|
|
|
|
|
logout();
|
2024-08-21 11:49:38 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2024-09-02 12:14:13 +01:00
|
|
|
|
|
|
|
void logout() async {
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
|
|
prefs.remove('token');
|
|
|
|
prefs.remove('refresh-token');
|
|
|
|
prefs.remove('id');
|
|
|
|
}
|