Repository navigation
Expand file tree
/
Copy pathschema.sql
More file actions
104 lines (89 loc) · 3.65 KB
/
Copy pathschema.sql
File metadata and controls
104 lines (89 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
-- PaletteSnap reference schema
-- Run this in the Supabase SQL editor (or via psql) to set up a fresh backend.
-- The columns mirror exactly what src/store/useStore.ts reads and writes.
create extension if not exists "pgcrypto";
-- ---------------------------------------------------------------------------
-- palettes
-- ---------------------------------------------------------------------------
create table if not exists public.palettes (
id text primary key,
colors text[] not null,
tags text[] not null default '{}',
likes integer not null default 0,
is_user_created boolean not null default false,
creator_hash text,
created_at timestamptz not null default now()
);
create index if not exists palettes_created_at_idx
on public.palettes (created_at desc, id asc);
create index if not exists palettes_creator_hash_day_idx
on public.palettes (creator_hash, created_at desc);
-- ---------------------------------------------------------------------------
-- likes (one row per device per palette)
-- ---------------------------------------------------------------------------
create table if not exists public.likes (
device_id text not null,
palette_id text not null references public.palettes (id) on delete cascade,
created_at timestamptz not null default now(),
primary key (device_id, palette_id)
);
create index if not exists likes_palette_id_idx on public.likes (palette_id);
create index if not exists likes_device_id_idx on public.likes (device_id);
-- ---------------------------------------------------------------------------
-- Row level security
--
-- PaletteSnap has no accounts. The client is anonymous, so RLS here only
-- stops writes from other keys, not from other visitors. Tighten these
-- policies if you add auth later.
-- ---------------------------------------------------------------------------
alter table public.palettes enable row level security;
alter table public.likes enable row level security;
-- Weekly publish quota: at most 20 palettes per browser fingerprint per ISO
-- calendar week (Monday 00:00 UTC). SECURITY DEFINER so the INSERT policy can
-- count rows without tripping over same-table recursion. This runs in the
-- database, so calling the REST API directly cannot get around it - only a
-- brand new fingerprint can.
create or replace function public.can_create_palette(p_creator_hash text)
returns boolean
language sql
security definer
set search_path = public
as $$
select p_creator_hash is not null
and (
select count(*) < 20
from public.palettes p
where p.creator_hash = p_creator_hash
and p.created_at >= (date_trunc('week', now() at time zone 'utc') at time zone 'utc')
);
$$;
grant execute on function public.can_create_palette(text) to anon;
grant execute on function public.can_create_palette(text) to authenticated;
create policy "anyone can read palettes"
on public.palettes for select
to anon, authenticated
using (true);
create policy "anyone can publish a palette"
on public.palettes for insert
to anon, authenticated
with check (
is_user_created = true
and public.can_create_palette(creator_hash)
);
create policy "anyone can update like counts"
on public.palettes for update
to anon, authenticated
using (true)
with check (true);
create policy "anyone can read likes"
on public.likes for select
to anon, authenticated
using (true);
create policy "anyone can add a like"
on public.likes for insert
to anon, authenticated
with check (true);
create policy "anyone can remove a like"
on public.likes for delete
to anon, authenticated
using (true);