MIF_E31222656/lib/services/auth_services.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;
}
}
}