165 lines
5.6 KiB
Dart
165 lines
5.6 KiB
Dart
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
|
|
import 'package:dio/dio.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '../models/user_model.dart';
|
|
import '../core/api_config.dart';
|
|
import 'auth_service.dart';
|
|
|
|
/// Implementasi [AuthService] menggunakan layanan **Supabase Auth** untuk autentikasi user
|
|
/// dan **SharedPreferences** untuk menyimpan cache lokal data `device_id` IoT.
|
|
class SupabaseAuthService implements AuthService {
|
|
final supabase.SupabaseClient _client = supabase.Supabase.instance.client;
|
|
final Dio _dio = Dio();
|
|
|
|
@override
|
|
Future<UserModel> login(String email, String password) async {
|
|
try {
|
|
// 1. Melakukan sign in ke Supabase Auth
|
|
final response = await _client.auth.signInWithPassword(
|
|
email: email,
|
|
password: password,
|
|
);
|
|
|
|
final user = response.user;
|
|
if (user == null) {
|
|
throw Exception('Login gagal: User tidak ditemukan');
|
|
}
|
|
|
|
// 2. Mengambil informasi device_id yang terhubung ke pengguna dari database backend
|
|
String? deviceId;
|
|
try {
|
|
final deviceRes = await _dio.get('${ApiConfig.baseUrl}/device/my-device/${user.id}');
|
|
if (deviceRes.statusCode == 200) {
|
|
deviceId = deviceRes.data['device_id'];
|
|
}
|
|
} catch (e) {
|
|
print('Gagal mengambil info device: $e');
|
|
// Tidak melempar error agar login tetap sukses meskipun user belum menghubungkan device
|
|
}
|
|
|
|
// 3. Menyimpan device_id ke cache lokal (SharedPreferences) agar auto-login selanjutnya lebih cepat dan offline-safe
|
|
if (deviceId != null) {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString('cached_device_id', deviceId);
|
|
}
|
|
|
|
return UserModel(
|
|
id: user.id,
|
|
name: user.userMetadata?['name'] ?? user.email?.split('@').first ?? 'User',
|
|
email: user.email ?? email,
|
|
token: response.session?.accessToken ?? '',
|
|
deviceId: deviceId,
|
|
);
|
|
} on supabase.AuthException catch (e) {
|
|
// Pemetaan pesan error dari Supabase ke bahasa Indonesia yang ramah pengguna
|
|
if (e.message.contains('Invalid login credentials')) {
|
|
throw Exception('Email atau password salah.');
|
|
} else if (e.message.contains('Email not confirmed')) {
|
|
throw Exception('Email belum diverifikasi.');
|
|
}
|
|
throw Exception(e.message);
|
|
} catch (e) {
|
|
throw Exception('Terjadi kesalahan koneksi atau server.');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<UserModel> register(String name, String email, String password) async {
|
|
try {
|
|
// Melakukan pendaftaran akun ke Supabase Auth beserta meta data nama lengkap
|
|
final response = await _client.auth.signUp(
|
|
email: email,
|
|
password: password,
|
|
data: {'name': name},
|
|
);
|
|
|
|
final user = response.user;
|
|
if (user == null) {
|
|
throw Exception('Registrasi gagal');
|
|
}
|
|
|
|
return UserModel(
|
|
id: user.id,
|
|
name: name,
|
|
email: email,
|
|
token: response.session?.accessToken ?? '',
|
|
deviceId: null,
|
|
);
|
|
} on supabase.AuthException catch (e) {
|
|
if (e.message.contains('already registered')) {
|
|
throw Exception('Email sudah terdaftar.');
|
|
} else if (e.message.contains('weak')) {
|
|
throw Exception('Password terlalu lemah.');
|
|
}
|
|
throw Exception(e.message);
|
|
} catch (e) {
|
|
throw Exception('Terjadi kesalahan koneksi atau server.');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<String> claimDevice(String userId, String claimCode) async {
|
|
try {
|
|
// Mengirim request klaim alat ke REST API backend
|
|
final claimRes = await _dio.post('${ApiConfig.baseUrl}/device/claim', data: {
|
|
'claim_code': claimCode.trim(),
|
|
'user_id': userId,
|
|
});
|
|
|
|
if (claimRes.statusCode == 200) {
|
|
final deviceId = claimRes.data['device']['device_id'] as String;
|
|
// Menyimpan ID perangkat yang berhasil diklaim ke SharedPreferences
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString('cached_device_id', deviceId);
|
|
return deviceId;
|
|
} else {
|
|
throw Exception('Gagal klaim perangkat: ${claimRes.statusCode}');
|
|
}
|
|
} catch (e) {
|
|
String errorMsg = 'Gagal klaim perangkat.';
|
|
if (e is DioException && e.response?.data != null) {
|
|
errorMsg = e.response?.data['message'] ?? errorMsg;
|
|
}
|
|
throw Exception(errorMsg);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> logout() async {
|
|
// 1. Membersihkan sesi Supabase Auth di server
|
|
await _client.auth.signOut();
|
|
|
|
// 2. Menghapus cache lokal device_id agar bersih saat login pengguna lain
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.remove('cached_device_id');
|
|
}
|
|
|
|
/// Memulihkan sesi login yang tersimpan di internal storage untuk Auto-Login.
|
|
/// Mengambil data user dari sesi aktif Supabase, dan device_id dari SharedPreferences.
|
|
@override
|
|
Future<UserModel?> checkSession() async {
|
|
final session = _client.auth.currentSession;
|
|
if (session != null) {
|
|
final user = session.user;
|
|
|
|
// Mengambil device_id dari cache lokal (cepat & aman saat offline)
|
|
String? deviceId;
|
|
try {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
deviceId = prefs.getString('cached_device_id');
|
|
} catch (e) {
|
|
print('Gagal membaca cache device_id: $e');
|
|
}
|
|
|
|
return UserModel(
|
|
id: user.id,
|
|
name: user.userMetadata?['name'] ?? user.email?.split('@').first ?? 'User',
|
|
email: user.email ?? '',
|
|
token: session.accessToken,
|
|
deviceId: deviceId,
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
}
|