244 lines
8.1 KiB
Dart
244 lines
8.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'dart:convert';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
/// Utility class to fix the fields table schema
|
|
class FixFieldsTableUtil {
|
|
/// Fix the fields table schema
|
|
static Future<void> fixFieldsTable(BuildContext context) async {
|
|
try {
|
|
// Show loading dialog
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder:
|
|
(context) => const AlertDialog(
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
CircularProgressIndicator(),
|
|
SizedBox(height: 16),
|
|
Text('Memperbaiki struktur tabel fields...'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final client = Supabase.instance.client;
|
|
|
|
// Clear cache first
|
|
await _clearSupabaseCache();
|
|
|
|
// Step 1: Check if updated_at column exists
|
|
final columnsResult = await client
|
|
.rpc(
|
|
'check_column_exists',
|
|
params: {'table_name': 'fields', 'column_name': 'updated_at'},
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
|
|
print('Column check result: $columnsResult');
|
|
|
|
// Step 2: Add updated_at column if it doesn't exist
|
|
if (columnsResult == false) {
|
|
await client
|
|
.rpc(
|
|
'execute_sql',
|
|
params: {
|
|
'sql_statement':
|
|
'ALTER TABLE public.fields ADD COLUMN updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()',
|
|
},
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
print('Added updated_at column');
|
|
}
|
|
|
|
// Step 3: Create or replace the update trigger function
|
|
await client
|
|
.rpc(
|
|
'execute_sql',
|
|
params: {
|
|
'sql_statement':
|
|
'CREATE OR REPLACE FUNCTION update_fields_updated_at() RETURNS TRIGGER AS \$\$ BEGIN NEW.updated_at = now(); RETURN NEW; END; \$\$ LANGUAGE plpgsql;',
|
|
},
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
print('Created trigger function');
|
|
|
|
// Step 4: Create the trigger if it doesn't exist
|
|
await client
|
|
.rpc(
|
|
'execute_sql',
|
|
params: {
|
|
'sql_statement':
|
|
'DROP TRIGGER IF EXISTS trigger_fields_updated_at ON public.fields; CREATE TRIGGER trigger_fields_updated_at BEFORE UPDATE ON public.fields FOR EACH ROW EXECUTE PROCEDURE update_fields_updated_at();',
|
|
},
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
print('Created trigger');
|
|
|
|
// Step 5: Update all fields to set updated_at = created_at where null
|
|
await client
|
|
.rpc(
|
|
'execute_sql',
|
|
params: {
|
|
'sql_statement':
|
|
'UPDATE public.fields SET updated_at = created_at WHERE updated_at IS NULL',
|
|
},
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
print('Updated null updated_at values');
|
|
|
|
// Step 6: Fix RLS policies to avoid recursion issues
|
|
await _fixRLSPolicies(client);
|
|
|
|
// Close the dialog
|
|
Navigator.pop(context);
|
|
|
|
// Show success message
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Struktur tabel fields berhasil diperbaiki'),
|
|
backgroundColor: Colors.green,
|
|
),
|
|
);
|
|
} catch (e) {
|
|
print('Error fixing fields table: $e');
|
|
|
|
// Close the dialog if it's open
|
|
Navigator.pop(context);
|
|
|
|
// Show error message
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
'Gagal memperbaiki struktur tabel fields: ${e.toString()}',
|
|
),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Fix RLS policies to avoid recursion issues
|
|
static Future<void> _fixRLSPolicies(SupabaseClient client) async {
|
|
try {
|
|
// Fix user_roles policies
|
|
await client
|
|
.rpc(
|
|
'execute_sql',
|
|
params: {
|
|
'sql_statement': '''
|
|
-- Remove existing policies from user_roles
|
|
DROP POLICY IF EXISTS "Users can view their own roles" ON public.user_roles;
|
|
DROP POLICY IF EXISTS "Users can insert their own roles" ON public.user_roles;
|
|
DROP POLICY IF EXISTS "Users can update their own roles" ON public.user_roles;
|
|
DROP POLICY IF EXISTS "Users can delete their own roles" ON public.user_roles;
|
|
|
|
-- Create simplified policies for user_roles
|
|
CREATE POLICY "Enable read access for authenticated users"
|
|
ON public.user_roles FOR SELECT
|
|
USING (auth.role() = 'authenticated');
|
|
|
|
CREATE POLICY "Enable insert access for authenticated users"
|
|
ON public.user_roles FOR INSERT
|
|
WITH CHECK (auth.role() = 'authenticated');
|
|
|
|
CREATE POLICY "Enable update for users based on user_id"
|
|
ON public.user_roles FOR UPDATE
|
|
USING (auth.uid() = user_id)
|
|
WITH CHECK (auth.uid() = user_id);
|
|
|
|
CREATE POLICY "Enable delete for users based on user_id"
|
|
ON public.user_roles FOR DELETE
|
|
USING (auth.uid() = user_id);
|
|
''',
|
|
},
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
|
|
// Fix fields policies
|
|
await client
|
|
.rpc(
|
|
'execute_sql',
|
|
params: {
|
|
'sql_statement': '''
|
|
-- Remove existing policies from fields
|
|
DROP POLICY IF EXISTS "Users can view their own fields" ON public.fields;
|
|
DROP POLICY IF EXISTS "Users can insert their own fields" ON public.fields;
|
|
DROP POLICY IF EXISTS "Users can update their own fields" ON public.fields;
|
|
DROP POLICY IF EXISTS "Users can delete their own fields" ON public.fields;
|
|
|
|
-- Create simplified policies for fields
|
|
CREATE POLICY "Enable read access for authenticated users"
|
|
ON public.fields FOR SELECT
|
|
USING (auth.role() = 'authenticated');
|
|
|
|
CREATE POLICY "Enable insert access for authenticated users"
|
|
ON public.fields FOR INSERT
|
|
WITH CHECK (auth.uid() = user_id);
|
|
|
|
CREATE POLICY "Enable update for users based on user_id"
|
|
ON public.fields FOR UPDATE
|
|
USING (auth.uid() = user_id)
|
|
WITH CHECK (auth.uid() = user_id);
|
|
|
|
CREATE POLICY "Enable delete for users based on user_id"
|
|
ON public.fields FOR DELETE
|
|
USING (auth.uid() = user_id);
|
|
''',
|
|
},
|
|
)
|
|
.timeout(const Duration(seconds: 10));
|
|
|
|
print('Fixed RLS policies');
|
|
} catch (e) {
|
|
print('Error fixing RLS policies: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// Clear Supabase cache to help with stuck issues
|
|
static Future<void> _clearSupabaseCache() async {
|
|
try {
|
|
// Clear SharedPreferences cache
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final keys =
|
|
prefs
|
|
.getKeys()
|
|
.where(
|
|
(key) =>
|
|
key.startsWith('supabase') ||
|
|
key.contains('auth') ||
|
|
key.contains('fields') ||
|
|
key.contains('cache'),
|
|
)
|
|
.toList();
|
|
|
|
for (var key in keys) {
|
|
await prefs.remove(key);
|
|
}
|
|
|
|
print('Cleared ${keys.length} cache entries');
|
|
|
|
// Force refresh auth session
|
|
try {
|
|
await Supabase.instance.client.auth.refreshSession();
|
|
print('Auth session refreshed');
|
|
} catch (e) {
|
|
print('Error refreshing auth session: $e');
|
|
// Continue even if this fails
|
|
}
|
|
} catch (e) {
|
|
print('Error clearing cache: $e');
|
|
// Continue even if this fails
|
|
}
|
|
}
|
|
|
|
/// Public method to clear cache
|
|
static Future<void> clearCache() async {
|
|
await _clearSupabaseCache();
|
|
}
|
|
}
|