Files
fichajes/supabase/schema.sql
Antoni Nuñez Romeu 114fda056d
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 3m51s
Hotfixes and CI/CD
2026-04-17 15:33:19 +02:00

192 lines
5.9 KiB
PL/PgSQL

-- ============================================================
-- Time Tracker App - Teams & Privileges Schema
-- Run this SQL in your Supabase SQL Editor
-- ============================================================
-- 1. Teams table
CREATE TABLE IF NOT EXISTS teams (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXT NOT NULL,
description TEXT DEFAULT '',
created_at TIMESTAMPTZ DEFAULT now(),
created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL
);
-- 2. Team Members table (links users to teams with roles)
CREATE TABLE IF NOT EXISTS team_members (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'manager', 'user')),
joined_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(team_id, user_id)
);
-- 3. User Profiles table (extends auth.users with global role)
CREATE TABLE IF NOT EXISTS user_profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
full_name TEXT DEFAULT '',
phone TEXT DEFAULT '',
company TEXT DEFAULT '',
avatar_url TEXT DEFAULT '',
global_role TEXT NOT NULL DEFAULT 'user' CHECK (global_role IN ('admin', 'manager', 'user')),
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============================================================
-- Indexes
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_team_members_team_id ON team_members(team_id);
CREATE INDEX IF NOT EXISTS idx_team_members_user_id ON team_members(user_id);
CREATE INDEX IF NOT EXISTS idx_user_profiles_global_role ON user_profiles(global_role);
-- ============================================================
-- Row Level Security (RLS)
-- ============================================================
-- Teams: anyone authenticated can read; only admins can insert/update/delete
ALTER TABLE teams ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Teams are viewable by authenticated users"
ON teams FOR SELECT
TO authenticated
USING (true);
CREATE POLICY "Admins can insert teams"
ON teams FOR INSERT
TO authenticated
WITH CHECK (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND global_role = 'admin'
)
);
CREATE POLICY "Admins can update teams"
ON teams FOR UPDATE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND global_role = 'admin'
)
);
CREATE POLICY "Admins can delete teams"
ON teams FOR DELETE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND global_role = 'admin'
)
);
-- Team Members: authenticated users can read; admins/managers can manage
ALTER TABLE team_members ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Team members are viewable by authenticated users"
ON team_members FOR SELECT
TO authenticated
USING (true);
CREATE POLICY "Admins can insert team members"
ON team_members FOR INSERT
TO authenticated
WITH CHECK (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND global_role IN ('admin', 'manager')
)
);
CREATE POLICY "Admins can update team members"
ON team_members FOR UPDATE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND global_role IN ('admin', 'manager')
)
);
CREATE POLICY "Admins can delete team members"
ON team_members FOR DELETE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND global_role IN ('admin', 'manager')
)
);
-- User Profiles: users can read all profiles, update their own; admins can update any
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "User profiles are viewable by authenticated users"
ON user_profiles FOR SELECT
TO authenticated
USING (true);
CREATE POLICY "Users can update own profile"
ON user_profiles FOR UPDATE
TO authenticated
USING (auth.uid() = id);
CREATE POLICY "Admins can update any profile"
ON user_profiles FOR UPDATE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND global_role = 'admin'
)
);
CREATE POLICY "Users can insert own profile"
ON user_profiles FOR INSERT
TO authenticated
WITH CHECK (auth.uid() = id);
-- ============================================================
-- Trigger: Auto-create user_profiles on signup
-- ============================================================
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.user_profiles (id, full_name, avatar_url)
VALUES (
NEW.id,
COALESCE(NEW.raw_user_meta_data->>'full_name', ''),
COALESCE(NEW.raw_user_meta_data->>'avatar_url', '')
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
-- ============================================================
-- Trigger: Auto-update updated_at on user_profiles
-- ============================================================
CREATE OR REPLACE FUNCTION public.update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS update_user_profiles_updated_at ON user_profiles;
CREATE TRIGGER update_user_profiles_updated_at
BEFORE UPDATE ON user_profiles
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at();
-- ============================================================
-- Seed: Make the first user an admin (adjust the user_id as needed)
-- ============================================================
-- UPDATE user_profiles SET global_role = 'admin' WHERE id = 'YOUR_USER_ID_HERE';