diff --git a/src/utils/supabase/admin.ts b/src/utils/supabase/admin.ts
index 277d2cf..faadb47 100644
--- a/src/utils/supabase/admin.ts
+++ b/src/utils/supabase/admin.ts
@@ -1,14 +1,20 @@
import { createClient } from '@supabase/supabase-js'
export function createAdminClient() {
+ const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
+ const key = process.env.SUPABASE_SERVICE_ROLE_KEY;
+
+ console.log('Admin Client Init - URL:', url);
+ console.log('Admin Client Init - Key Length:', key?.length);
+
return createClient(
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
- process.env.SUPABASE_SERVICE_ROLE_KEY!,
+ url!,
+ key!,
{
auth: {
autoRefreshToken: false,
persistSession: false
}
}
- )
+ );
}
diff --git a/supabase/migrations/20240319000001_fix_employees_rls.sql b/supabase/migrations/20240319000001_fix_employees_rls.sql
new file mode 100644
index 0000000..f737ba8
--- /dev/null
+++ b/supabase/migrations/20240319000001_fix_employees_rls.sql
@@ -0,0 +1,94 @@
+-- Fix RLS Policies for Employees Table (V5 - Architectural Fix)
+-- This version moves user context to the 'users' table to break recursion.
+
+-- 1. Extend public.users table (Idempotent)
+DO $$
+BEGIN
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='role_name') THEN
+ ALTER TABLE public.users ADD COLUMN role_name TEXT;
+ END IF;
+ IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='company_id') THEN
+ ALTER TABLE public.users ADD COLUMN company_id UUID;
+ END IF;
+END $$;
+
+-- 2. Create Sync Function
+CREATE OR REPLACE FUNCTION public.sync_user_security_context()
+RETURNS TRIGGER AS $$
+BEGIN
+ -- Update the users table when an employee is linked to a user_id
+ IF NEW.user_id IS NOT NULL THEN
+ UPDATE public.users
+ SET
+ role_name = (SELECT name FROM public.roles WHERE id = NEW.role_id),
+ company_id = NEW.company_id
+ WHERE id = NEW.user_id;
+ END IF;
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;
+
+-- 3. Create Sync Trigger
+DROP TRIGGER IF EXISTS on_employee_security_change ON public.employees;
+CREATE TRIGGER on_employee_security_change
+AFTER INSERT OR UPDATE OF role_id, company_id, user_id ON public.employees
+FOR EACH ROW
+EXECUTE FUNCTION public.sync_user_security_context();
+
+-- 4. Initial Sync (Seed existing users)
+UPDATE public.users u
+SET
+ role_name = r.name,
+ company_id = e.company_id
+FROM public.employees e
+JOIN public.roles r ON e.role_id = r.id
+WHERE e.user_id = u.id;
+
+-- 5. Helper Function (Bypass RLS)
+-- This one is now super safe because it only checks 'users' table
+CREATE OR REPLACE FUNCTION public.is_authorized_to_manage(target_company_id UUID)
+RETURNS BOOLEAN AS $$
+BEGIN
+ -- Allow if table is empty (initial setup)
+ IF NOT EXISTS (SELECT 1 FROM public.employees) THEN
+ RETURN TRUE;
+ END IF;
+
+ -- Check users table for cached role info
+ -- Since we are querying 'users' from an 'employees' policy, there is NO RECURSION.
+ RETURN EXISTS (
+ SELECT 1 FROM public.users
+ WHERE id = auth.uid()
+ AND (role_name = 'admin' OR (role_name = 'manager' AND company_id = target_company_id))
+ );
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;
+
+-- 6. Redefine Employees Policies
+DROP POLICY IF EXISTS "Employees unified select" ON public.employees;
+DROP POLICY IF EXISTS "Employees unified insert" ON public.employees;
+DROP POLICY IF EXISTS "Employees unified update" ON public.employees;
+DROP POLICY IF EXISTS "Employees unified delete" ON public.employees;
+DROP POLICY IF EXISTS "View employees policy" ON public.employees;
+DROP POLICY IF EXISTS "Manage employees policy" ON public.employees;
+DROP POLICY IF EXISTS "Initial setup allowance" ON public.employees;
+
+-- SELECT: Colleagues can see each other, Admins see all
+CREATE POLICY "Employees select policy"
+ON public.employees
+FOR SELECT TO authenticated
+USING (
+ user_id = auth.uid() -- Can see self
+ OR EXISTS (
+ SELECT 1 FROM public.users u
+ WHERE u.id = auth.uid()
+ AND (u.role_name = 'admin' OR u.company_id = employees.company_id)
+ )
+);
+
+-- INSERT/UPDATE/DELETE
+CREATE POLICY "Employees management policy"
+ON public.employees
+FOR ALL TO authenticated
+USING ( public.is_authorized_to_manage(company_id) )
+WITH CHECK ( public.is_authorized_to_manage(company_id) );