Files
acmcc/supabase/migrations/20260327005648_76d23085-8c9e-4820-a3ea-ccf022b96156.sql
T
2026-06-01 20:19:26 -04:00

155 lines
7.4 KiB
SQL

-- Elections table
CREATE TABLE public.elections (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
association_id UUID NOT NULL REFERENCES public.associations(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT,
election_type TEXT NOT NULL DEFAULT 'board_election',
status TEXT NOT NULL DEFAULT 'draft',
voting_start TIMESTAMPTZ,
voting_end TIMESTAMPTZ,
results_locked BOOLEAN NOT NULL DEFAULT true,
quorum_required INTEGER NOT NULL DEFAULT 0,
proxies_count INTEGER NOT NULL DEFAULT 0,
in_person_count INTEGER NOT NULL DEFAULT 0,
created_by UUID REFERENCES auth.users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Election positions (e.g. President, Treasurer)
CREATE TABLE public.election_positions (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
election_id UUID NOT NULL REFERENCES public.elections(id) ON DELETE CASCADE,
title TEXT NOT NULL,
seats_available INTEGER NOT NULL DEFAULT 1,
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Election candidates
CREATE TABLE public.election_candidates (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
position_id UUID NOT NULL REFERENCES public.election_positions(id) ON DELETE CASCADE,
candidate_name TEXT NOT NULL,
bio TEXT,
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Eligible voters (one vote per unit)
CREATE TABLE public.election_eligible_voters (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
election_id UUID NOT NULL REFERENCES public.elections(id) ON DELETE CASCADE,
owner_id UUID NOT NULL REFERENCES public.owners(id) ON DELETE CASCADE,
unit_id UUID REFERENCES public.units(id) ON DELETE SET NULL,
has_consent BOOLEAN NOT NULL DEFAULT false,
consent_date TIMESTAMPTZ,
vote_token UUID UNIQUE DEFAULT gen_random_uuid(),
has_voted BOOLEAN NOT NULL DEFAULT false,
voted_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(election_id, unit_id)
);
-- Anonymous ballots (no link to voter identity - linked only to token)
CREATE TABLE public.election_ballots (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
election_id UUID NOT NULL REFERENCES public.elections(id) ON DELETE CASCADE,
position_id UUID NOT NULL REFERENCES public.election_positions(id) ON DELETE CASCADE,
candidate_id UUID REFERENCES public.election_candidates(id) ON DELETE SET NULL,
vote_token UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Audit log (tracks who voted but NOT how)
CREATE TABLE public.election_audit_log (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
election_id UUID NOT NULL REFERENCES public.elections(id) ON DELETE CASCADE,
voter_description TEXT NOT NULL,
action TEXT NOT NULL DEFAULT 'voted',
ip_address TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Enable RLS
ALTER TABLE public.elections ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.election_positions ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.election_candidates ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.election_eligible_voters ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.election_ballots ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.election_audit_log ENABLE ROW LEVEL SECURITY;
-- Elections: staff full access, owners can read their own association's elections
CREATE POLICY "Staff can manage elections" ON public.elections
FOR ALL TO authenticated
USING (public.has_role(auth.uid(), 'admin') OR public.has_role(auth.uid(), 'manager') OR public.has_role(auth.uid(), 'employee'));
CREATE POLICY "Owners can view their association elections" ON public.elections
FOR SELECT TO authenticated
USING (association_id IN (SELECT public.get_user_association_ids()));
-- Positions: staff full access, authenticated read
CREATE POLICY "Staff can manage positions" ON public.election_positions
FOR ALL TO authenticated
USING (public.has_role(auth.uid(), 'admin') OR public.has_role(auth.uid(), 'manager') OR public.has_role(auth.uid(), 'employee'));
CREATE POLICY "Authenticated can view positions" ON public.election_positions
FOR SELECT TO authenticated
USING (election_id IN (SELECT id FROM public.elections WHERE association_id IN (SELECT public.get_user_association_ids())));
-- Candidates: staff full access, authenticated read
CREATE POLICY "Staff can manage candidates" ON public.election_candidates
FOR ALL TO authenticated
USING (public.has_role(auth.uid(), 'admin') OR public.has_role(auth.uid(), 'manager') OR public.has_role(auth.uid(), 'employee'));
CREATE POLICY "Authenticated can view candidates" ON public.election_candidates
FOR SELECT TO authenticated
USING (position_id IN (SELECT id FROM public.election_positions WHERE election_id IN (SELECT id FROM public.elections WHERE association_id IN (SELECT public.get_user_association_ids()))));
-- Eligible voters: staff full access, voters can see their own record
CREATE POLICY "Staff can manage eligible voters" ON public.election_eligible_voters
FOR ALL TO authenticated
USING (public.has_role(auth.uid(), 'admin') OR public.has_role(auth.uid(), 'manager') OR public.has_role(auth.uid(), 'employee'));
CREATE POLICY "Owners can view their own voter record" ON public.election_eligible_voters
FOR SELECT TO authenticated
USING (owner_id IN (SELECT id FROM public.owners WHERE user_id = auth.uid()));
-- Ballots: staff can read (for tallying), voters can insert/update their own
CREATE POLICY "Staff can view ballots" ON public.election_ballots
FOR SELECT TO authenticated
USING (public.has_role(auth.uid(), 'admin') OR public.has_role(auth.uid(), 'manager'));
CREATE POLICY "Voters can insert ballots" ON public.election_ballots
FOR INSERT TO authenticated
WITH CHECK (vote_token IN (SELECT vote_token FROM public.election_eligible_voters WHERE owner_id IN (SELECT id FROM public.owners WHERE user_id = auth.uid())));
CREATE POLICY "Voters can update their ballots" ON public.election_ballots
FOR UPDATE TO authenticated
USING (vote_token IN (SELECT vote_token FROM public.election_eligible_voters WHERE owner_id IN (SELECT id FROM public.owners WHERE user_id = auth.uid())));
CREATE POLICY "Voters can delete their ballots" ON public.election_ballots
FOR DELETE TO authenticated
USING (vote_token IN (SELECT vote_token FROM public.election_eligible_voters WHERE owner_id IN (SELECT id FROM public.owners WHERE user_id = auth.uid())));
-- Audit log: staff can read, system inserts
CREATE POLICY "Staff can view audit log" ON public.election_audit_log
FOR SELECT TO authenticated
USING (public.has_role(auth.uid(), 'admin') OR public.has_role(auth.uid(), 'manager'));
CREATE POLICY "Authenticated can insert audit log" ON public.election_audit_log
FOR INSERT TO authenticated
WITH CHECK (true);
-- Indexes
CREATE INDEX idx_elections_association ON public.elections(association_id);
CREATE INDEX idx_elections_status ON public.elections(status);
CREATE INDEX idx_election_positions_election ON public.election_positions(election_id);
CREATE INDEX idx_election_candidates_position ON public.election_candidates(position_id);
CREATE INDEX idx_election_eligible_voters_election ON public.election_eligible_voters(election_id);
CREATE INDEX idx_election_eligible_voters_token ON public.election_eligible_voters(vote_token);
CREATE INDEX idx_election_ballots_election ON public.election_ballots(election_id);
CREATE INDEX idx_election_ballots_token ON public.election_ballots(vote_token);