621 lines
19 KiB
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();
|
|
}
|
|
} |