MIF_E31231033/lib/services/auth_service.dart

621 lines
19 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:http_parser/http_parser.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
// ================= BASE URL =================
static const String baseUrl =
'https://ta.myhost.id/E31231033/backend-laravel/public/api';
/// ================= DEFAULT HEADERS =================
static const Map<String, String> _headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
};
/// ================= LOGIN =================
static Future<Map<String, dynamic>> login({
required String email,
required String password,
}) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/login'),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: jsonEncode({'email': email, 'password': password}),
);
final data = jsonDecode(response.body);
if (response.statusCode == 200 && data['status'] == true) {
final prefs = await SharedPreferences.getInstance();
final user = data['data']?['user'];
final token = data['data']?['token'];
if (user == null || token == null) {
return {'success': false, 'message': 'Response server tidak valid'};
}
await prefs.setString('token', token);
await prefs.setString('role', user['role']);
await prefs.setString('nama_user', user['name']);
await prefs.setBool('isLogin', true);
return {
'success': true,
'data': {'user': user, 'token': token}
};
}
return {
'success': false,
'message': data['message'] ?? 'Login gagal',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= REGISTER =================
static Future<Map<String, dynamic>> register({
required String name,
required String email,
required String password,
}) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/register'),
headers: _headers,
body: jsonEncode({'name': name, 'email': email, 'password': password}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 201 || response.statusCode == 200,
'message': data['message'] ?? 'Registrasi berhasil',
'new_user': data['new_user'] ?? true,
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= FORGOT PASSWORD =================
static Future<Map<String, dynamic>> forgotPassword(String email) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/forgot-password'),
headers: _headers,
body: jsonEncode({'email': email}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 &&
(data['status'] == true || data['success'] == true),
'message': data['message'] ?? 'Gagal kirim OTP',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= VERIFY RESET OTP =================
static Future<Map<String, dynamic>> verifyResetOtp({
required String email,
required String otp,
}) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/verify-reset-otp'),
headers: _headers,
body: jsonEncode({'email': email, 'otp': otp}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'] ?? 'OTP tidak valid',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= RESET PASSWORD =================
static Future<Map<String, dynamic>> resetPassword({
required String email,
required String otp,
required String password,
}) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/reset-password'),
headers: _headers,
body: jsonEncode({'email': email, 'otp': otp, 'password': password}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'] ?? 'Password berhasil diubah',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= VERIFY OTP =================
static Future<Map<String, dynamic>> verifyOtp({
required String email,
required String otp,
}) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/verify-otp'),
headers: _headers,
body: jsonEncode({'email': email, 'otp': otp}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'] ?? 'OTP tidak valid',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= RESEND OTP =================
static Future<Map<String, dynamic>> resendOtp(
{required String email}) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/resend-otp'),
headers: _headers,
body: jsonEncode({'email': email}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'] ?? 'Gagal mengirim ulang OTP',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= GET PROFILE =================
static Future<Map<String, dynamic>> getProfile() async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
if (token == null) {
return {'success': false, 'message': 'Token tidak ditemukan'};
}
final response = await http.get(
Uri.parse('$baseUrl/me'),
headers: {..._headers, 'Authorization': 'Bearer $token'},
);
final data = jsonDecode(response.body);
if (response.statusCode == 200 && data['status'] == true) {
return {'success': true, 'data': data['data']};
}
return {'success': false, 'message': data['message']};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= KIRIM LAPORAN 1 FOTO =================
static Future<Map<String, dynamic>> kirimLaporan({
required String jadwalId,
required String lokasiId,
required String keterangan,
required double latitude,
required double longitude,
required File foto,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
if (token == null) {
return {'success': false, 'message': 'Token tidak ditemukan'};
}
var request = http.MultipartRequest(
"POST",
Uri.parse('$baseUrl/laporan'),
);
request.headers['Accept'] = 'application/json';
request.headers['Authorization'] = 'Bearer $token';
request.headers['User-Agent'] =
'Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36';
request.fields['jadwal_id'] = jadwalId;
request.fields['lokasi_id'] = lokasiId;
request.fields['keterangan'] = keterangan;
request.fields['latitude'] = latitude.toString();
request.fields['longitude'] = longitude.toString();
request.files.add(await http.MultipartFile.fromPath('foto', foto.path));
var streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 201,
'message': data['message'] ?? 'Gagal kirim laporan',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= MULTI FOTO & VIDEO =================
static Future<Map<String, dynamic>> kirimLaporanMultiMedia({
required String jadwalId,
required String lokasiId,
required String keterangan,
required double latitude,
required double longitude,
required List<File> images,
required List<File> videos,
void Function(double progress, String status)? onProgress,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
if (token == null) {
return {'success': false, 'message': 'Token tidak ditemukan'};
}
onProgress?.call(0.0, 'Menyiapkan file...');
var request = http.MultipartRequest(
"POST",
Uri.parse('$baseUrl/laporan'),
);
request.headers['Accept'] = 'application/json';
request.headers['Authorization'] = 'Bearer $token';
request.fields['jadwal_id'] = jadwalId;
request.fields['lokasi_id'] = lokasiId;
request.fields['keterangan'] = keterangan;
request.fields['latitude'] = latitude.toString();
request.fields['longitude'] = longitude.toString();
for (int i = 0; i < images.length; i++) {
final file = images[i];
final ext = file.path.split('.').last.toLowerCase();
request.files.add(
http.MultipartFile(
'foto[]',
file.openRead(),
await file.length(),
filename: 'foto_$i.$ext',
contentType: MediaType('image', ext == 'png' ? 'png' : 'jpeg'),
),
);
}
for (int i = 0; i < videos.length; i++) {
final file = videos[i];
final ext = file.path.split('.').last.toLowerCase();
request.files.add(
http.MultipartFile(
'video[]',
file.openRead(),
await file.length(),
filename: 'video_$i.$ext',
contentType: MediaType('video', 'mp4'),
),
);
}
onProgress?.call(0.2, 'Mengupload...');
final streamedResponse =
await request.send().timeout(const Duration(minutes: 5));
final response = await http.Response.fromStream(streamedResponse);
onProgress?.call(1.0, 'Selesai');
final data = jsonDecode(response.body);
if (response.statusCode == 200 || response.statusCode == 201) {
return {
'success': true,
'message': data['message'] ?? 'Laporan berhasil dikirim',
'data': data,
};
} else {
String errorMsg = data['message'] ?? 'Gagal kirim laporan';
if (data['errors'] != null) {
final errors = data['errors'] as Map;
errorMsg = errors.values.map((e) => e[0]).join('\n');
}
return {
'success': false,
'message': errorMsg,
'data': data,
};
}
} on TimeoutException {
return {
'success': false,
'message': 'Timeout. Coba koneksi lebih stabil.'
};
} on SocketException {
return {'success': false, 'message': 'Tidak ada koneksi internet'};
} catch (e) {
return {'success': false, 'message': 'Error: ${e.toString()}'};
}
}
/// ================= UPDATE PROFILE =================
static Future<Map<String, dynamic>> updateProfile({
required String name,
required String email,
String? noHp,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
final response = await http.put(
Uri.parse('$baseUrl/update-profile'),
headers: {
..._headers,
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode({'name': name, 'email': email, 'no_hp': noHp ?? ""}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200,
'message': data['message'],
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= UPLOAD FOTO PROFIL =================
static Future<Map<String, dynamic>> uploadPhoto(File image) async {
try {
String? token = await getToken();
if (token == null) {
return {'status': false, 'message': 'Token tidak ditemukan'};
}
var request = http.MultipartRequest(
'POST',
Uri.parse("$baseUrl/upload-foto"),
);
request.headers['Authorization'] = "Bearer $token";
request.headers['Accept'] = 'application/json';
var multipartFile =
await http.MultipartFile.fromPath('foto', image.path);
request.files.add(multipartFile);
var streamedResponse = await request.send();
var res = await http.Response.fromStream(streamedResponse);
final data = jsonDecode(res.body);
if (data['status'] == true) {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('foto', data['data']['foto']);
}
return data;
} catch (e) {
return {'status': false, 'message': e.toString()};
}
}
/// ================= DELETE FOTO PROFIL =================
static Future<Map<String, dynamic>> deletePhoto() async {
try {
final token = await getToken();
if (token == null) {
return {'status': false, 'message': 'Token tidak ditemukan'};
}
final response = await http.delete(
Uri.parse('$baseUrl/delete-photo'),
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
);
final data = jsonDecode(response.body);
if (data['status'] == true) {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('foto');
}
return data;
} catch (e) {
return {'status': false, 'message': e.toString()};
}
}
/// ================= GET TOKEN =================
static Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('token');
}
/// ================= GET NAMA USER =================
static Future<String?> getNamaUser() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('nama_user');
}
/// ================= GET ROLE =================
static Future<String?> getRole() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('role');
}
/// ================= UPDATE EMAIL =================
static Future<Map<String, dynamic>> updateEmail(String newEmail) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
if (token == null) {
return {'success': false, 'message': 'Token tidak ditemukan'};
}
final response = await http.put(
Uri.parse('$baseUrl/update-email'),
headers: {
..._headers,
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode({'email': newEmail}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'] ?? 'Gagal memperbarui email',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= UPDATE PASSWORD =================
static Future<Map<String, dynamic>> updatePassword(
String oldPassword,
String newPassword,
) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
final response = await http.put(
Uri.parse('$baseUrl/update-password'),
headers: {
..._headers,
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode({
'old_password': oldPassword,
'new_password': newPassword,
'confirm_password': newPassword,
}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'],
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= SEND EMAIL OTP (untuk ganti email) =================
/// Mengirim kode OTP 6 digit ke [newEmail] yang akan digunakan sebagai email baru.
/// Backend: POST /email/send-otp { email: newEmail }
/// Response yang diharapkan: { status: true, message: "..." }
static Future<Map<String, dynamic>> sendEmailOtp(String newEmail) async {
try {
final token = await getToken();
if (token == null) {
return {'success': false, 'message': 'Token tidak ditemukan'};
}
final response = await http.post(
Uri.parse('$baseUrl/email/send-otp'),
headers: {
..._headers,
'Authorization': 'Bearer $token',
},
body: jsonEncode({'email': newEmail}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'] ?? 'Gagal mengirim OTP',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= VERIFY EMAIL OTP (untuk ganti email) =================
/// Memverifikasi [otp] yang dikirim ke [newEmail], sekaligus mengganti email user.
/// Backend: POST /email/verify-otp { email: newEmail, otp: "123456" }
/// Response yang diharapkan: { status: true, message: "..." }
static Future<Map<String, dynamic>> verifyEmailOtp(
String newEmail,
String otp,
) async {
try {
final token = await getToken();
if (token == null) {
return {'success': false, 'message': 'Token tidak ditemukan'};
}
final response = await http.post(
Uri.parse('$baseUrl/email/verify-otp'),
headers: {
..._headers,
'Authorization': 'Bearer $token',
},
body: jsonEncode({'email': newEmail, 'otp': otp}),
);
final data = jsonDecode(response.body);
return {
'success': response.statusCode == 200 && data['status'] == true,
'message': data['message'] ?? 'OTP tidak valid atau sudah kadaluarsa',
};
} catch (e) {
return {'success': false, 'message': e.toString()};
}
}
/// ================= LOGOUT =================
static Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
}
}