358 lines
10 KiB
Dart
358 lines
10 KiB
Dart
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'dart:async';
|
|
import 'package:get_it/get_it.dart';
|
|
import 'package:tugas_akhir_supabase/services/user_presence_service.dart';
|
|
import 'package:tugas_akhir_supabase/services/session_manager.dart';
|
|
|
|
class AuthServices {
|
|
final SupabaseClient _supabase = Supabase.instance.client;
|
|
|
|
// Sign in with email and password
|
|
Future<AuthResponse> signInWithEmailPassword(
|
|
String email,
|
|
String password,
|
|
) async {
|
|
try {
|
|
// Tambahkan timeout untuk mencegah permintaan menggantung
|
|
final response = await _supabase.auth
|
|
.signInWithPassword(email: email, password: password)
|
|
.timeout(
|
|
const Duration(seconds: 15),
|
|
onTimeout: () {
|
|
throw TimeoutException(
|
|
'Koneksi timeout. Silakan coba lagi nanti.',
|
|
);
|
|
},
|
|
);
|
|
|
|
// Register and initialize UserPresenceService after successful login
|
|
await _registerUserPresenceService();
|
|
|
|
// Update session manager
|
|
SessionManager.setUserLoggedIn(true);
|
|
|
|
return response;
|
|
} catch (e) {
|
|
debugPrint('Error saat login: $e');
|
|
|
|
// Re-throw exception untuk ditangani di UI
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
// Register and initialize UserPresenceService
|
|
Future<void> _registerUserPresenceService() async {
|
|
try {
|
|
final currentUser = _supabase.auth.currentUser;
|
|
if (currentUser == null) return;
|
|
|
|
// Register the service if not already registered
|
|
if (!GetIt.instance.isRegistered<UserPresenceService>()) {
|
|
debugPrint('Registering UserPresenceService after login');
|
|
GetIt.instance.registerSingleton<UserPresenceService>(
|
|
UserPresenceService(),
|
|
);
|
|
await GetIt.instance<UserPresenceService>().initialize();
|
|
} else {
|
|
// If already registered, just initialize it
|
|
debugPrint('UserPresenceService already registered, initializing');
|
|
await GetIt.instance<UserPresenceService>().initialize();
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error registering UserPresenceService: $e');
|
|
}
|
|
}
|
|
|
|
// Sign Up with email and password
|
|
Future<AuthResponse> signUpWithEmailPassword(
|
|
String email,
|
|
String password,
|
|
) async {
|
|
final response = await _supabase.auth.signUp(
|
|
email: email,
|
|
password: password,
|
|
);
|
|
|
|
return response;
|
|
}
|
|
|
|
// Sign Out
|
|
Future<void> signOut() async {
|
|
try {
|
|
// Dispose UserPresenceService if registered
|
|
if (GetIt.instance.isRegistered<UserPresenceService>()) {
|
|
debugPrint('Disposing UserPresenceService during sign out');
|
|
GetIt.instance<UserPresenceService>().dispose();
|
|
|
|
// Unregister the service
|
|
if (GetIt.instance.isRegistered<UserPresenceService>()) {
|
|
GetIt.instance.unregister<UserPresenceService>();
|
|
}
|
|
}
|
|
|
|
// Update session manager
|
|
SessionManager.setUserLoggedIn(false);
|
|
|
|
// Sign out from Supabase
|
|
await _supabase.auth.signOut();
|
|
} catch (e) {
|
|
debugPrint('Error during sign out: $e');
|
|
// Still try to sign out from Supabase even if there was an error
|
|
await _supabase.auth.signOut();
|
|
}
|
|
}
|
|
|
|
// Get current user ID
|
|
String? getCurrentUserId() {
|
|
final session = _supabase.auth.currentSession;
|
|
final user = session?.user;
|
|
return user?.id;
|
|
}
|
|
|
|
// Request password reset (sends email with OTP)
|
|
Future<void> forgotPassword(String email, {String? redirectUrl}) async {
|
|
try {
|
|
await _supabase.auth.resetPasswordForEmail(
|
|
email,
|
|
redirectTo: redirectUrl,
|
|
);
|
|
} catch (e) {
|
|
debugPrint('Error sending password reset email: $e');
|
|
throw Exception('Gagal mengirim email reset password: $e');
|
|
}
|
|
}
|
|
|
|
// Verify OTP for password reset or signup
|
|
Future<AuthResponse> verifyOTP({
|
|
required String email,
|
|
required String token,
|
|
required OtpType type,
|
|
}) async {
|
|
try {
|
|
final response = await _supabase.auth.verifyOTP(
|
|
email: email,
|
|
token: token,
|
|
type: type,
|
|
);
|
|
|
|
return response;
|
|
} catch (e) {
|
|
debugPrint('Error verifying OTP: $e');
|
|
throw Exception('Verifikasi OTP gagal: $e');
|
|
}
|
|
}
|
|
|
|
// Reset password (after OTP verification)
|
|
Future<void> resetPassword(String newPassword) async {
|
|
try {
|
|
await _supabase.auth.updateUser(UserAttributes(password: newPassword));
|
|
} catch (e) {
|
|
debugPrint('Error resetting password: $e');
|
|
throw Exception('Gagal mengubah password: $e');
|
|
}
|
|
}
|
|
|
|
// Check if user is logged in (without checking timeout)
|
|
bool isUserLoggedIn() {
|
|
return _supabase.auth.currentSession != null;
|
|
}
|
|
|
|
// Get current authenticated user
|
|
User? getCurrentUser() {
|
|
return _supabase.auth.currentUser;
|
|
}
|
|
|
|
// Get current user email
|
|
String? getCurrentUserEmail() {
|
|
final session = _supabase.auth.currentSession;
|
|
final user = session?.user;
|
|
return user?.email;
|
|
}
|
|
|
|
// Get user role
|
|
Future<String?> getUserRole() async {
|
|
final userId = getCurrentUserId();
|
|
if (userId == null) return null;
|
|
|
|
try {
|
|
debugPrint('Mencoba mendapatkan role untuk user ID: $userId');
|
|
|
|
// Coba query langsung ke tabel
|
|
final response =
|
|
await _supabase
|
|
.from('user_roles')
|
|
.select('role')
|
|
.eq('user_id', userId)
|
|
.maybeSingle();
|
|
|
|
debugPrint('Response dari query user_roles: $response');
|
|
|
|
if (response != null) {
|
|
final role = response['role'] as String?;
|
|
debugPrint('Role ditemukan: $role');
|
|
return role;
|
|
} else {
|
|
debugPrint('Tidak ada role yang ditemukan untuk user ID: $userId');
|
|
return null;
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error getting user role: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Check if user is admin
|
|
Future<bool> isAdmin() async {
|
|
try {
|
|
final userId = getCurrentUserId();
|
|
debugPrint('Checking admin status for user ID: $userId');
|
|
|
|
if (userId == null) {
|
|
debugPrint('User ID is null, not an admin');
|
|
return false;
|
|
}
|
|
|
|
// First try: cek langsung dari tabel tanpa RLS
|
|
try {
|
|
debugPrint('Trying direct table query for admin check...');
|
|
final response =
|
|
await _supabase
|
|
.from('user_roles')
|
|
.select('role')
|
|
.eq('user_id', userId)
|
|
.eq('role', 'admin')
|
|
.maybeSingle();
|
|
|
|
final isAdmin = response != null;
|
|
debugPrint('Direct table query result: $isAdmin');
|
|
if (isAdmin) return true;
|
|
} catch (directError) {
|
|
debugPrint('Direct table query failed: $directError');
|
|
}
|
|
|
|
// Second try: gunakan fungsi is_admin_no_rls untuk menghindari infinite recursion
|
|
try {
|
|
debugPrint('Trying RPC function for admin check...');
|
|
final response = await _supabase.rpc(
|
|
'is_admin_no_rls',
|
|
params: {'input_user_id': userId},
|
|
);
|
|
|
|
debugPrint('Admin check RPC response: $response');
|
|
|
|
// Response dari RPC akan berupa boolean
|
|
final isAdmin = response == true;
|
|
debugPrint('RPC admin check result: $isAdmin');
|
|
return isAdmin;
|
|
} catch (rpcError) {
|
|
debugPrint('RPC function failed: $rpcError');
|
|
}
|
|
|
|
// Third try: cek dengan query yang lebih sederhana
|
|
try {
|
|
debugPrint('Trying simple query for admin check...');
|
|
final response = await _supabase
|
|
.from('user_roles')
|
|
.select('*')
|
|
.eq('user_id', userId)
|
|
.eq('role', 'admin')
|
|
.limit(1);
|
|
|
|
final isAdmin = response.isNotEmpty;
|
|
debugPrint('Simple query result: $isAdmin');
|
|
return isAdmin;
|
|
} catch (simpleError) {
|
|
debugPrint('Simple query failed: $simpleError');
|
|
}
|
|
|
|
// If all methods fail, return false
|
|
debugPrint('All admin check methods failed, returning false');
|
|
return false;
|
|
} catch (e) {
|
|
debugPrint('Error checking admin status: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Force refresh user session
|
|
Future<void> refreshSession() async {
|
|
try {
|
|
final session = _supabase.auth.currentSession;
|
|
if (session != null) {
|
|
await _supabase.auth.refreshSession();
|
|
debugPrint('Session refreshed successfully');
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error refreshing session: $e');
|
|
}
|
|
}
|
|
|
|
// Get all admins
|
|
Future<List<Map<String, dynamic>>> getAllAdmins() async {
|
|
try {
|
|
final response = await _supabase
|
|
.from('user_roles')
|
|
.select('user_id')
|
|
.eq('role', 'admin');
|
|
|
|
return List<Map<String, dynamic>>.from(response);
|
|
} catch (e) {
|
|
debugPrint('Error getting all admins: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// Count admins
|
|
Future<int> countAdmins() async {
|
|
try {
|
|
final admins = await getAllAdmins();
|
|
return admins.length;
|
|
} catch (e) {
|
|
debugPrint('Error counting admins: $e');
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Future<AuthResponse> signInWithEmailAndPassword(
|
|
String email,
|
|
String password,
|
|
) async {
|
|
try {
|
|
debugPrint('Attempting to sign in with email: $email');
|
|
final response = await _supabase.auth.signInWithPassword(
|
|
email: email,
|
|
password: password,
|
|
);
|
|
debugPrint('Sign in response: ${response.session != null}');
|
|
return response;
|
|
} on AuthException catch (e) {
|
|
debugPrint('AuthException during sign in: ${e.message}');
|
|
rethrow;
|
|
} on PostgrestException catch (e) {
|
|
debugPrint('PostgrestException during sign in: ${e.message}');
|
|
|
|
// Check for infinite recursion error
|
|
if (e.code == '42P17' && e.message.contains('infinite recursion')) {
|
|
throw Exception(
|
|
'Terjadi masalah pada kebijakan database. Silakan hubungi admin untuk memperbaiki kebijakan users atau gunakan tombol "Perbaiki Database" jika Anda adalah admin.',
|
|
);
|
|
}
|
|
|
|
rethrow;
|
|
} catch (e) {
|
|
debugPrint('Unknown exception during sign in: $e');
|
|
|
|
// Check for infinite recursion error in generic exception
|
|
if (e.toString().contains('infinite recursion') &&
|
|
e.toString().contains('42P17')) {
|
|
throw Exception(
|
|
'Terjadi masalah pada kebijakan database. Silakan hubungi admin untuk memperbaiki kebijakan users atau gunakan tombol "Perbaiki Database" jika Anda adalah admin.',
|
|
);
|
|
}
|
|
|
|
rethrow;
|
|
}
|
|
}
|
|
}
|