Hotfixes and CI/CD
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 3m51s
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 3m51s
This commit is contained in:
48
supabase/close_sessions_cron.sql
Normal file
48
supabase/close_sessions_cron.sql
Normal file
@@ -0,0 +1,48 @@
|
||||
-- ============================================================
|
||||
-- Time Tracker App - Automatic Session Closure at Midnight
|
||||
-- Run this SQL in your Supabase SQL Editor
|
||||
-- ============================================================
|
||||
|
||||
-- Check if pg_cron extension is available
|
||||
CREATE EXTENSION IF NOT EXISTS pg_cron;
|
||||
|
||||
-- Function to close active sessions at midnight
|
||||
-- This will only close sessions that are currently running (end_time IS NULL)
|
||||
-- and will preserve all session history
|
||||
CREATE OR REPLACE FUNCTION close_active_sessions_at_midnight()
|
||||
RETURNS void AS $$
|
||||
DECLARE
|
||||
closed_count INTEGER;
|
||||
BEGIN
|
||||
-- Update all active sessions (where end_time is NULL)
|
||||
-- Set end_time to today at 00:00:00 (midnight)
|
||||
-- This closes only currently active sessions, preserving all history
|
||||
UPDATE timers
|
||||
SET end_time = DATE_TRUNC('day', NOW()),
|
||||
duration_ms = EXTRACT(EPOCH FROM (DATE_TRUNC('day', NOW()) - start_time)) * 1000
|
||||
WHERE end_time IS NULL
|
||||
AND start_time < DATE_TRUNC('day', NOW());
|
||||
|
||||
-- Get the count of closed sessions for the notice
|
||||
GET DIAGNOSTICS closed_count = ROW_COUNT;
|
||||
|
||||
RAISE NOTICE 'Closed % active sessions at midnight (history preserved)', closed_count;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Schedule the function to run daily at midnight (00:00)
|
||||
-- This will run in the timezone of your Supabase project
|
||||
SELECT cron.schedule(
|
||||
'close-active-sessions-at-midnight', -- job name
|
||||
'0 0 * * *', -- cron expression (at 00:00 every day)
|
||||
$$SELECT close_active_sessions_at_midnight()$$
|
||||
);
|
||||
|
||||
-- To manually test the function, you can run:
|
||||
-- SELECT close_active_sessions_at_midnight();
|
||||
|
||||
-- To view all cron jobs:
|
||||
-- SELECT * FROM cron.job;
|
||||
|
||||
-- To unschedule this job:
|
||||
-- SELECT cron.unschedule('close-active-sessions-at-midnight');
|
||||
192
supabase/schema.sql
Normal file
192
supabase/schema.sql
Normal file
@@ -0,0 +1,192 @@
|
||||
-- ============================================================
|
||||
-- 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';
|
||||
Reference in New Issue
Block a user