diff options
| author | Fuwn <[email protected]> | 2026-02-09 23:59:08 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-09 23:59:08 -0800 |
| commit | 7e45dd49e3b3ef4514530410489b044a9934ead0 (patch) | |
| tree | 005ef3a3fb9561a8abffbe90d0a6970a96a9828a /supabase | |
| parent | fix: P0 correctness/security fixes and P1 lint error resolution (diff) | |
| download | asa.news-7e45dd49e3b3ef4514530410489b044a9934ead0.tar.xz asa.news-7e45dd49e3b3ef4514530410489b044a9934ead0.zip | |
fix: resolve Supabase security and performance advisories
Enable RLS on rate_limits table (accessed only via SECURITY DEFINER).
Fix api_keys delete policy to use (select auth.uid()) subquery to
avoid per-row re-evaluation. Refresh schema dump from live database.
Diffstat (limited to 'supabase')
| -rw-r--r-- | supabase/schema.sql | 4351 |
1 files changed, 3396 insertions, 955 deletions
diff --git a/supabase/schema.sql b/supabase/schema.sql index fb5f3ab..f75fe9e 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -1,488 +1,204 @@ --- asa.news database schema --- dumped 2026-02-08 from live supabase instance (kbugptrwjnenmgkhdofn) --- this file is for reference only — migrations are applied via supabase mcp - --- ============================================================================= --- extensions --- ============================================================================= - -CREATE EXTENSION IF NOT EXISTS pg_trgm; -CREATE EXTENSION IF NOT EXISTS moddatetime SCHEMA extensions; -CREATE EXTENSION IF NOT EXISTS pgmq; -CREATE EXTENSION IF NOT EXISTS pg_cron; -CREATE EXTENSION IF NOT EXISTS pgsodium; - --- ============================================================================= --- custom types --- ============================================================================= - -CREATE TYPE public.feed_visibility AS ENUM ('public', 'authenticated'); -CREATE TYPE public.subscription_tier AS ENUM ('free', 'pro', 'developer'); - --- ============================================================================= --- tables --- ============================================================================= - -CREATE TABLE public.feeds ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - url text NOT NULL, - site_url text, - title text, - description text, - language text, - feed_type text, - image_url text, - visibility feed_visibility NOT NULL DEFAULT 'public'::feed_visibility, - etag text, - last_modified text, - last_fetched_at timestamp with time zone, - last_fetch_error text, - last_fetch_error_at timestamp with time zone, - consecutive_failures integer NOT NULL DEFAULT 0, - next_fetch_at timestamp with time zone NOT NULL DEFAULT now(), - fetch_interval_seconds integer NOT NULL DEFAULT 1800, - subscriber_count integer NOT NULL DEFAULT 0, - created_at timestamp with time zone NOT NULL DEFAULT now(), - updated_at timestamp with time zone NOT NULL DEFAULT now() -); +-- +-- PostgreSQL database dump +-- -CREATE TABLE public.user_profiles ( - id uuid NOT NULL, - display_name text, - tier subscription_tier NOT NULL DEFAULT 'free'::subscription_tier, - feed_count integer NOT NULL DEFAULT 0, - folder_count integer NOT NULL DEFAULT 0, - muted_keyword_count integer NOT NULL DEFAULT 0, - custom_feed_count integer NOT NULL DEFAULT 0, - created_at timestamp with time zone NOT NULL DEFAULT now(), - updated_at timestamp with time zone NOT NULL DEFAULT now(), - stripe_customer_identifier text, - stripe_subscription_identifier text, - stripe_subscription_status text, - stripe_current_period_end timestamp with time zone, - highlight_count integer NOT NULL DEFAULT 0, - webhook_url text, - webhook_secret text, - webhook_enabled boolean NOT NULL DEFAULT false, - webhook_consecutive_failures integer NOT NULL DEFAULT 0 -); +-- \restrict 3Un8h9VlAyPuVfPugYV7LM7XGnkxI3KC1nPU1aarC0scGxlDzBQB81bgO7tvPsv -CREATE TABLE public.folders ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - name text NOT NULL, - position integer NOT NULL DEFAULT 0, - created_at timestamp with time zone NOT NULL DEFAULT now(), - updated_at timestamp with time zone NOT NULL DEFAULT now(), - icon_url text -); +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 -CREATE TABLE public.subscriptions ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - feed_id uuid NOT NULL, - folder_id uuid, - custom_title text, - position integer NOT NULL DEFAULT 0, - vault_secret_id uuid, - hidden_from_timeline boolean NOT NULL DEFAULT false, - created_at timestamp with time zone NOT NULL DEFAULT now(), - updated_at timestamp with time zone NOT NULL DEFAULT now() -); +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +-- SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; -CREATE TABLE public.entries ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - feed_id uuid NOT NULL, - owner_id uuid, - guid text NOT NULL, - url text, - title text, - author text, - summary text, - content_html text, - content_text text, - image_url text, - published_at timestamp with time zone, - word_count integer, - fetched_at timestamp with time zone NOT NULL DEFAULT now(), - created_at timestamp with time zone NOT NULL DEFAULT now(), - enclosure_url text, - enclosure_type text, - enclosure_length bigint -); +-- +-- Name: pg_cron; Type: EXTENSION; Schema: -; Owner: - +-- -CREATE TABLE public.user_entry_states ( - user_id uuid NOT NULL, - entry_id uuid NOT NULL, - read boolean NOT NULL DEFAULT false, - saved boolean NOT NULL DEFAULT false, - read_at timestamp with time zone, - saved_at timestamp with time zone -); +CREATE EXTENSION IF NOT EXISTS "pg_cron" WITH SCHEMA "pg_catalog"; -CREATE TABLE public.muted_keywords ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - keyword text NOT NULL, - created_at timestamp with time zone NOT NULL DEFAULT now() -); -CREATE TABLE public.custom_feeds ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - name text NOT NULL, - query text NOT NULL, - position integer NOT NULL DEFAULT 0, - created_at timestamp with time zone NOT NULL DEFAULT now(), - updated_at timestamp with time zone NOT NULL DEFAULT now(), - source_folder_id uuid, - match_mode text NOT NULL DEFAULT 'or'::text, - icon_url text -); +-- +-- Name: EXTENSION "pg_cron"; Type: COMMENT; Schema: -; Owner: +-- -CREATE TABLE public.shared_entries ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - entry_id uuid NOT NULL, - share_token text NOT NULL, - created_at timestamp with time zone NOT NULL DEFAULT now(), - expires_at timestamp with time zone, - note text, - highlighted_text text, - highlight_text_offset integer, - highlight_text_length integer, - highlight_text_prefix text DEFAULT ''::text, - highlight_text_suffix text DEFAULT ''::text -); +-- COMMENT ON EXTENSION "pg_cron" IS 'Job scheduler for PostgreSQL'; -CREATE TABLE public.user_highlights ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - entry_id uuid NOT NULL, - highlighted_text text NOT NULL, - note text, - text_offset integer NOT NULL, - text_length integer NOT NULL, - text_prefix text NOT NULL DEFAULT ''::text, - text_suffix text NOT NULL DEFAULT ''::text, - color text NOT NULL DEFAULT 'yellow'::text, - created_at timestamp with time zone NOT NULL DEFAULT now() -); -CREATE TABLE public.api_keys ( - id uuid NOT NULL DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - key_hash text NOT NULL, - key_prefix character(8) NOT NULL, - label text, - created_at timestamp with time zone NOT NULL DEFAULT now(), - last_used_at timestamp with time zone, - revoked_at timestamp with time zone -); +-- +-- Name: pgsodium; Type: EXTENSION; Schema: -; Owner: - +-- --- ============================================================================= --- primary keys --- ============================================================================= - -ALTER TABLE public.feeds ADD CONSTRAINT feeds_pkey PRIMARY KEY (id); -ALTER TABLE public.user_profiles ADD CONSTRAINT user_profiles_pkey PRIMARY KEY (id); -ALTER TABLE public.folders ADD CONSTRAINT folders_pkey PRIMARY KEY (id); -ALTER TABLE public.subscriptions ADD CONSTRAINT subscriptions_pkey PRIMARY KEY (id); -ALTER TABLE public.entries ADD CONSTRAINT entries_pkey PRIMARY KEY (id); -ALTER TABLE public.user_entry_states ADD CONSTRAINT user_entry_states_pkey PRIMARY KEY (user_id, entry_id); -ALTER TABLE public.muted_keywords ADD CONSTRAINT muted_keywords_pkey PRIMARY KEY (id); -ALTER TABLE public.custom_feeds ADD CONSTRAINT custom_feeds_pkey PRIMARY KEY (id); -ALTER TABLE public.shared_entries ADD CONSTRAINT shared_entries_pkey PRIMARY KEY (id); -ALTER TABLE public.user_highlights ADD CONSTRAINT user_highlights_pkey PRIMARY KEY (id); -ALTER TABLE public.api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id); - --- ============================================================================= --- unique constraints --- ============================================================================= - -ALTER TABLE public.api_keys ADD CONSTRAINT api_keys_key_hash_key UNIQUE (key_hash); -ALTER TABLE public.folders ADD CONSTRAINT folders_user_id_name_key UNIQUE (user_id, name); -ALTER TABLE public.shared_entries ADD CONSTRAINT shared_entries_share_token_key UNIQUE (share_token); -ALTER TABLE public.shared_entries ADD CONSTRAINT shared_entries_user_id_entry_id_key UNIQUE (user_id, entry_id); -ALTER TABLE public.subscriptions ADD CONSTRAINT subscriptions_user_id_feed_id_key UNIQUE (user_id, feed_id); -ALTER TABLE public.user_profiles ADD CONSTRAINT user_profiles_stripe_customer_identifier_key UNIQUE (stripe_customer_identifier); - --- ============================================================================= --- foreign keys --- ============================================================================= - -ALTER TABLE public.api_keys ADD CONSTRAINT api_keys_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.custom_feeds ADD CONSTRAINT custom_feeds_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.custom_feeds ADD CONSTRAINT custom_feeds_source_folder_id_fkey FOREIGN KEY (source_folder_id) REFERENCES public.folders(id); -ALTER TABLE public.entries ADD CONSTRAINT entries_feed_id_fkey FOREIGN KEY (feed_id) REFERENCES public.feeds(id); -ALTER TABLE public.entries ADD CONSTRAINT entries_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); -ALTER TABLE public.folders ADD CONSTRAINT folders_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.muted_keywords ADD CONSTRAINT muted_keywords_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.shared_entries ADD CONSTRAINT shared_entries_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.shared_entries ADD CONSTRAINT shared_entries_entry_id_fkey FOREIGN KEY (entry_id) REFERENCES public.entries(id); -ALTER TABLE public.subscriptions ADD CONSTRAINT subscriptions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.subscriptions ADD CONSTRAINT subscriptions_feed_id_fkey FOREIGN KEY (feed_id) REFERENCES public.feeds(id); -ALTER TABLE public.subscriptions ADD CONSTRAINT subscriptions_folder_id_fkey FOREIGN KEY (folder_id) REFERENCES public.folders(id); -ALTER TABLE public.user_entry_states ADD CONSTRAINT user_entry_states_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.user_entry_states ADD CONSTRAINT user_entry_states_entry_id_fkey FOREIGN KEY (entry_id) REFERENCES public.entries(id); -ALTER TABLE public.user_highlights ADD CONSTRAINT user_highlights_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id); -ALTER TABLE public.user_highlights ADD CONSTRAINT user_highlights_entry_id_fkey FOREIGN KEY (entry_id) REFERENCES public.entries(id); -ALTER TABLE public.user_profiles ADD CONSTRAINT user_profiles_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id); - --- ============================================================================= --- indexes --- ============================================================================= - --- feeds -CREATE UNIQUE INDEX idx_feeds_url_public ON public.feeds USING btree (url) WHERE (visibility = 'public'::feed_visibility); -CREATE INDEX idx_feeds_next_fetch ON public.feeds USING btree (next_fetch_at) WHERE (consecutive_failures < 10); - --- entries -CREATE UNIQUE INDEX idx_entries_dedup_public ON public.entries USING btree (feed_id, guid) WHERE (owner_id IS NULL); -CREATE UNIQUE INDEX idx_entries_dedup_authenticated ON public.entries USING btree (feed_id, owner_id, guid) WHERE (owner_id IS NOT NULL); -CREATE INDEX idx_entries_feed_published ON public.entries USING btree (feed_id, published_at DESC NULLS LAST); -CREATE INDEX idx_entries_published ON public.entries USING btree (published_at DESC NULLS LAST); -CREATE INDEX idx_entries_owner ON public.entries USING btree (owner_id, published_at DESC NULLS LAST) WHERE (owner_id IS NOT NULL); -CREATE INDEX idx_entries_title_trigram ON public.entries USING gin (title gin_trgm_ops); -CREATE INDEX idx_entries_content_trigram ON public.entries USING gin (content_text gin_trgm_ops); - --- subscriptions -CREATE INDEX idx_subscriptions_user ON public.subscriptions USING btree (user_id); -CREATE INDEX idx_subscriptions_feed ON public.subscriptions USING btree (feed_id); -CREATE INDEX idx_subscriptions_folder ON public.subscriptions USING btree (folder_id); - --- user_entry_states -CREATE INDEX idx_user_entry_states_entry ON public.user_entry_states USING btree (entry_id); -CREATE INDEX idx_user_entry_states_read ON public.user_entry_states USING btree (user_id, entry_id) WHERE (read = true); -CREATE INDEX idx_user_entry_states_saved ON public.user_entry_states USING btree (user_id, saved_at DESC) WHERE (saved = true); - --- folders -CREATE INDEX idx_folders_user ON public.folders USING btree (user_id); - --- muted_keywords -CREATE INDEX idx_muted_keywords_user ON public.muted_keywords USING btree (user_id); -CREATE UNIQUE INDEX idx_muted_keywords_user_keyword ON public.muted_keywords USING btree (user_id, lower(keyword)); - --- custom_feeds -CREATE INDEX idx_custom_feeds_user ON public.custom_feeds USING btree (user_id); -CREATE INDEX idx_custom_feeds_source_folder ON public.custom_feeds USING btree (source_folder_id); - --- shared_entries -CREATE INDEX idx_shared_entries_entry ON public.shared_entries USING btree (entry_id); - --- user_highlights -CREATE INDEX user_highlights_user_entry_idx ON public.user_highlights USING btree (user_id, entry_id); -CREATE INDEX user_highlights_user_created_idx ON public.user_highlights USING btree (user_id, created_at DESC); -CREATE INDEX idx_user_highlights_entry ON public.user_highlights USING btree (entry_id); - --- api_keys -CREATE INDEX idx_api_keys_user_id ON public.api_keys USING btree (user_id); -CREATE INDEX idx_api_keys_key_hash ON public.api_keys USING btree (key_hash) WHERE (revoked_at IS NULL); - --- user_profiles -CREATE INDEX idx_user_profiles_stripe_customer ON public.user_profiles USING btree (stripe_customer_identifier) WHERE (stripe_customer_identifier IS NOT NULL); - --- ============================================================================= --- row level security --- ============================================================================= - -ALTER TABLE public.api_keys ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.custom_feeds ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.entries ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.feeds ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.folders ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.muted_keywords ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.shared_entries ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.user_entry_states ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.user_highlights ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.user_profiles ENABLE ROW LEVEL SECURITY; - --- ============================================================================= --- rls policies --- ============================================================================= - --- api_keys -CREATE POLICY "Users can view own API keys" ON public.api_keys AS PERMISSIVE FOR SELECT USING ((auth.uid() = user_id)); -CREATE POLICY "Users can insert own API keys" ON public.api_keys AS PERMISSIVE FOR INSERT WITH CHECK ((auth.uid() = user_id)); -CREATE POLICY "Users can update own API keys" ON public.api_keys AS PERMISSIVE FOR UPDATE USING ((auth.uid() = user_id)); -CREATE POLICY "Users can delete own API keys" ON public.api_keys AS PERMISSIVE FOR DELETE USING ((auth.uid() = user_id)); - --- custom_feeds -CREATE POLICY "users can view own custom feeds" ON public.custom_feeds AS PERMISSIVE FOR SELECT USING ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can insert own custom feeds" ON public.custom_feeds AS PERMISSIVE FOR INSERT WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can update own custom feeds" ON public.custom_feeds AS PERMISSIVE FOR UPDATE USING ((user_id = (SELECT auth.uid()))) WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can delete own custom feeds" ON public.custom_feeds AS PERMISSIVE FOR DELETE USING ((user_id = (SELECT auth.uid()))); - --- entries -CREATE POLICY "users can view entries from subscribed public feeds" ON public.entries AS PERMISSIVE FOR SELECT - USING (( - (owner_id IS NULL AND EXISTS (SELECT 1 FROM subscriptions s WHERE s.feed_id = entries.feed_id AND s.user_id = (SELECT auth.uid()))) - OR (owner_id = (SELECT auth.uid())) - )); - --- feeds -CREATE POLICY "users can view accessible feeds" ON public.feeds AS PERMISSIVE FOR SELECT - USING (( - (visibility = 'public'::feed_visibility AND (SELECT auth.role()) = 'authenticated') - OR (visibility = 'authenticated'::feed_visibility AND EXISTS (SELECT 1 FROM subscriptions s WHERE s.feed_id = feeds.id AND s.user_id = (SELECT auth.uid()))) - )); - --- folders -CREATE POLICY "users can view own folders" ON public.folders AS PERMISSIVE FOR SELECT USING ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can insert own folders" ON public.folders AS PERMISSIVE FOR INSERT WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can update own folders" ON public.folders AS PERMISSIVE FOR UPDATE USING ((user_id = (SELECT auth.uid()))) WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can delete own folders" ON public.folders AS PERMISSIVE FOR DELETE USING ((user_id = (SELECT auth.uid()))); - --- muted_keywords -CREATE POLICY "users can view own muted keywords" ON public.muted_keywords AS PERMISSIVE FOR SELECT USING ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can insert own muted keywords" ON public.muted_keywords AS PERMISSIVE FOR INSERT WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can delete own muted keywords" ON public.muted_keywords AS PERMISSIVE FOR DELETE USING ((user_id = (SELECT auth.uid()))); - --- shared_entries -CREATE POLICY "authenticated users can view own shared entries" ON public.shared_entries AS PERMISSIVE FOR SELECT TO authenticated USING ((user_id = (SELECT auth.uid()))); -CREATE POLICY "owners can insert own shared entries" ON public.shared_entries AS PERMISSIVE FOR INSERT WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "owners can update own shared entries" ON public.shared_entries AS PERMISSIVE FOR UPDATE USING ((user_id = (SELECT auth.uid()))) WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "owners can delete own shared entries" ON public.shared_entries AS PERMISSIVE FOR DELETE USING ((user_id = (SELECT auth.uid()))); - --- subscriptions -CREATE POLICY "users can view own subscriptions" ON public.subscriptions AS PERMISSIVE FOR SELECT USING ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can insert own subscriptions" ON public.subscriptions AS PERMISSIVE FOR INSERT WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can update own subscriptions" ON public.subscriptions AS PERMISSIVE FOR UPDATE USING ((user_id = (SELECT auth.uid()))) WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can delete own subscriptions" ON public.subscriptions AS PERMISSIVE FOR DELETE USING ((user_id = (SELECT auth.uid()))); - --- user_entry_states -CREATE POLICY "users can manage own entry states" ON public.user_entry_states AS PERMISSIVE FOR ALL USING ((user_id = (SELECT auth.uid()))) WITH CHECK ((user_id = (SELECT auth.uid()))); - --- user_highlights -CREATE POLICY "users can view own highlights" ON public.user_highlights AS PERMISSIVE FOR SELECT USING ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can insert own highlights" ON public.user_highlights AS PERMISSIVE FOR INSERT WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can update own highlights" ON public.user_highlights AS PERMISSIVE FOR UPDATE USING ((user_id = (SELECT auth.uid()))) WITH CHECK ((user_id = (SELECT auth.uid()))); -CREATE POLICY "users can delete own highlights" ON public.user_highlights AS PERMISSIVE FOR DELETE USING ((user_id = (SELECT auth.uid()))); - --- user_profiles -CREATE POLICY "users can view own profile" ON public.user_profiles AS PERMISSIVE FOR SELECT USING ((id = (SELECT auth.uid()))); -CREATE POLICY "users can update own profile" ON public.user_profiles AS PERMISSIVE FOR UPDATE USING ((id = (SELECT auth.uid()))) WITH CHECK ((id = (SELECT auth.uid()))); - --- ============================================================================= --- functions --- ============================================================================= - -CREATE OR REPLACE FUNCTION public.handle_new_user() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -begin - insert into public.user_profiles (id, display_name) - values (new.id, coalesce(new.raw_user_meta_data ->> 'display_name', split_part(new.email, '@', 1))); +CREATE EXTENSION IF NOT EXISTS "pgsodium"; - return new; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.subscribe_to_feed(feed_url text, target_folder_id uuid DEFAULT NULL::uuid, feed_custom_title text DEFAULT NULL::text, feed_credential text DEFAULT NULL::text, feed_auth_type text DEFAULT 'bearer'::text) - RETURNS uuid - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -declare - resolved_feed_id uuid; - new_subscription_id uuid; - resolved_vault_id uuid; - resolved_visibility public.feed_visibility; - existing_subscription_id uuid; -begin - if feed_credential is not null and feed_auth_type not in ('bearer', 'basic', 'query_param') then - raise exception 'Invalid authentication type: %. Must be bearer, basic, or query_param', feed_auth_type; - end if; - if feed_credential is not null then - resolved_visibility := 'authenticated'; - else - resolved_visibility := 'public'; - end if; +-- +-- Name: EXTENSION "pgsodium"; Type: COMMENT; Schema: -; Owner: +-- - if resolved_visibility = 'public' then - select id into resolved_feed_id - from public.feeds - where url = feed_url and visibility = 'public'; +-- COMMENT ON EXTENSION "pgsodium" IS 'Pgsodium is a modern cryptography library for Postgres.'; - if resolved_feed_id is null then - insert into public.feeds (url, visibility) - values (feed_url, 'public') - returning id into resolved_feed_id; - end if; - select id into existing_subscription_id - from public.subscriptions - where user_id = auth.uid() and feed_id = resolved_feed_id; +-- +-- Name: SCHEMA "public"; Type: COMMENT; Schema: -; Owner: pg_database_owner +-- - if existing_subscription_id is not null then - raise exception 'You are already subscribed to this feed'; - end if; - else - insert into public.feeds (url, visibility, fetch_interval_seconds) - values (feed_url, 'authenticated', 300) - returning id into resolved_feed_id; +COMMENT ON SCHEMA "public" IS 'standard public schema'; - resolved_vault_id := vault.create_secret( - jsonb_build_object( - 'authenticationType', feed_auth_type, - 'authenticationValue', feed_credential - )::text, - 'feed_credential_' || resolved_feed_id::text, - 'Credential for authenticated feed' - ); - end if; - insert into public.subscriptions (user_id, feed_id, folder_id, custom_title, vault_secret_id) - values (auth.uid(), resolved_feed_id, target_folder_id, feed_custom_title, resolved_vault_id) - returning id into new_subscription_id; +-- +-- Name: moddatetime; Type: EXTENSION; Schema: -; Owner: - +-- - return new_subscription_id; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.update_feed_credentials(p_feed_id uuid, p_auth_type text, p_credential text) - RETURNS void - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -declare - v_vault_secret_id uuid; -begin - if p_auth_type not in ('bearer', 'basic', 'query_param') then - raise exception 'Invalid authentication type: %. Must be bearer, basic, or query_param', p_auth_type; - end if; +CREATE EXTENSION IF NOT EXISTS "moddatetime" WITH SCHEMA "extensions"; - select s.vault_secret_id into v_vault_secret_id - from public.subscriptions s - where s.feed_id = p_feed_id and s.user_id = auth.uid(); - if v_vault_secret_id is null then - raise exception 'No authenticated subscription found for this feed'; - end if; +-- +-- Name: EXTENSION "moddatetime"; Type: COMMENT; Schema: -; Owner: +-- - perform vault.update_secret( - v_vault_secret_id, - jsonb_build_object( - 'authenticationType', p_auth_type, - 'authenticationValue', p_credential - )::text - ); -end; -$function$; - -CREATE OR REPLACE FUNCTION public.add_feed_credentials(p_subscription_id uuid, p_auth_type text, p_credential text) - RETURNS void - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ +-- COMMENT ON EXTENSION "moddatetime" IS 'functions for tracking last modification time'; + + +-- +-- Name: pg_graphql; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "pg_graphql" WITH SCHEMA "graphql"; + + +-- +-- Name: EXTENSION "pg_graphql"; Type: COMMENT; Schema: -; Owner: +-- + +-- COMMENT ON EXTENSION "pg_graphql" IS 'pg_graphql: GraphQL support'; + + +-- +-- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" WITH SCHEMA "extensions"; + + +-- +-- Name: EXTENSION "pg_stat_statements"; Type: COMMENT; Schema: -; Owner: +-- + +-- COMMENT ON EXTENSION "pg_stat_statements" IS 'track planning and execution statistics of all SQL statements executed'; + + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "pg_trgm" WITH SCHEMA "extensions"; + + +-- +-- Name: EXTENSION "pg_trgm"; Type: COMMENT; Schema: -; Owner: +-- + +-- COMMENT ON EXTENSION "pg_trgm" IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA "extensions"; + + +-- +-- Name: EXTENSION "pgcrypto"; Type: COMMENT; Schema: -; Owner: +-- + +-- COMMENT ON EXTENSION "pgcrypto" IS 'cryptographic functions'; + + +-- +-- Name: pgmq; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "pgmq"; + + +-- +-- Name: EXTENSION "pgmq"; Type: COMMENT; Schema: -; Owner: +-- + +-- COMMENT ON EXTENSION "pgmq" IS 'A lightweight message queue. Like AWS SQS and RSMQ but on Postgres.'; + + +-- +-- Name: supabase_vault; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "supabase_vault" WITH SCHEMA "vault"; + + +-- +-- Name: EXTENSION "supabase_vault"; Type: COMMENT; Schema: -; Owner: +-- + +-- COMMENT ON EXTENSION "supabase_vault" IS 'Supabase Vault Extension'; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA "extensions"; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: +-- + +-- COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: feed_visibility; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE "public"."feed_visibility" AS ENUM ( + 'public', + 'authenticated' +); + + +ALTER TYPE "public"."feed_visibility" OWNER TO "postgres"; + +-- +-- Name: subscription_tier; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE "public"."subscription_tier" AS ENUM ( + 'free', + 'pro', + 'developer' +); + + +ALTER TYPE "public"."subscription_tier" OWNER TO "postgres"; + +-- +-- Name: add_feed_credentials("uuid", "text", "text"); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."add_feed_credentials"("p_subscription_id" "uuid", "p_auth_type" "text", "p_credential" "text") RETURNS "void" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ declare v_old_feed_id uuid; v_feed_url text; @@ -530,83 +246,374 @@ begin set subscriber_count = greatest(subscriber_count - 1, 0) where id = v_old_feed_id; end; -$function$; - -CREATE OR REPLACE FUNCTION public.update_feed_url(target_feed_id uuid, new_url text) - RETURNS void - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ +$$; + + +ALTER FUNCTION "public"."add_feed_credentials"("p_subscription_id" "uuid", "p_auth_type" "text", "p_credential" "text") OWNER TO "postgres"; + +-- +-- Name: check_custom_feed_limit(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."check_custom_feed_limit"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +declare + current_tier public.subscription_tier; + current_count int; + maximum_allowed int; +begin + select tier, custom_feed_count into current_tier, current_count + from public.user_profiles where id = new.user_id; + + maximum_allowed := case current_tier when 'free' then 1 when 'pro' then 1000 when 'developer' then 1000 else 1 end; + + if current_count >= maximum_allowed then + raise exception 'You have reached the maximum number of custom feeds for your plan (% on % tier)', maximum_allowed, current_tier; + end if; + + update public.user_profiles set custom_feed_count = custom_feed_count + 1 where id = new.user_id; + + return new; +end; +$$; + + +ALTER FUNCTION "public"."check_custom_feed_limit"() OWNER TO "postgres"; + +-- +-- Name: check_folder_limit(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."check_folder_limit"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +declare + current_tier public.subscription_tier; + current_count int; + maximum_allowed int; +begin + select tier, folder_count into current_tier, current_count + from public.user_profiles where id = new.user_id; + + maximum_allowed := case current_tier when 'free' then 3 when 'pro' then 10000 when 'developer' then 10000 else 3 end; + + if current_count >= maximum_allowed then + raise exception 'You have reached the maximum number of folders for your plan (% on % tier)', maximum_allowed, current_tier; + end if; + + update public.user_profiles set folder_count = folder_count + 1 where id = new.user_id; + + return new; +end; +$$; + + +ALTER FUNCTION "public"."check_folder_limit"() OWNER TO "postgres"; + +-- +-- Name: check_highlight_limit(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."check_highlight_limit"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +declare + current_tier public.subscription_tier; + current_count int; + maximum_allowed int; +begin + select tier, highlight_count into current_tier, current_count + from public.user_profiles where id = new.user_id; + + maximum_allowed := case current_tier when 'free' then 50 when 'pro' then 100000 when 'developer' then 100000 else 50 end; + + if current_count >= maximum_allowed then + raise exception 'You have reached the maximum number of highlights for your plan (% on % tier)', maximum_allowed, current_tier; + end if; + + update public.user_profiles set highlight_count = highlight_count + 1 where id = new.user_id; + + return new; +end; +$$; + + +ALTER FUNCTION "public"."check_highlight_limit"() OWNER TO "postgres"; + +-- +-- Name: check_muted_keyword_limit(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."check_muted_keyword_limit"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +declare + current_tier public.subscription_tier; + current_count int; + maximum_allowed int; +begin + select tier, muted_keyword_count into current_tier, current_count + from public.user_profiles where id = new.user_id; + + maximum_allowed := case current_tier when 'free' then 5 when 'pro' then 10000 when 'developer' then 10000 else 5 end; + + if current_count >= maximum_allowed then + raise exception 'You have reached the maximum number of muted keywords for your plan (% on % tier)', maximum_allowed, current_tier; + end if; + + update public.user_profiles set muted_keyword_count = muted_keyword_count + 1 where id = new.user_id; + + return new; +end; +$$; + + +ALTER FUNCTION "public"."check_muted_keyword_limit"() OWNER TO "postgres"; + +-- +-- Name: check_rate_limit("text", integer, integer); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."check_rate_limit"("p_identifier" "text", "p_limit" integer, "p_window_seconds" integer) RETURNS "jsonb" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO 'public' + AS $$ +DECLARE + v_window_start timestamptz; + v_current_count integer; BEGIN - IF NOT EXISTS ( - SELECT 1 FROM public.subscriptions - WHERE feed_id = target_feed_id - AND user_id = auth.uid() - ) THEN - RAISE EXCEPTION 'not subscribed to this feed'; - END IF; + v_window_start := to_timestamp( + floor(extract(epoch from now()) / p_window_seconds) * p_window_seconds + ); - UPDATE public.feeds SET url = new_url WHERE id = target_feed_id; + INSERT INTO public.rate_limits (identifier, window_start, request_count) + VALUES (p_identifier, v_window_start, 1) + ON CONFLICT (identifier, window_start) + DO UPDATE SET request_count = rate_limits.request_count + 1 + RETURNING request_count INTO v_current_count; + + RETURN jsonb_build_object( + 'success', v_current_count <= p_limit, + 'remaining', greatest(p_limit - v_current_count, 0) + ); END; -$function$; - -CREATE OR REPLACE FUNCTION public.get_timeline(target_folder_id uuid DEFAULT NULL::uuid, target_feed_id uuid DEFAULT NULL::uuid, result_limit integer DEFAULT 50, pagination_cursor timestamp with time zone DEFAULT NULL::timestamp with time zone, unread_only boolean DEFAULT false) - RETURNS TABLE(entry_id uuid, feed_id uuid, feed_title text, custom_title text, entry_title text, entry_url text, author text, summary text, image_url text, published_at timestamp with time zone, is_read boolean, is_saved boolean, enclosure_url text, enclosure_type text) - LANGUAGE sql - STABLE SECURITY DEFINER - SET search_path TO '' -AS $function$ - SELECT - e.id AS entry_id, - e.feed_id, - f.title AS feed_title, - s.custom_title, - e.title AS entry_title, - e.url AS entry_url, - e.author, - e.summary, - e.image_url, - e.published_at, - COALESCE(ues.read, false) AS is_read, - COALESCE(ues.saved, false) AS is_saved, - e.enclosure_url, - e.enclosure_type - FROM public.entries e - INNER JOIN public.subscriptions s - ON s.feed_id = e.feed_id AND s.user_id = auth.uid() - INNER JOIN public.feeds f ON f.id = e.feed_id - LEFT JOIN public.user_entry_states ues - ON ues.entry_id = e.id AND ues.user_id = auth.uid() - WHERE - (e.owner_id IS NULL OR e.owner_id = auth.uid()) - AND (target_folder_id IS NULL OR s.folder_id = target_folder_id) - AND (target_feed_id IS NULL OR e.feed_id = target_feed_id) - AND (target_feed_id IS NOT NULL OR s.hidden_from_timeline = false) - AND (pagination_cursor IS NULL OR e.published_at < pagination_cursor) - AND (NOT unread_only OR COALESCE(ues.read, false) = false) - AND NOT EXISTS ( - SELECT 1 FROM public.muted_keywords mk - WHERE mk.user_id = auth.uid() - AND ( - e.title ILIKE '%' || mk.keyword || '%' - OR e.summary ILIKE '%' || mk.keyword || '%' - ) - ) - AND ( - (SELECT tier FROM public.user_profiles WHERE id = auth.uid()) = 'pro' - OR e.published_at >= now() - interval '14 days' - ) - ORDER BY e.published_at DESC NULLS LAST - LIMIT result_limit; -$function$; - -CREATE OR REPLACE FUNCTION public.get_custom_feed(target_custom_feed_id uuid, result_limit integer DEFAULT 50, pagination_cursor timestamp with time zone DEFAULT NULL::timestamp with time zone) - RETURNS TABLE(entry_id uuid, feed_id uuid, feed_title text, entry_title text, entry_url text, author text, summary text, published_at timestamp with time zone, is_read boolean, is_saved boolean) - LANGUAGE sql - STABLE SECURITY DEFINER - SET search_path TO '' -AS $function$ +$$; + + +ALTER FUNCTION "public"."check_rate_limit"("p_identifier" "text", "p_limit" integer, "p_window_seconds" integer) OWNER TO "postgres"; + +-- +-- Name: check_subscription_limit(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."check_subscription_limit"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +declare + current_tier public.subscription_tier; + current_count int; + maximum_allowed int; + feed_visibility public.feed_visibility; +begin + select tier, feed_count into current_tier, current_count + from public.user_profiles where id = new.user_id; + + maximum_allowed := case current_tier when 'free' then 10 when 'pro' then 200 when 'developer' then 500 else 10 end; + + if current_count >= maximum_allowed then + raise exception 'You have reached the maximum number of feeds for your plan (% on % tier)', maximum_allowed, current_tier; + end if; + + select visibility into feed_visibility from public.feeds where id = new.feed_id; + + if current_tier = 'free' and feed_visibility = 'authenticated' then + raise exception 'Authenticated feeds require a Pro subscription'; + end if; + + update public.user_profiles set feed_count = feed_count + 1 where id = new.user_id; + + update public.feeds + set subscriber_count = subscriber_count + 1 + where id = new.feed_id; + + return new; +end; +$$; + + +ALTER FUNCTION "public"."check_subscription_limit"() OWNER TO "postgres"; + +-- +-- Name: cleanup_stale_entries(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."cleanup_stale_entries"() RETURNS integer + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO 'public' + AS $$ +DECLARE + deleted_count integer; +BEGIN + DELETE FROM public.entries + WHERE published_at < NOW() - INTERVAL '90 days' + AND id NOT IN ( + SELECT entry_id FROM public.user_entry_states WHERE saved = true + ); + GET DIAGNOSTICS deleted_count = ROW_COUNT; + RETURN deleted_count; +END; +$$; + + +ALTER FUNCTION "public"."cleanup_stale_entries"() OWNER TO "postgres"; + +-- +-- Name: decrement_custom_feed_count(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."decrement_custom_feed_count"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +begin + update public.user_profiles + set custom_feed_count = greatest(custom_feed_count - 1, 0) + where id = old.user_id; + + return old; +end; +$$; + + +ALTER FUNCTION "public"."decrement_custom_feed_count"() OWNER TO "postgres"; + +-- +-- Name: decrement_folder_count(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."decrement_folder_count"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +begin + update public.user_profiles + set folder_count = greatest(folder_count - 1, 0) + where id = old.user_id; + + return old; +end; +$$; + + +ALTER FUNCTION "public"."decrement_folder_count"() OWNER TO "postgres"; + +-- +-- Name: decrement_highlight_count(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."decrement_highlight_count"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +begin + update public.user_profiles + set highlight_count = greatest(highlight_count - 1, 0) + where id = old.user_id; + + return old; +end; +$$; + + +ALTER FUNCTION "public"."decrement_highlight_count"() OWNER TO "postgres"; + +-- +-- Name: decrement_muted_keyword_count(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."decrement_muted_keyword_count"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +begin + update public.user_profiles + set muted_keyword_count = greatest(muted_keyword_count - 1, 0) + where id = old.user_id; + + return old; +end; +$$; + + +ALTER FUNCTION "public"."decrement_muted_keyword_count"() OWNER TO "postgres"; + +-- +-- Name: decrement_subscription_count(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."decrement_subscription_count"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +declare + feed_vis public.feed_visibility; +begin + update public.user_profiles + set feed_count = greatest(feed_count - 1, 0) + where id = old.user_id; + + update public.feeds + set subscriber_count = greatest(subscriber_count - 1, 0) + where id = old.feed_id; + + select visibility into feed_vis from public.feeds where id = old.feed_id; + + if feed_vis = 'authenticated' then + delete from public.entries where feed_id = old.feed_id and owner_id = old.user_id; + if old.vault_secret_id is not null then + delete from vault.secrets where id = old.vault_secret_id; + end if; + delete from public.feeds where id = old.feed_id; + end if; + + return old; +end; +$$; + + +ALTER FUNCTION "public"."decrement_subscription_count"() OWNER TO "postgres"; + +-- +-- Name: enqueue_feed_refresh_on_subscribe(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."enqueue_feed_refresh_on_subscribe"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +BEGIN + PERFORM pgmq.send('feed_refresh', jsonb_build_object('feed_id', NEW.feed_id)); + RETURN NEW; +END; +$$; + + +ALTER FUNCTION "public"."enqueue_feed_refresh_on_subscribe"() OWNER TO "postgres"; + +-- +-- Name: get_custom_feed("uuid", integer, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."get_custom_feed"("target_custom_feed_id" "uuid", "result_limit" integer DEFAULT 50, "pagination_cursor" timestamp with time zone DEFAULT NULL::timestamp with time zone) RETURNS TABLE("entry_id" "uuid", "feed_id" "uuid", "feed_title" "text", "entry_title" "text", "entry_url" "text", "author" "text", "summary" "text", "published_at" timestamp with time zone, "is_read" boolean, "is_saved" boolean) + LANGUAGE "sql" STABLE SECURITY DEFINER + SET "search_path" TO '' + AS $$ select e.id, e.feed_id, f.title, e.title, e.url, e.author, e.summary, e.published_at, @@ -629,14 +636,19 @@ AS $function$ ) order by e.published_at desc nulls last limit result_limit; -$function$; - -CREATE OR REPLACE FUNCTION public.get_custom_feed_timeline(p_custom_feed_id uuid, p_result_limit integer DEFAULT 50, p_pagination_cursor timestamp with time zone DEFAULT NULL::timestamp with time zone) - RETURNS TABLE(entry_id uuid, feed_id uuid, feed_title text, custom_title text, entry_title text, entry_url text, author text, summary text, image_url text, published_at timestamp with time zone, is_read boolean, is_saved boolean, enclosure_url text, enclosure_type text) - LANGUAGE plpgsql - STABLE SECURITY DEFINER - SET search_path TO '' -AS $function$ +$$; + + +ALTER FUNCTION "public"."get_custom_feed"("target_custom_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone) OWNER TO "postgres"; + +-- +-- Name: get_custom_feed_timeline("uuid", integer, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."get_custom_feed_timeline"("p_custom_feed_id" "uuid", "p_result_limit" integer DEFAULT 50, "p_pagination_cursor" timestamp with time zone DEFAULT NULL::timestamp with time zone) RETURNS TABLE("entry_id" "uuid", "feed_id" "uuid", "feed_title" "text", "custom_title" "text", "entry_title" "text", "entry_url" "text", "author" "text", "summary" "text", "image_url" "text", "published_at" timestamp with time zone, "is_read" boolean, "is_saved" boolean, "enclosure_url" "text", "enclosure_type" "text") + LANGUAGE "plpgsql" STABLE SECURITY DEFINER + SET "search_path" TO '' + AS $$ DECLARE v_query TEXT; v_match_mode TEXT; @@ -712,14 +724,74 @@ BEGIN ORDER BY e.published_at DESC NULLS LAST LIMIT p_result_limit; END; -$function$; - -CREATE OR REPLACE FUNCTION public.get_unread_counts() - RETURNS TABLE(feed_id uuid, unread_count bigint) - LANGUAGE sql - STABLE SECURITY DEFINER - SET search_path TO 'public' -AS $function$ +$$; + + +ALTER FUNCTION "public"."get_custom_feed_timeline"("p_custom_feed_id" "uuid", "p_result_limit" integer, "p_pagination_cursor" timestamp with time zone) OWNER TO "postgres"; + +-- +-- Name: get_timeline("uuid", "uuid", integer, timestamp with time zone, boolean); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."get_timeline"("target_folder_id" "uuid" DEFAULT NULL::"uuid", "target_feed_id" "uuid" DEFAULT NULL::"uuid", "result_limit" integer DEFAULT 50, "pagination_cursor" timestamp with time zone DEFAULT NULL::timestamp with time zone, "unread_only" boolean DEFAULT false) RETURNS TABLE("entry_id" "uuid", "feed_id" "uuid", "feed_title" "text", "custom_title" "text", "entry_title" "text", "entry_url" "text", "author" "text", "summary" "text", "image_url" "text", "published_at" timestamp with time zone, "is_read" boolean, "is_saved" boolean, "enclosure_url" "text", "enclosure_type" "text") + LANGUAGE "sql" STABLE SECURITY DEFINER + SET "search_path" TO '' + AS $$ + SELECT + e.id AS entry_id, + e.feed_id, + f.title AS feed_title, + s.custom_title, + e.title AS entry_title, + e.url AS entry_url, + e.author, + e.summary, + e.image_url, + e.published_at, + COALESCE(ues.read, false) AS is_read, + COALESCE(ues.saved, false) AS is_saved, + e.enclosure_url, + e.enclosure_type + FROM public.entries e + INNER JOIN public.subscriptions s + ON s.feed_id = e.feed_id AND s.user_id = auth.uid() + INNER JOIN public.feeds f ON f.id = e.feed_id + LEFT JOIN public.user_entry_states ues + ON ues.entry_id = e.id AND ues.user_id = auth.uid() + WHERE + (e.owner_id IS NULL OR e.owner_id = auth.uid()) + AND (target_folder_id IS NULL OR s.folder_id = target_folder_id) + AND (target_feed_id IS NULL OR e.feed_id = target_feed_id) + AND (target_feed_id IS NOT NULL OR s.hidden_from_timeline = false) + AND (pagination_cursor IS NULL OR e.published_at < pagination_cursor) + AND (NOT unread_only OR COALESCE(ues.read, false) = false) + AND NOT EXISTS ( + SELECT 1 FROM public.muted_keywords mk + WHERE mk.user_id = auth.uid() + AND ( + e.title ILIKE '%' || mk.keyword || '%' + OR e.summary ILIKE '%' || mk.keyword || '%' + ) + ) + AND ( + (SELECT tier FROM public.user_profiles WHERE id = auth.uid()) IN ('pro', 'developer') + OR e.published_at >= now() - interval '14 days' + ) + ORDER BY e.published_at DESC NULLS LAST + LIMIT result_limit; +$$; + + +ALTER FUNCTION "public"."get_timeline"("target_folder_id" "uuid", "target_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone, "unread_only" boolean) OWNER TO "postgres"; + +-- +-- Name: get_unread_counts(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."get_unread_counts"() RETURNS TABLE("feed_id" "uuid", "unread_count" bigint) + LANGUAGE "sql" STABLE SECURITY DEFINER + SET "search_path" TO 'public' + AS $$ SELECT e.feed_id, COUNT(*) AS unread_count FROM entries e INNER JOIN subscriptions s ON s.feed_id = e.feed_id AND s.user_id = auth.uid() @@ -730,14 +802,38 @@ AS $function$ AND ues.read = true ) GROUP BY e.feed_id; -$function$; - -CREATE OR REPLACE FUNCTION public.mark_all_as_read(p_feed_id uuid DEFAULT NULL::uuid, p_folder_id uuid DEFAULT NULL::uuid, p_read_state boolean DEFAULT true) - RETURNS bigint - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO 'public' -AS $function$ +$$; + + +ALTER FUNCTION "public"."get_unread_counts"() OWNER TO "postgres"; + +-- +-- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."handle_new_user"() RETURNS "trigger" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +begin + insert into public.user_profiles (id, display_name) + values (new.id, coalesce(new.raw_user_meta_data ->> 'display_name', split_part(new.email, '@', 1))); + + return new; +end; +$$; + + +ALTER FUNCTION "public"."handle_new_user"() OWNER TO "postgres"; + +-- +-- Name: mark_all_as_read("uuid", "uuid", boolean); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."mark_all_as_read"("p_feed_id" "uuid" DEFAULT NULL::"uuid", "p_folder_id" "uuid" DEFAULT NULL::"uuid", "p_read_state" boolean DEFAULT true) RETURNS bigint + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO 'public' + AS $$ DECLARE affected_count bigint; BEGIN @@ -769,14 +865,85 @@ BEGIN GET DIAGNOSTICS affected_count = ROW_COUNT; RETURN affected_count; END; -$function$; - -CREATE OR REPLACE FUNCTION public.search_entries(p_query text, p_result_limit integer DEFAULT 30) - RETURNS TABLE(entry_id uuid, feed_id uuid, feed_title text, custom_title text, entry_title text, entry_url text, author text, summary text, image_url text, published_at timestamp with time zone, is_read boolean, is_saved boolean) - LANGUAGE sql - STABLE SECURITY DEFINER - SET search_path TO '' -AS $function$ +$$; + + +ALTER FUNCTION "public"."mark_all_as_read"("p_feed_id" "uuid", "p_folder_id" "uuid", "p_read_state" boolean) OWNER TO "postgres"; + +-- +-- Name: prevent_billing_column_modification(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."prevent_billing_column_modification"() RETURNS "trigger" + LANGUAGE "plpgsql" + SET "search_path" TO 'public' + AS $$ +BEGIN + IF coalesce(auth.role(), '') != 'service_role' THEN + IF NEW.tier IS DISTINCT FROM OLD.tier + OR NEW.stripe_customer_identifier IS DISTINCT FROM OLD.stripe_customer_identifier + OR NEW.stripe_subscription_identifier IS DISTINCT FROM OLD.stripe_subscription_identifier + OR NEW.stripe_subscription_status IS DISTINCT FROM OLD.stripe_subscription_status + OR NEW.stripe_current_period_end IS DISTINCT FROM OLD.stripe_current_period_end + THEN + RAISE EXCEPTION 'Billing columns cannot be modified directly'; + END IF; + END IF; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION "public"."prevent_billing_column_modification"() OWNER TO "postgres"; + +-- +-- Name: request_feed_refresh("uuid"); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."request_feed_refresh"("target_subscription_id" "uuid") RETURNS "void" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +declare + resolved_feed_id uuid; + resolved_tier public.subscription_tier; +begin + select s.feed_id into resolved_feed_id + from public.subscriptions s + where s.id = target_subscription_id and s.user_id = auth.uid(); + + if resolved_feed_id is null then + raise exception 'Subscription not found'; + end if; + + select tier into resolved_tier from public.user_profiles where id = auth.uid(); + + if resolved_tier = 'free' then + raise exception 'Manual feed refresh requires a Pro subscription'; + end if; + + perform pgmq.send( + 'feed_refresh', + jsonb_build_object( + 'feed_id', resolved_feed_id, + 'user_id', auth.uid(), + 'requested_at', now() + ) + ); +end; +$$; + + +ALTER FUNCTION "public"."request_feed_refresh"("target_subscription_id" "uuid") OWNER TO "postgres"; + +-- +-- Name: search_entries("text", integer); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."search_entries"("p_query" "text", "p_result_limit" integer DEFAULT 30) RETURNS TABLE("entry_id" "uuid", "feed_id" "uuid", "feed_title" "text", "custom_title" "text", "entry_title" "text", "entry_url" "text", "author" "text", "summary" "text", "image_url" "text", "published_at" timestamp with time zone, "is_read" boolean, "is_saved" boolean) + LANGUAGE "sql" STABLE SECURITY DEFINER + SET "search_path" TO '' + AS $$ SELECT e.id AS entry_id, e.feed_id, @@ -806,441 +973,2715 @@ AS $function$ ) ORDER BY e.published_at DESC LIMIT p_result_limit; -$function$; - -CREATE OR REPLACE FUNCTION public.request_feed_refresh(target_subscription_id uuid) - RETURNS void - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ +$$; + + +ALTER FUNCTION "public"."search_entries"("p_query" "text", "p_result_limit" integer) OWNER TO "postgres"; + +-- +-- Name: subscribe_to_feed("text", "uuid", "text", "text", "text"); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."subscribe_to_feed"("feed_url" "text", "target_folder_id" "uuid" DEFAULT NULL::"uuid", "feed_custom_title" "text" DEFAULT NULL::"text", "feed_credential" "text" DEFAULT NULL::"text", "feed_auth_type" "text" DEFAULT 'bearer'::"text") RETURNS "uuid" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ declare resolved_feed_id uuid; - resolved_tier public.subscription_tier; + new_subscription_id uuid; + resolved_vault_id uuid; + resolved_visibility public.feed_visibility; + existing_subscription_id uuid; begin - select s.feed_id into resolved_feed_id - from public.subscriptions s - where s.id = target_subscription_id and s.user_id = auth.uid(); + if feed_credential is not null and feed_auth_type not in ('bearer', 'basic', 'query_param') then + raise exception 'Invalid authentication type: %. Must be bearer, basic, or query_param', feed_auth_type; + end if; - if resolved_feed_id is null then - raise exception 'Subscription not found'; + if feed_credential is not null then + resolved_visibility := 'authenticated'; + else + resolved_visibility := 'public'; end if; - select tier into resolved_tier from public.user_profiles where id = auth.uid(); + if resolved_visibility = 'public' then + select id into resolved_feed_id + from public.feeds + where url = feed_url and visibility = 'public'; - if resolved_tier = 'free' then - raise exception 'Manual feed refresh requires a Pro subscription'; + if resolved_feed_id is null then + insert into public.feeds (url, visibility) + values (feed_url, 'public') + returning id into resolved_feed_id; + end if; + + select id into existing_subscription_id + from public.subscriptions + where user_id = auth.uid() and feed_id = resolved_feed_id; + + if existing_subscription_id is not null then + raise exception 'You are already subscribed to this feed'; + end if; + else + insert into public.feeds (url, visibility, fetch_interval_seconds) + values (feed_url, 'authenticated', 300) + returning id into resolved_feed_id; + + resolved_vault_id := vault.create_secret( + jsonb_build_object( + 'authenticationType', feed_auth_type, + 'authenticationValue', feed_credential + )::text, + 'feed_credential_' || resolved_feed_id::text, + 'Credential for authenticated feed' + ); end if; - perform pgmq.send( - 'feed_refresh', - jsonb_build_object( - 'feed_id', resolved_feed_id, - 'user_id', auth.uid(), - 'requested_at', now() - ) - ); + insert into public.subscriptions (user_id, feed_id, folder_id, custom_title, vault_secret_id) + values (auth.uid(), resolved_feed_id, target_folder_id, feed_custom_title, resolved_vault_id) + returning id into new_subscription_id; + + return new_subscription_id; end; -$function$; - -CREATE OR REPLACE FUNCTION public.check_subscription_limit() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ +$$; + + +ALTER FUNCTION "public"."subscribe_to_feed"("feed_url" "text", "target_folder_id" "uuid", "feed_custom_title" "text", "feed_credential" "text", "feed_auth_type" "text") OWNER TO "postgres"; + +-- +-- Name: update_feed_credentials("uuid", "text", "text"); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE FUNCTION "public"."update_feed_credentials"("p_feed_id" "uuid", "p_auth_type" "text", "p_credential" "text") RETURNS "void" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ declare - current_tier public.subscription_tier; - current_count int; - maximum_allowed int; - feed_visibility public.feed_visibility; + v_vault_secret_id uuid; begin - select tier, feed_count into current_tier, current_count - from public.user_profiles where id = new.user_id; + if p_auth_type not in ('bearer', 'basic', 'query_param') then + raise exception 'Invalid authentication type: %. Must be bearer, basic, or query_param', p_auth_type; + end if; - maximum_allowed := case current_tier when 'free' then 10 when 'pro' then 200 when 'developer' then 500 else 10 end; + select s.vault_secret_id into v_vault_secret_id + from public.subscriptions s + where s.feed_id = p_feed_id and s.user_id = auth.uid(); - if current_count >= maximum_allowed then - raise exception 'You have reached the maximum number of feeds for your plan (% on % tier)', maximum_allowed, current_tier; + if v_vault_secret_id is null then + raise exception 'No authenticated subscription found for this feed'; end if; - select visibility into feed_visibility from public.feeds where id = new.feed_id; + perform vault.update_secret( + v_vault_secret_id, + jsonb_build_object( + 'authenticationType', p_auth_type, + 'authenticationValue', p_credential + )::text + ); +end; +$$; - if current_tier = 'free' and feed_visibility = 'authenticated' then - raise exception 'Authenticated feeds require a Pro subscription'; - end if; - update public.user_profiles set feed_count = feed_count + 1 where id = new.user_id; +ALTER FUNCTION "public"."update_feed_credentials"("p_feed_id" "uuid", "p_auth_type" "text", "p_credential" "text") OWNER TO "postgres"; - update public.feeds - set subscriber_count = subscriber_count + 1 - where id = new.feed_id; +-- +-- Name: update_feed_url("uuid", "text"); Type: FUNCTION; Schema: public; Owner: postgres +-- - return new; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.check_folder_limit() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -declare - current_tier public.subscription_tier; - current_count int; - maximum_allowed int; -begin - select tier, folder_count into current_tier, current_count - from public.user_profiles where id = new.user_id; +CREATE OR REPLACE FUNCTION "public"."update_feed_url"("target_feed_id" "uuid", "new_url" "text") RETURNS "void" + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO '' + AS $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM public.subscriptions + WHERE feed_id = target_feed_id + AND user_id = auth.uid() + ) THEN + RAISE EXCEPTION 'not subscribed to this feed'; + END IF; - maximum_allowed := case current_tier when 'free' then 3 when 'pro' then 10000 when 'developer' then 10000 else 3 end; + UPDATE public.feeds SET url = new_url WHERE id = target_feed_id; +END; +$$; - if current_count >= maximum_allowed then - raise exception 'You have reached the maximum number of folders for your plan (% on % tier)', maximum_allowed, current_tier; - end if; - update public.user_profiles set folder_count = folder_count + 1 where id = new.user_id; +ALTER FUNCTION "public"."update_feed_url"("target_feed_id" "uuid", "new_url" "text") OWNER TO "postgres"; - return new; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.check_muted_keyword_limit() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -declare - current_tier public.subscription_tier; - current_count int; - maximum_allowed int; -begin - select tier, muted_keyword_count into current_tier, current_count - from public.user_profiles where id = new.user_id; +SET default_tablespace = ''; - maximum_allowed := case current_tier when 'free' then 5 when 'pro' then 10000 when 'developer' then 10000 else 5 end; +SET default_table_access_method = "heap"; - if current_count >= maximum_allowed then - raise exception 'You have reached the maximum number of muted keywords for your plan (% on % tier)', maximum_allowed, current_tier; - end if; +-- +-- Name: api_keys; Type: TABLE; Schema: public; Owner: postgres +-- - update public.user_profiles set muted_keyword_count = muted_keyword_count + 1 where id = new.user_id; +CREATE TABLE IF NOT EXISTS "public"."api_keys" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "user_id" "uuid" NOT NULL, + "key_hash" "text" NOT NULL, + "key_prefix" character(8) NOT NULL, + "label" "text", + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "last_used_at" timestamp with time zone, + "revoked_at" timestamp with time zone +); - return new; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.check_custom_feed_limit() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -declare - current_tier public.subscription_tier; - current_count int; - maximum_allowed int; -begin - select tier, custom_feed_count into current_tier, current_count - from public.user_profiles where id = new.user_id; - maximum_allowed := case current_tier when 'free' then 1 when 'pro' then 1000 when 'developer' then 1000 else 1 end; +ALTER TABLE "public"."api_keys" OWNER TO "postgres"; + +-- +-- Name: custom_feeds; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."custom_feeds" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "user_id" "uuid" NOT NULL, + "name" "text" NOT NULL, + "query" "text" NOT NULL, + "position" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "updated_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "source_folder_id" "uuid", + "match_mode" "text" DEFAULT 'or'::"text" NOT NULL, + "icon_url" "text", + CONSTRAINT "custom_feeds_match_mode_check" CHECK (("match_mode" = ANY (ARRAY['and'::"text", 'or'::"text"]))) +); - if current_count >= maximum_allowed then - raise exception 'You have reached the maximum number of custom feeds for your plan (% on % tier)', maximum_allowed, current_tier; - end if; - update public.user_profiles set custom_feed_count = custom_feed_count + 1 where id = new.user_id; +ALTER TABLE "public"."custom_feeds" OWNER TO "postgres"; + +-- +-- Name: entries; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."entries" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "feed_id" "uuid" NOT NULL, + "owner_id" "uuid", + "guid" "text" NOT NULL, + "url" "text", + "title" "text", + "author" "text", + "summary" "text", + "content_html" "text", + "content_text" "text", + "image_url" "text", + "published_at" timestamp with time zone, + "word_count" integer, + "fetched_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "enclosure_url" "text", + "enclosure_type" "text", + "enclosure_length" bigint +); - return new; -end; -$function$; -CREATE OR REPLACE FUNCTION public.check_highlight_limit() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER -AS $function$ -declare - current_tier public.subscription_tier; - current_count int; - maximum_allowed int; -begin - select tier, highlight_count into current_tier, current_count - from public.user_profiles where id = new.user_id; +ALTER TABLE "public"."entries" OWNER TO "postgres"; + +-- +-- Name: feeds; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."feeds" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "url" "text" NOT NULL, + "site_url" "text", + "title" "text", + "description" "text", + "language" "text", + "feed_type" "text", + "image_url" "text", + "visibility" "public"."feed_visibility" DEFAULT 'public'::"public"."feed_visibility" NOT NULL, + "etag" "text", + "last_modified" "text", + "last_fetched_at" timestamp with time zone, + "last_fetch_error" "text", + "last_fetch_error_at" timestamp with time zone, + "consecutive_failures" integer DEFAULT 0 NOT NULL, + "next_fetch_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "fetch_interval_seconds" integer DEFAULT 1800 NOT NULL, + "subscriber_count" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "updated_at" timestamp with time zone DEFAULT "now"() NOT NULL +); - maximum_allowed := case current_tier when 'free' then 50 when 'pro' then 100000 when 'developer' then 100000 else 50 end; - if current_count >= maximum_allowed then - raise exception 'You have reached the maximum number of highlights for your plan (% on % tier)', maximum_allowed, current_tier; - end if; +ALTER TABLE "public"."feeds" OWNER TO "postgres"; - update public.user_profiles set highlight_count = highlight_count + 1 where id = new.user_id; +-- +-- Name: folders; Type: TABLE; Schema: public; Owner: postgres +-- - return new; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.decrement_subscription_count() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -declare - feed_vis public.feed_visibility; -begin - update public.user_profiles - set feed_count = greatest(feed_count - 1, 0) - where id = old.user_id; +CREATE TABLE IF NOT EXISTS "public"."folders" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "user_id" "uuid" NOT NULL, + "name" "text" NOT NULL, + "position" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "updated_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "icon_url" "text" +); - update public.feeds - set subscriber_count = greatest(subscriber_count - 1, 0) - where id = old.feed_id; - select visibility into feed_vis from public.feeds where id = old.feed_id; +ALTER TABLE "public"."folders" OWNER TO "postgres"; - if feed_vis = 'authenticated' then - delete from public.entries where feed_id = old.feed_id and owner_id = old.user_id; - if old.vault_secret_id is not null then - delete from vault.secrets where id = old.vault_secret_id; - end if; - delete from public.feeds where id = old.feed_id; - end if; +-- +-- Name: muted_keywords; Type: TABLE; Schema: public; Owner: postgres +-- - return old; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.decrement_folder_count() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -begin - update public.user_profiles - set folder_count = greatest(folder_count - 1, 0) - where id = old.user_id; +CREATE TABLE IF NOT EXISTS "public"."muted_keywords" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "user_id" "uuid" NOT NULL, + "keyword" "text" NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL +); - return old; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.decrement_muted_keyword_count() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -begin - update public.user_profiles - set muted_keyword_count = greatest(muted_keyword_count - 1, 0) - where id = old.user_id; - return old; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.decrement_custom_feed_count() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -begin - update public.user_profiles - set custom_feed_count = greatest(custom_feed_count - 1, 0) - where id = old.user_id; +ALTER TABLE "public"."muted_keywords" OWNER TO "postgres"; - return old; -end; -$function$; - -CREATE OR REPLACE FUNCTION public.decrement_highlight_count() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO '' -AS $function$ -begin - update public.user_profiles - set highlight_count = greatest(highlight_count - 1, 0) - where id = old.user_id; +-- +-- Name: rate_limits; Type: TABLE; Schema: public; Owner: postgres +-- - return old; -end; -$function$; +CREATE UNLOGGED TABLE "public"."rate_limits" ( + "identifier" "text" NOT NULL, + "window_start" timestamp with time zone NOT NULL, + "request_count" integer DEFAULT 1 NOT NULL +); -CREATE OR REPLACE FUNCTION public.enqueue_feed_refresh_on_subscribe() - RETURNS trigger - LANGUAGE plpgsql - SECURITY DEFINER -AS $function$ -BEGIN - PERFORM pgmq.send('feed_refresh', jsonb_build_object('feed_id', NEW.feed_id)); - RETURN NEW; -END; -$function$; -CREATE OR REPLACE FUNCTION public.prevent_billing_column_modification() - RETURNS trigger - LANGUAGE plpgsql - SET search_path TO 'public' -AS $function$ -BEGIN - IF coalesce(auth.role(), '') != 'service_role' THEN - IF NEW.tier IS DISTINCT FROM OLD.tier - OR NEW.stripe_customer_identifier IS DISTINCT FROM OLD.stripe_customer_identifier - OR NEW.stripe_subscription_identifier IS DISTINCT FROM OLD.stripe_subscription_identifier - OR NEW.stripe_subscription_status IS DISTINCT FROM OLD.stripe_subscription_status - OR NEW.stripe_current_period_end IS DISTINCT FROM OLD.stripe_current_period_end - THEN - RAISE EXCEPTION 'Billing columns cannot be modified directly'; - END IF; - END IF; - RETURN NEW; -END; -$function$; - -CREATE OR REPLACE FUNCTION public.cleanup_stale_entries() - RETURNS integer - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO 'public' -AS $function$ -DECLARE - deleted_count integer; -BEGIN - DELETE FROM public.entries - WHERE published_at < NOW() - INTERVAL '90 days' - AND id NOT IN ( - SELECT entry_id FROM public.user_entry_states WHERE saved = true - ); - GET DIAGNOSTICS deleted_count = ROW_COUNT; - RETURN deleted_count; -END; -$function$; - --- ============================================================================= --- triggers (public tables) --- ============================================================================= - --- feeds -CREATE TRIGGER feeds_updated_at BEFORE UPDATE ON public.feeds FOR EACH ROW EXECUTE FUNCTION moddatetime('updated_at'); - --- user_profiles -CREATE TRIGGER prevent_billing_modification BEFORE UPDATE ON public.user_profiles FOR EACH ROW EXECUTE FUNCTION prevent_billing_column_modification(); -CREATE TRIGGER user_profiles_updated_at BEFORE UPDATE ON public.user_profiles FOR EACH ROW EXECUTE FUNCTION moddatetime('updated_at'); - --- folders -CREATE TRIGGER check_folder_limit_trigger BEFORE INSERT ON public.folders FOR EACH ROW EXECUTE FUNCTION check_folder_limit(); -CREATE TRIGGER decrement_folder_count_trigger AFTER DELETE ON public.folders FOR EACH ROW EXECUTE FUNCTION decrement_folder_count(); -CREATE TRIGGER folders_updated_at BEFORE UPDATE ON public.folders FOR EACH ROW EXECUTE FUNCTION moddatetime('updated_at'); - --- subscriptions -CREATE TRIGGER check_subscription_limit_trigger BEFORE INSERT ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION check_subscription_limit(); -CREATE TRIGGER decrement_subscription_count_trigger AFTER DELETE ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION decrement_subscription_count(); -CREATE TRIGGER subscriptions_updated_at BEFORE UPDATE ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION moddatetime('updated_at'); -CREATE TRIGGER trigger_enqueue_feed_refresh_after_subscribe AFTER INSERT ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION enqueue_feed_refresh_on_subscribe(); - --- muted_keywords -CREATE TRIGGER check_muted_keyword_limit_trigger BEFORE INSERT ON public.muted_keywords FOR EACH ROW EXECUTE FUNCTION check_muted_keyword_limit(); -CREATE TRIGGER decrement_muted_keyword_count_trigger AFTER DELETE ON public.muted_keywords FOR EACH ROW EXECUTE FUNCTION decrement_muted_keyword_count(); - --- custom_feeds -CREATE TRIGGER check_custom_feed_limit_trigger BEFORE INSERT ON public.custom_feeds FOR EACH ROW EXECUTE FUNCTION check_custom_feed_limit(); -CREATE TRIGGER decrement_custom_feed_count_trigger AFTER DELETE ON public.custom_feeds FOR EACH ROW EXECUTE FUNCTION decrement_custom_feed_count(); -CREATE TRIGGER custom_feeds_updated_at BEFORE UPDATE ON public.custom_feeds FOR EACH ROW EXECUTE FUNCTION moddatetime('updated_at'); - --- user_highlights -CREATE TRIGGER check_highlight_limit_trigger BEFORE INSERT ON public.user_highlights FOR EACH ROW EXECUTE FUNCTION check_highlight_limit(); -CREATE TRIGGER decrement_highlight_count_trigger AFTER DELETE ON public.user_highlights FOR EACH ROW EXECUTE FUNCTION decrement_highlight_count(); - --- ============================================================================= --- triggers (auth schema -- creates user profile on signup) --- ============================================================================= - -CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION handle_new_user(); - --- ============================================================================= --- pg_cron scheduled jobs --- ============================================================================= - --- daily at 3:00 AM UTC: purge old private entries for free-tier users (>14 days) -SELECT cron.schedule('purge-old-private-entries', '0 3 * * *', $$ - delete from public.entries e - where e.owner_id is not null - and exists ( - select 1 from public.user_profiles p - where p.id = e.owner_id - and p.tier = 'free' - ) - and e.published_at < now() - interval '14 days'; - $$); - --- daily at 3:00 AM UTC: cleanup stale entries (>90 days, not saved) -SELECT cron.schedule('cleanup-stale-entries', '0 3 * * *', $$SELECT cleanup_stale_entries()$$); - --- daily at 4:00 AM UTC: purge orphaned entries (no subscribers) -SELECT cron.schedule('purge-orphaned-entries', '0 4 * * *', $$ - delete from public.entries e - where e.owner_id is null - and not exists ( - select 1 from public.subscriptions s where s.feed_id = e.feed_id - ); - $$); - --- daily at 4:30 AM UTC: purge orphaned public feeds (0 subscribers, stale >7 days) -SELECT cron.schedule('purge-orphaned-feeds', '30 4 * * *', $$ - delete from public.feeds - where visibility = 'public' - and subscriber_count = 0 - and updated_at < now() - interval '7 days'; - $$); - --- every 5 minutes: cleanup expired rate limit windows -SELECT cron.schedule('cleanup-rate-limits', '*/5 * * * *', $$DELETE FROM public.rate_limits WHERE window_start < now() - interval '10 minutes'$$); - --- ============================================================================= --- rate limiting --- ============================================================================= - -CREATE UNLOGGED TABLE public.rate_limits ( - identifier text NOT NULL, - window_start timestamptz NOT NULL, - request_count integer NOT NULL DEFAULT 1, - PRIMARY KEY (identifier, window_start) +ALTER TABLE "public"."rate_limits" OWNER TO "postgres"; + +-- +-- Name: shared_entries; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."shared_entries" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "user_id" "uuid" NOT NULL, + "entry_id" "uuid" NOT NULL, + "share_token" "text" NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "expires_at" timestamp with time zone, + "note" "text", + "highlight_text" "text", + "highlighted_text" "text", + "highlight_text_offset" integer, + "highlight_text_length" integer, + "highlight_text_prefix" "text" DEFAULT ''::"text", + "highlight_text_suffix" "text" DEFAULT ''::"text" ); -CREATE OR REPLACE FUNCTION public.check_rate_limit( - p_identifier text, - p_limit integer, - p_window_seconds integer -) RETURNS jsonb -LANGUAGE plpgsql -SECURITY DEFINER -SET search_path TO 'public' -AS $function$ -DECLARE - v_window_start timestamptz; - v_current_count integer; -BEGIN - v_window_start := to_timestamp( - floor(extract(epoch from now()) / p_window_seconds) * p_window_seconds - ); - INSERT INTO public.rate_limits (identifier, window_start, request_count) - VALUES (p_identifier, v_window_start, 1) - ON CONFLICT (identifier, window_start) - DO UPDATE SET request_count = rate_limits.request_count + 1 - RETURNING request_count INTO v_current_count; +ALTER TABLE "public"."shared_entries" OWNER TO "postgres"; + +-- +-- Name: subscriptions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."subscriptions" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "user_id" "uuid" NOT NULL, + "feed_id" "uuid" NOT NULL, + "folder_id" "uuid", + "custom_title" "text", + "position" integer DEFAULT 0 NOT NULL, + "vault_secret_id" "uuid", + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "updated_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "hidden_from_timeline" boolean DEFAULT false NOT NULL +); + + +ALTER TABLE "public"."subscriptions" OWNER TO "postgres"; + +-- +-- Name: user_entry_states; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."user_entry_states" ( + "user_id" "uuid" NOT NULL, + "entry_id" "uuid" NOT NULL, + "read" boolean DEFAULT false NOT NULL, + "saved" boolean DEFAULT false NOT NULL, + "read_at" timestamp with time zone, + "saved_at" timestamp with time zone +); + + +ALTER TABLE "public"."user_entry_states" OWNER TO "postgres"; + +-- +-- Name: user_highlights; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."user_highlights" ( + "id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL, + "user_id" "uuid" NOT NULL, + "entry_id" "uuid" NOT NULL, + "highlighted_text" "text" NOT NULL, + "note" "text", + "text_offset" integer NOT NULL, + "text_length" integer NOT NULL, + "text_prefix" "text" DEFAULT ''::"text" NOT NULL, + "text_suffix" "text" DEFAULT ''::"text" NOT NULL, + "color" "text" DEFAULT 'yellow'::"text" NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL +); + + +ALTER TABLE "public"."user_highlights" OWNER TO "postgres"; + +-- +-- Name: user_profiles; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE IF NOT EXISTS "public"."user_profiles" ( + "id" "uuid" NOT NULL, + "display_name" "text", + "tier" "public"."subscription_tier" DEFAULT 'free'::"public"."subscription_tier" NOT NULL, + "feed_count" integer DEFAULT 0 NOT NULL, + "folder_count" integer DEFAULT 0 NOT NULL, + "muted_keyword_count" integer DEFAULT 0 NOT NULL, + "custom_feed_count" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "updated_at" timestamp with time zone DEFAULT "now"() NOT NULL, + "stripe_customer_identifier" "text", + "stripe_subscription_identifier" "text", + "stripe_subscription_status" "text", + "stripe_current_period_end" timestamp with time zone, + "highlight_count" integer DEFAULT 0 NOT NULL, + "webhook_url" "text", + "webhook_secret" "text", + "webhook_enabled" boolean DEFAULT false NOT NULL, + "webhook_consecutive_failures" integer DEFAULT 0 NOT NULL +); + + +ALTER TABLE "public"."user_profiles" OWNER TO "postgres"; + +-- +-- Name: api_keys api_keys_key_hash_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."api_keys" + ADD CONSTRAINT "api_keys_key_hash_key" UNIQUE ("key_hash"); + + +-- +-- Name: api_keys api_keys_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."api_keys" + ADD CONSTRAINT "api_keys_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: custom_feeds custom_feeds_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."custom_feeds" + ADD CONSTRAINT "custom_feeds_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: entries entries_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."entries" + ADD CONSTRAINT "entries_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: feeds feeds_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."feeds" + ADD CONSTRAINT "feeds_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: folders folders_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."folders" + ADD CONSTRAINT "folders_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: folders folders_user_id_name_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."folders" + ADD CONSTRAINT "folders_user_id_name_key" UNIQUE ("user_id", "name"); + + +-- +-- Name: muted_keywords muted_keywords_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."muted_keywords" + ADD CONSTRAINT "muted_keywords_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: rate_limits rate_limits_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."rate_limits" + ADD CONSTRAINT "rate_limits_pkey" PRIMARY KEY ("identifier", "window_start"); + + +-- +-- Name: shared_entries shared_entries_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."shared_entries" + ADD CONSTRAINT "shared_entries_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: shared_entries shared_entries_share_token_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."shared_entries" + ADD CONSTRAINT "shared_entries_share_token_key" UNIQUE ("share_token"); + + +-- +-- Name: shared_entries shared_entries_user_id_entry_id_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."shared_entries" + ADD CONSTRAINT "shared_entries_user_id_entry_id_key" UNIQUE ("user_id", "entry_id"); + + +-- +-- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."subscriptions" + ADD CONSTRAINT "subscriptions_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: subscriptions subscriptions_user_id_feed_id_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."subscriptions" + ADD CONSTRAINT "subscriptions_user_id_feed_id_key" UNIQUE ("user_id", "feed_id"); + + +-- +-- Name: user_entry_states user_entry_states_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_entry_states" + ADD CONSTRAINT "user_entry_states_pkey" PRIMARY KEY ("user_id", "entry_id"); + + +-- +-- Name: user_highlights user_highlights_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_highlights" + ADD CONSTRAINT "user_highlights_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: user_profiles user_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_profiles" + ADD CONSTRAINT "user_profiles_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: user_profiles user_profiles_stripe_customer_identifier_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_profiles" + ADD CONSTRAINT "user_profiles_stripe_customer_identifier_key" UNIQUE ("stripe_customer_identifier"); + + +-- +-- Name: idx_api_keys_key_hash; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_api_keys_key_hash" ON "public"."api_keys" USING "btree" ("key_hash") WHERE ("revoked_at" IS NULL); + + +-- +-- Name: idx_api_keys_user_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_api_keys_user_id" ON "public"."api_keys" USING "btree" ("user_id"); + + +-- +-- Name: idx_custom_feeds_source_folder; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_custom_feeds_source_folder" ON "public"."custom_feeds" USING "btree" ("source_folder_id"); + + +-- +-- Name: idx_custom_feeds_user; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_custom_feeds_user" ON "public"."custom_feeds" USING "btree" ("user_id"); + + +-- +-- Name: idx_entries_content_trigram; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_entries_content_trigram" ON "public"."entries" USING "gin" ("content_text" "extensions"."gin_trgm_ops"); + + +-- +-- Name: idx_entries_dedup_authenticated; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX "idx_entries_dedup_authenticated" ON "public"."entries" USING "btree" ("feed_id", "owner_id", "guid") WHERE ("owner_id" IS NOT NULL); + + +-- +-- Name: idx_entries_dedup_public; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX "idx_entries_dedup_public" ON "public"."entries" USING "btree" ("feed_id", "guid") WHERE ("owner_id" IS NULL); + + +-- +-- Name: idx_entries_feed_published; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_entries_feed_published" ON "public"."entries" USING "btree" ("feed_id", "published_at" DESC NULLS LAST); + + +-- +-- Name: idx_entries_owner; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_entries_owner" ON "public"."entries" USING "btree" ("owner_id", "published_at" DESC NULLS LAST) WHERE ("owner_id" IS NOT NULL); + + +-- +-- Name: idx_entries_published; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_entries_published" ON "public"."entries" USING "btree" ("published_at" DESC NULLS LAST); + + +-- +-- Name: idx_entries_title_trigram; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_entries_title_trigram" ON "public"."entries" USING "gin" ("title" "extensions"."gin_trgm_ops"); + + +-- +-- Name: idx_feeds_next_fetch; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_feeds_next_fetch" ON "public"."feeds" USING "btree" ("next_fetch_at") WHERE ("consecutive_failures" < 10); + + +-- +-- Name: idx_feeds_url_public; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX "idx_feeds_url_public" ON "public"."feeds" USING "btree" ("url") WHERE ("visibility" = 'public'::"public"."feed_visibility"); + + +-- +-- Name: idx_folders_user; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_folders_user" ON "public"."folders" USING "btree" ("user_id"); + + +-- +-- Name: idx_muted_keywords_user; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_muted_keywords_user" ON "public"."muted_keywords" USING "btree" ("user_id"); + + +-- +-- Name: idx_muted_keywords_user_keyword; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX "idx_muted_keywords_user_keyword" ON "public"."muted_keywords" USING "btree" ("user_id", "lower"("keyword")); + + +-- +-- Name: idx_shared_entries_entry; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_shared_entries_entry" ON "public"."shared_entries" USING "btree" ("entry_id"); + + +-- +-- Name: idx_subscriptions_feed; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_subscriptions_feed" ON "public"."subscriptions" USING "btree" ("feed_id"); + + +-- +-- Name: idx_subscriptions_folder; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_subscriptions_folder" ON "public"."subscriptions" USING "btree" ("folder_id"); + + +-- +-- Name: idx_subscriptions_user; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_subscriptions_user" ON "public"."subscriptions" USING "btree" ("user_id"); + + +-- +-- Name: idx_user_entry_states_entry; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_user_entry_states_entry" ON "public"."user_entry_states" USING "btree" ("entry_id"); + + +-- +-- Name: idx_user_entry_states_read; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_user_entry_states_read" ON "public"."user_entry_states" USING "btree" ("user_id", "entry_id") WHERE ("read" = true); + + +-- +-- Name: idx_user_entry_states_saved; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_user_entry_states_saved" ON "public"."user_entry_states" USING "btree" ("user_id", "saved_at" DESC) WHERE ("saved" = true); + + +-- +-- Name: idx_user_highlights_entry; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_user_highlights_entry" ON "public"."user_highlights" USING "btree" ("entry_id"); + + +-- +-- Name: idx_user_profiles_stripe_customer; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "idx_user_profiles_stripe_customer" ON "public"."user_profiles" USING "btree" ("stripe_customer_identifier") WHERE ("stripe_customer_identifier" IS NOT NULL); + + +-- +-- Name: user_highlights_user_created_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "user_highlights_user_created_idx" ON "public"."user_highlights" USING "btree" ("user_id", "created_at" DESC); + + +-- +-- Name: user_highlights_user_entry_idx; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "user_highlights_user_entry_idx" ON "public"."user_highlights" USING "btree" ("user_id", "entry_id"); + + +-- +-- Name: custom_feeds check_custom_feed_limit_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "check_custom_feed_limit_trigger" BEFORE INSERT ON "public"."custom_feeds" FOR EACH ROW EXECUTE FUNCTION "public"."check_custom_feed_limit"(); + + +-- +-- Name: folders check_folder_limit_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "check_folder_limit_trigger" BEFORE INSERT ON "public"."folders" FOR EACH ROW EXECUTE FUNCTION "public"."check_folder_limit"(); + + +-- +-- Name: user_highlights check_highlight_limit_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "check_highlight_limit_trigger" BEFORE INSERT ON "public"."user_highlights" FOR EACH ROW EXECUTE FUNCTION "public"."check_highlight_limit"(); + + +-- +-- Name: muted_keywords check_muted_keyword_limit_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "check_muted_keyword_limit_trigger" BEFORE INSERT ON "public"."muted_keywords" FOR EACH ROW EXECUTE FUNCTION "public"."check_muted_keyword_limit"(); + + +-- +-- Name: subscriptions check_subscription_limit_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "check_subscription_limit_trigger" BEFORE INSERT ON "public"."subscriptions" FOR EACH ROW EXECUTE FUNCTION "public"."check_subscription_limit"(); + + +-- +-- Name: custom_feeds custom_feeds_updated_at; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "custom_feeds_updated_at" BEFORE UPDATE ON "public"."custom_feeds" FOR EACH ROW EXECUTE FUNCTION "extensions"."moddatetime"('updated_at'); + + +-- +-- Name: custom_feeds decrement_custom_feed_count_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "decrement_custom_feed_count_trigger" AFTER DELETE ON "public"."custom_feeds" FOR EACH ROW EXECUTE FUNCTION "public"."decrement_custom_feed_count"(); + + +-- +-- Name: folders decrement_folder_count_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "decrement_folder_count_trigger" AFTER DELETE ON "public"."folders" FOR EACH ROW EXECUTE FUNCTION "public"."decrement_folder_count"(); + + +-- +-- Name: user_highlights decrement_highlight_count_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "decrement_highlight_count_trigger" AFTER DELETE ON "public"."user_highlights" FOR EACH ROW EXECUTE FUNCTION "public"."decrement_highlight_count"(); + + +-- +-- Name: muted_keywords decrement_muted_keyword_count_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "decrement_muted_keyword_count_trigger" AFTER DELETE ON "public"."muted_keywords" FOR EACH ROW EXECUTE FUNCTION "public"."decrement_muted_keyword_count"(); + + +-- +-- Name: subscriptions decrement_subscription_count_trigger; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "decrement_subscription_count_trigger" AFTER DELETE ON "public"."subscriptions" FOR EACH ROW EXECUTE FUNCTION "public"."decrement_subscription_count"(); + + +-- +-- Name: feeds feeds_updated_at; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "feeds_updated_at" BEFORE UPDATE ON "public"."feeds" FOR EACH ROW EXECUTE FUNCTION "extensions"."moddatetime"('updated_at'); + + +-- +-- Name: folders folders_updated_at; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "folders_updated_at" BEFORE UPDATE ON "public"."folders" FOR EACH ROW EXECUTE FUNCTION "extensions"."moddatetime"('updated_at'); + + +-- +-- Name: user_profiles prevent_billing_modification; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "prevent_billing_modification" BEFORE UPDATE ON "public"."user_profiles" FOR EACH ROW EXECUTE FUNCTION "public"."prevent_billing_column_modification"(); + + +-- +-- Name: subscriptions subscriptions_updated_at; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "subscriptions_updated_at" BEFORE UPDATE ON "public"."subscriptions" FOR EACH ROW EXECUTE FUNCTION "extensions"."moddatetime"('updated_at'); + + +-- +-- Name: subscriptions trigger_enqueue_feed_refresh_after_subscribe; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "trigger_enqueue_feed_refresh_after_subscribe" AFTER INSERT ON "public"."subscriptions" FOR EACH ROW EXECUTE FUNCTION "public"."enqueue_feed_refresh_on_subscribe"(); + + +-- +-- Name: user_profiles user_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE OR REPLACE TRIGGER "user_profiles_updated_at" BEFORE UPDATE ON "public"."user_profiles" FOR EACH ROW EXECUTE FUNCTION "extensions"."moddatetime"('updated_at'); + + +-- +-- Name: api_keys api_keys_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."api_keys" + ADD CONSTRAINT "api_keys_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: custom_feeds custom_feeds_source_folder_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."custom_feeds" + ADD CONSTRAINT "custom_feeds_source_folder_id_fkey" FOREIGN KEY ("source_folder_id") REFERENCES "public"."folders"("id") ON DELETE SET NULL; + + +-- +-- Name: custom_feeds custom_feeds_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."custom_feeds" + ADD CONSTRAINT "custom_feeds_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: entries entries_feed_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."entries" + ADD CONSTRAINT "entries_feed_id_fkey" FOREIGN KEY ("feed_id") REFERENCES "public"."feeds"("id") ON DELETE CASCADE; + + +-- +-- Name: entries entries_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."entries" + ADD CONSTRAINT "entries_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: folders folders_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."folders" + ADD CONSTRAINT "folders_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: muted_keywords muted_keywords_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."muted_keywords" + ADD CONSTRAINT "muted_keywords_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: shared_entries shared_entries_entry_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."shared_entries" + ADD CONSTRAINT "shared_entries_entry_id_fkey" FOREIGN KEY ("entry_id") REFERENCES "public"."entries"("id") ON DELETE CASCADE; + + +-- +-- Name: shared_entries shared_entries_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."shared_entries" + ADD CONSTRAINT "shared_entries_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: subscriptions subscriptions_feed_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."subscriptions" + ADD CONSTRAINT "subscriptions_feed_id_fkey" FOREIGN KEY ("feed_id") REFERENCES "public"."feeds"("id") ON DELETE CASCADE; + + +-- +-- Name: subscriptions subscriptions_folder_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."subscriptions" + ADD CONSTRAINT "subscriptions_folder_id_fkey" FOREIGN KEY ("folder_id") REFERENCES "public"."folders"("id") ON DELETE SET NULL; + + +-- +-- Name: subscriptions subscriptions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."subscriptions" + ADD CONSTRAINT "subscriptions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: user_entry_states user_entry_states_entry_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_entry_states" + ADD CONSTRAINT "user_entry_states_entry_id_fkey" FOREIGN KEY ("entry_id") REFERENCES "public"."entries"("id") ON DELETE CASCADE; + + +-- +-- Name: user_entry_states user_entry_states_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_entry_states" + ADD CONSTRAINT "user_entry_states_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: user_highlights user_highlights_entry_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_highlights" + ADD CONSTRAINT "user_highlights_entry_id_fkey" FOREIGN KEY ("entry_id") REFERENCES "public"."entries"("id") ON DELETE CASCADE; + + +-- +-- Name: user_highlights user_highlights_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_highlights" + ADD CONSTRAINT "user_highlights_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: user_profiles user_profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY "public"."user_profiles" + ADD CONSTRAINT "user_profiles_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: api_keys Users can delete own API keys; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Users can delete own API keys" ON "public"."api_keys" FOR DELETE USING ((( SELECT "auth"."uid"() AS "uid") = "user_id")); + + +-- +-- Name: api_keys Users can insert own API keys; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Users can insert own API keys" ON "public"."api_keys" FOR INSERT WITH CHECK ((( SELECT "auth"."uid"() AS "uid") = "user_id")); + + +-- +-- Name: api_keys Users can update own API keys; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Users can update own API keys" ON "public"."api_keys" FOR UPDATE USING ((( SELECT "auth"."uid"() AS "uid") = "user_id")); + + +-- +-- Name: api_keys Users can view own API keys; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Users can view own API keys" ON "public"."api_keys" FOR SELECT USING ((( SELECT "auth"."uid"() AS "uid") = "user_id")); + + +-- +-- Name: api_keys; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."api_keys" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: shared_entries authenticated users can view own shared entries; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "authenticated users can view own shared entries" ON "public"."shared_entries" FOR SELECT TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: custom_feeds; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."custom_feeds" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: entries; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."entries" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: feeds; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."feeds" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: folders; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."folders" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: muted_keywords; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."muted_keywords" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: shared_entries owners can delete own shared entries; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "owners can delete own shared entries" ON "public"."shared_entries" FOR DELETE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: shared_entries owners can insert own shared entries; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "owners can insert own shared entries" ON "public"."shared_entries" FOR INSERT WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: shared_entries owners can update own shared entries; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "owners can update own shared entries" ON "public"."shared_entries" FOR UPDATE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: rate_limits; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."rate_limits" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: shared_entries; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."shared_entries" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."subscriptions" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: user_entry_states; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."user_entry_states" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: user_highlights; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."user_highlights" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: user_profiles; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE "public"."user_profiles" ENABLE ROW LEVEL SECURITY; + +-- +-- Name: custom_feeds users can delete own custom feeds; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can delete own custom feeds" ON "public"."custom_feeds" FOR DELETE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: folders users can delete own folders; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can delete own folders" ON "public"."folders" FOR DELETE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: user_highlights users can delete own highlights; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can delete own highlights" ON "public"."user_highlights" FOR DELETE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: muted_keywords users can delete own muted keywords; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can delete own muted keywords" ON "public"."muted_keywords" FOR DELETE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: subscriptions users can delete own subscriptions; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can delete own subscriptions" ON "public"."subscriptions" FOR DELETE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: custom_feeds users can insert own custom feeds; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can insert own custom feeds" ON "public"."custom_feeds" FOR INSERT WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: folders users can insert own folders; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can insert own folders" ON "public"."folders" FOR INSERT WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: user_highlights users can insert own highlights; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can insert own highlights" ON "public"."user_highlights" FOR INSERT WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: muted_keywords users can insert own muted keywords; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can insert own muted keywords" ON "public"."muted_keywords" FOR INSERT WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: subscriptions users can insert own subscriptions; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can insert own subscriptions" ON "public"."subscriptions" FOR INSERT WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: user_entry_states users can manage own entry states; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can manage own entry states" ON "public"."user_entry_states" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: custom_feeds users can update own custom feeds; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can update own custom feeds" ON "public"."custom_feeds" FOR UPDATE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: folders users can update own folders; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can update own folders" ON "public"."folders" FOR UPDATE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: user_highlights users can update own highlights; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can update own highlights" ON "public"."user_highlights" FOR UPDATE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: user_profiles users can update own profile; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can update own profile" ON "public"."user_profiles" FOR UPDATE USING (("id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: subscriptions users can update own subscriptions; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can update own subscriptions" ON "public"."subscriptions" FOR UPDATE USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: feeds users can view accessible feeds; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view accessible feeds" ON "public"."feeds" FOR SELECT USING (((("visibility" = 'public'::"public"."feed_visibility") AND (( SELECT "auth"."role"() AS "role") = 'authenticated'::"text")) OR (("visibility" = 'authenticated'::"public"."feed_visibility") AND (EXISTS ( SELECT 1 + FROM "public"."subscriptions" "s" + WHERE (("s"."feed_id" = "feeds"."id") AND ("s"."user_id" = ( SELECT "auth"."uid"() AS "uid")))))))); + + +-- +-- Name: entries users can view entries from subscribed public feeds; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view entries from subscribed public feeds" ON "public"."entries" FOR SELECT USING (((("owner_id" IS NULL) AND (EXISTS ( SELECT 1 + FROM "public"."subscriptions" "s" + WHERE (("s"."feed_id" = "entries"."feed_id") AND ("s"."user_id" = ( SELECT "auth"."uid"() AS "uid")))))) OR ("owner_id" = ( SELECT "auth"."uid"() AS "uid")))); + + +-- +-- Name: custom_feeds users can view own custom feeds; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view own custom feeds" ON "public"."custom_feeds" FOR SELECT USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: folders users can view own folders; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view own folders" ON "public"."folders" FOR SELECT USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: user_highlights users can view own highlights; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view own highlights" ON "public"."user_highlights" FOR SELECT USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: muted_keywords users can view own muted keywords; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view own muted keywords" ON "public"."muted_keywords" FOR SELECT USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: user_profiles users can view own profile; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view own profile" ON "public"."user_profiles" FOR SELECT USING (("id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: subscriptions users can view own subscriptions; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "users can view own subscriptions" ON "public"."subscriptions" FOR SELECT USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))); + + +-- +-- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: postgres +-- + +-- CREATE PUBLICATION "supabase_realtime" WITH (publish = 'insert, update, delete, truncate'); + + +ALTER PUBLICATION "supabase_realtime" OWNER TO "postgres"; + +-- +-- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: supabase_admin +-- + +-- CREATE PUBLICATION "supabase_realtime_messages_publication" WITH (publish = 'insert, update, delete, truncate'); + + +-- ALTER PUBLICATION "supabase_realtime_messages_publication" OWNER TO "supabase_admin"; + +-- +-- Name: supabase_realtime entries; Type: PUBLICATION TABLE; Schema: public; Owner: postgres +-- + +ALTER PUBLICATION "supabase_realtime" ADD TABLE ONLY "public"."entries"; + + +-- +-- Name: SCHEMA "cron"; Type: ACL; Schema: -; Owner: supabase_admin +-- + +-- GRANT USAGE ON SCHEMA "cron" TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: SCHEMA "public"; Type: ACL; Schema: -; Owner: pg_database_owner +-- + +GRANT USAGE ON SCHEMA "public" TO "postgres"; +GRANT USAGE ON SCHEMA "public" TO "anon"; +GRANT USAGE ON SCHEMA "public" TO "authenticated"; +GRANT USAGE ON SCHEMA "public" TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_in"("cstring"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_in"("cstring") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_in"("cstring") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_in"("cstring") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_in"("cstring") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_out"("extensions"."gtrgm"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_out"("extensions"."gtrgm") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_out"("extensions"."gtrgm") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_out"("extensions"."gtrgm") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_out"("extensions"."gtrgm") TO "service_role"; + + +-- +-- Name: FUNCTION "alter_job"("job_id" bigint, "schedule" "text", "command" "text", "database" "text", "username" "text", "active" boolean); Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "cron"."alter_job"("job_id" bigint, "schedule" "text", "command" "text", "database" "text", "username" "text", "active" boolean) TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: FUNCTION "job_cache_invalidate"(); Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "cron"."job_cache_invalidate"() TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: FUNCTION "schedule"("schedule" "text", "command" "text"); Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "cron"."schedule"("schedule" "text", "command" "text") TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: FUNCTION "schedule"("job_name" "text", "schedule" "text", "command" "text"); Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "cron"."schedule"("job_name" "text", "schedule" "text", "command" "text") TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: FUNCTION "schedule_in_database"("job_name" "text", "schedule" "text", "command" "text", "database" "text", "username" "text", "active" boolean); Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "cron"."schedule_in_database"("job_name" "text", "schedule" "text", "command" "text", "database" "text", "username" "text", "active" boolean) TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: FUNCTION "unschedule"("job_id" bigint); Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "cron"."unschedule"("job_id" bigint) TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: FUNCTION "unschedule"("job_name" "text"); Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "cron"."unschedule"("job_name" "text") TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: FUNCTION "armor"("bytea"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."armor"("bytea") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."armor"("bytea") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."armor"("bytea") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "armor"("bytea", "text"[], "text"[]); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."armor"("bytea", "text"[], "text"[]) FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."armor"("bytea", "text"[], "text"[]) TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."armor"("bytea", "text"[], "text"[]) TO "dashboard_user"; + + +-- +-- Name: FUNCTION "crypt"("text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."crypt"("text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."crypt"("text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."crypt"("text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "dearmor"("text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."dearmor"("text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."dearmor"("text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."dearmor"("text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "decrypt"("bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."decrypt"("bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."decrypt"("bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."decrypt"("bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "decrypt_iv"("bytea", "bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."decrypt_iv"("bytea", "bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."decrypt_iv"("bytea", "bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."decrypt_iv"("bytea", "bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "digest"("bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."digest"("bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."digest"("bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."digest"("bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "digest"("text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."digest"("text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."digest"("text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."digest"("text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "encrypt"("bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."encrypt"("bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."encrypt"("bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."encrypt"("bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "encrypt_iv"("bytea", "bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."encrypt_iv"("bytea", "bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."encrypt_iv"("bytea", "bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."encrypt_iv"("bytea", "bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "gen_random_bytes"(integer); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."gen_random_bytes"(integer) FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gen_random_bytes"(integer) TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."gen_random_bytes"(integer) TO "dashboard_user"; + + +-- +-- Name: FUNCTION "gen_random_uuid"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."gen_random_uuid"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gen_random_uuid"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."gen_random_uuid"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "gen_salt"("text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."gen_salt"("text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gen_salt"("text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."gen_salt"("text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "gen_salt"("text", integer); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."gen_salt"("text", integer) FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gen_salt"("text", integer) TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."gen_salt"("text", integer) TO "dashboard_user"; + + +-- +-- Name: FUNCTION "gin_extract_query_trgm"("text", "internal", smallint, "internal", "internal", "internal", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_query_trgm"("text", "internal", smallint, "internal", "internal", "internal", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_query_trgm"("text", "internal", smallint, "internal", "internal", "internal", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_query_trgm"("text", "internal", smallint, "internal", "internal", "internal", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_query_trgm"("text", "internal", smallint, "internal", "internal", "internal", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gin_extract_value_trgm"("text", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_value_trgm"("text", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_value_trgm"("text", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_value_trgm"("text", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gin_extract_value_trgm"("text", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gin_trgm_consistent"("internal", smallint, "text", integer, "internal", "internal", "internal", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_consistent"("internal", smallint, "text", integer, "internal", "internal", "internal", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_consistent"("internal", smallint, "text", integer, "internal", "internal", "internal", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_consistent"("internal", smallint, "text", integer, "internal", "internal", "internal", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_consistent"("internal", smallint, "text", integer, "internal", "internal", "internal", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gin_trgm_triconsistent"("internal", smallint, "text", integer, "internal", "internal", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_triconsistent"("internal", smallint, "text", integer, "internal", "internal", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_triconsistent"("internal", smallint, "text", integer, "internal", "internal", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_triconsistent"("internal", smallint, "text", integer, "internal", "internal", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gin_trgm_triconsistent"("internal", smallint, "text", integer, "internal", "internal", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_compress"("internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_compress"("internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_compress"("internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_compress"("internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_compress"("internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_consistent"("internal", "text", smallint, "oid", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_consistent"("internal", "text", smallint, "oid", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_consistent"("internal", "text", smallint, "oid", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_consistent"("internal", "text", smallint, "oid", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_consistent"("internal", "text", smallint, "oid", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_decompress"("internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_decompress"("internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_decompress"("internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_decompress"("internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_decompress"("internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_distance"("internal", "text", smallint, "oid", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_distance"("internal", "text", smallint, "oid", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_distance"("internal", "text", smallint, "oid", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_distance"("internal", "text", smallint, "oid", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_distance"("internal", "text", smallint, "oid", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_options"("internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_options"("internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_options"("internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_options"("internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_options"("internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_penalty"("internal", "internal", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_penalty"("internal", "internal", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_penalty"("internal", "internal", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_penalty"("internal", "internal", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_penalty"("internal", "internal", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_picksplit"("internal", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_picksplit"("internal", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_picksplit"("internal", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_picksplit"("internal", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_picksplit"("internal", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_same"("extensions"."gtrgm", "extensions"."gtrgm", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_same"("extensions"."gtrgm", "extensions"."gtrgm", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_same"("extensions"."gtrgm", "extensions"."gtrgm", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_same"("extensions"."gtrgm", "extensions"."gtrgm", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_same"("extensions"."gtrgm", "extensions"."gtrgm", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "gtrgm_union"("internal", "internal"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_union"("internal", "internal") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_union"("internal", "internal") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_union"("internal", "internal") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."gtrgm_union"("internal", "internal") TO "service_role"; + + +-- +-- Name: FUNCTION "hmac"("bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."hmac"("bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."hmac"("bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."hmac"("bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "hmac"("text", "text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."hmac"("text", "text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."hmac"("text", "text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."hmac"("text", "text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "moddatetime"(); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."moddatetime"() TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."moddatetime"() TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."moddatetime"() TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."moddatetime"() TO "service_role"; + + +-- +-- Name: FUNCTION "pg_stat_statements"("showtext" boolean, OUT "userid" "oid", OUT "dbid" "oid", OUT "toplevel" boolean, OUT "queryid" bigint, OUT "query" "text", OUT "plans" bigint, OUT "total_plan_time" double precision, OUT "min_plan_time" double precision, OUT "max_plan_time" double precision, OUT "mean_plan_time" double precision, OUT "stddev_plan_time" double precision, OUT "calls" bigint, OUT "total_exec_time" double precision, OUT "min_exec_time" double precision, OUT "max_exec_time" double precision, OUT "mean_exec_time" double precision, OUT "stddev_exec_time" double precision, OUT "rows" bigint, OUT "shared_blks_hit" bigint, OUT "shared_blks_read" bigint, OUT "shared_blks_dirtied" bigint, OUT "shared_blks_written" bigint, OUT "local_blks_hit" bigint, OUT "local_blks_read" bigint, OUT "local_blks_dirtied" bigint, OUT "local_blks_written" bigint, OUT "temp_blks_read" bigint, OUT "temp_blks_written" bigint, OUT "shared_blk_read_time" double precision, OUT "shared_blk_write_time" double precision, OUT "local_blk_read_time" double precision, OUT "local_blk_write_time" double precision, OUT "temp_blk_read_time" double precision, OUT "temp_blk_write_time" double precision, OUT "wal_records" bigint, OUT "wal_fpi" bigint, OUT "wal_bytes" numeric, OUT "jit_functions" bigint, OUT "jit_generation_time" double precision, OUT "jit_inlining_count" bigint, OUT "jit_inlining_time" double precision, OUT "jit_optimization_count" bigint, OUT "jit_optimization_time" double precision, OUT "jit_emission_count" bigint, OUT "jit_emission_time" double precision, OUT "jit_deform_count" bigint, OUT "jit_deform_time" double precision, OUT "stats_since" timestamp with time zone, OUT "minmax_stats_since" timestamp with time zone); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pg_stat_statements"("showtext" boolean, OUT "userid" "oid", OUT "dbid" "oid", OUT "toplevel" boolean, OUT "queryid" bigint, OUT "query" "text", OUT "plans" bigint, OUT "total_plan_time" double precision, OUT "min_plan_time" double precision, OUT "max_plan_time" double precision, OUT "mean_plan_time" double precision, OUT "stddev_plan_time" double precision, OUT "calls" bigint, OUT "total_exec_time" double precision, OUT "min_exec_time" double precision, OUT "max_exec_time" double precision, OUT "mean_exec_time" double precision, OUT "stddev_exec_time" double precision, OUT "rows" bigint, OUT "shared_blks_hit" bigint, OUT "shared_blks_read" bigint, OUT "shared_blks_dirtied" bigint, OUT "shared_blks_written" bigint, OUT "local_blks_hit" bigint, OUT "local_blks_read" bigint, OUT "local_blks_dirtied" bigint, OUT "local_blks_written" bigint, OUT "temp_blks_read" bigint, OUT "temp_blks_written" bigint, OUT "shared_blk_read_time" double precision, OUT "shared_blk_write_time" double precision, OUT "local_blk_read_time" double precision, OUT "local_blk_write_time" double precision, OUT "temp_blk_read_time" double precision, OUT "temp_blk_write_time" double precision, OUT "wal_records" bigint, OUT "wal_fpi" bigint, OUT "wal_bytes" numeric, OUT "jit_functions" bigint, OUT "jit_generation_time" double precision, OUT "jit_inlining_count" bigint, OUT "jit_inlining_time" double precision, OUT "jit_optimization_count" bigint, OUT "jit_optimization_time" double precision, OUT "jit_emission_count" bigint, OUT "jit_emission_time" double precision, OUT "jit_deform_count" bigint, OUT "jit_deform_time" double precision, OUT "stats_since" timestamp with time zone, OUT "minmax_stats_since" timestamp with time zone) FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pg_stat_statements"("showtext" boolean, OUT "userid" "oid", OUT "dbid" "oid", OUT "toplevel" boolean, OUT "queryid" bigint, OUT "query" "text", OUT "plans" bigint, OUT "total_plan_time" double precision, OUT "min_plan_time" double precision, OUT "max_plan_time" double precision, OUT "mean_plan_time" double precision, OUT "stddev_plan_time" double precision, OUT "calls" bigint, OUT "total_exec_time" double precision, OUT "min_exec_time" double precision, OUT "max_exec_time" double precision, OUT "mean_exec_time" double precision, OUT "stddev_exec_time" double precision, OUT "rows" bigint, OUT "shared_blks_hit" bigint, OUT "shared_blks_read" bigint, OUT "shared_blks_dirtied" bigint, OUT "shared_blks_written" bigint, OUT "local_blks_hit" bigint, OUT "local_blks_read" bigint, OUT "local_blks_dirtied" bigint, OUT "local_blks_written" bigint, OUT "temp_blks_read" bigint, OUT "temp_blks_written" bigint, OUT "shared_blk_read_time" double precision, OUT "shared_blk_write_time" double precision, OUT "local_blk_read_time" double precision, OUT "local_blk_write_time" double precision, OUT "temp_blk_read_time" double precision, OUT "temp_blk_write_time" double precision, OUT "wal_records" bigint, OUT "wal_fpi" bigint, OUT "wal_bytes" numeric, OUT "jit_functions" bigint, OUT "jit_generation_time" double precision, OUT "jit_inlining_count" bigint, OUT "jit_inlining_time" double precision, OUT "jit_optimization_count" bigint, OUT "jit_optimization_time" double precision, OUT "jit_emission_count" bigint, OUT "jit_emission_time" double precision, OUT "jit_deform_count" bigint, OUT "jit_deform_time" double precision, OUT "stats_since" timestamp with time zone, OUT "minmax_stats_since" timestamp with time zone) TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pg_stat_statements"("showtext" boolean, OUT "userid" "oid", OUT "dbid" "oid", OUT "toplevel" boolean, OUT "queryid" bigint, OUT "query" "text", OUT "plans" bigint, OUT "total_plan_time" double precision, OUT "min_plan_time" double precision, OUT "max_plan_time" double precision, OUT "mean_plan_time" double precision, OUT "stddev_plan_time" double precision, OUT "calls" bigint, OUT "total_exec_time" double precision, OUT "min_exec_time" double precision, OUT "max_exec_time" double precision, OUT "mean_exec_time" double precision, OUT "stddev_exec_time" double precision, OUT "rows" bigint, OUT "shared_blks_hit" bigint, OUT "shared_blks_read" bigint, OUT "shared_blks_dirtied" bigint, OUT "shared_blks_written" bigint, OUT "local_blks_hit" bigint, OUT "local_blks_read" bigint, OUT "local_blks_dirtied" bigint, OUT "local_blks_written" bigint, OUT "temp_blks_read" bigint, OUT "temp_blks_written" bigint, OUT "shared_blk_read_time" double precision, OUT "shared_blk_write_time" double precision, OUT "local_blk_read_time" double precision, OUT "local_blk_write_time" double precision, OUT "temp_blk_read_time" double precision, OUT "temp_blk_write_time" double precision, OUT "wal_records" bigint, OUT "wal_fpi" bigint, OUT "wal_bytes" numeric, OUT "jit_functions" bigint, OUT "jit_generation_time" double precision, OUT "jit_inlining_count" bigint, OUT "jit_inlining_time" double precision, OUT "jit_optimization_count" bigint, OUT "jit_optimization_time" double precision, OUT "jit_emission_count" bigint, OUT "jit_emission_time" double precision, OUT "jit_deform_count" bigint, OUT "jit_deform_time" double precision, OUT "stats_since" timestamp with time zone, OUT "minmax_stats_since" timestamp with time zone) TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pg_stat_statements_info"(OUT "dealloc" bigint, OUT "stats_reset" timestamp with time zone); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pg_stat_statements_info"(OUT "dealloc" bigint, OUT "stats_reset" timestamp with time zone) FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pg_stat_statements_info"(OUT "dealloc" bigint, OUT "stats_reset" timestamp with time zone) TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pg_stat_statements_info"(OUT "dealloc" bigint, OUT "stats_reset" timestamp with time zone) TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pg_stat_statements_reset"("userid" "oid", "dbid" "oid", "queryid" bigint, "minmax_only" boolean); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pg_stat_statements_reset"("userid" "oid", "dbid" "oid", "queryid" bigint, "minmax_only" boolean) FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pg_stat_statements_reset"("userid" "oid", "dbid" "oid", "queryid" bigint, "minmax_only" boolean) TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pg_stat_statements_reset"("userid" "oid", "dbid" "oid", "queryid" bigint, "minmax_only" boolean) TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_armor_headers"("text", OUT "key" "text", OUT "value" "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_armor_headers"("text", OUT "key" "text", OUT "value" "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_armor_headers"("text", OUT "key" "text", OUT "value" "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_armor_headers"("text", OUT "key" "text", OUT "value" "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_key_id"("bytea"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_key_id"("bytea") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_key_id"("bytea") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_key_id"("bytea") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_decrypt"("bytea", "bytea"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_decrypt"("bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_decrypt"("bytea", "bytea", "text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea", "text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea", "text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt"("bytea", "bytea", "text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_decrypt_bytea"("bytea", "bytea"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_decrypt_bytea"("bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_decrypt_bytea"("bytea", "bytea", "text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea", "text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea", "text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_decrypt_bytea"("bytea", "bytea", "text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_encrypt"("text", "bytea"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_encrypt"("text", "bytea") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt"("text", "bytea") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt"("text", "bytea") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_encrypt"("text", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_encrypt"("text", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt"("text", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt"("text", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_encrypt_bytea"("bytea", "bytea"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_encrypt_bytea"("bytea", "bytea") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt_bytea"("bytea", "bytea") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt_bytea"("bytea", "bytea") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_pub_encrypt_bytea"("bytea", "bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_pub_encrypt_bytea"("bytea", "bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt_bytea"("bytea", "bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_pub_encrypt_bytea"("bytea", "bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_decrypt"("bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_decrypt"("bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt"("bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt"("bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_decrypt"("bytea", "text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_decrypt"("bytea", "text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt"("bytea", "text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt"("bytea", "text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_decrypt_bytea"("bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_decrypt_bytea"("bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt_bytea"("bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt_bytea"("bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_decrypt_bytea"("bytea", "text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_decrypt_bytea"("bytea", "text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt_bytea"("bytea", "text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_decrypt_bytea"("bytea", "text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_encrypt"("text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_encrypt"("text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt"("text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt"("text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_encrypt"("text", "text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_encrypt"("text", "text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt"("text", "text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt"("text", "text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_encrypt_bytea"("bytea", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_encrypt_bytea"("bytea", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt_bytea"("bytea", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt_bytea"("bytea", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "pgp_sym_encrypt_bytea"("bytea", "text", "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."pgp_sym_encrypt_bytea"("bytea", "text", "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt_bytea"("bytea", "text", "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."pgp_sym_encrypt_bytea"("bytea", "text", "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "set_limit"(real); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."set_limit"(real) TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."set_limit"(real) TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."set_limit"(real) TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."set_limit"(real) TO "service_role"; + + +-- +-- Name: FUNCTION "show_limit"(); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."show_limit"() TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."show_limit"() TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."show_limit"() TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."show_limit"() TO "service_role"; + + +-- +-- Name: FUNCTION "show_trgm"("text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."show_trgm"("text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."show_trgm"("text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."show_trgm"("text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."show_trgm"("text") TO "service_role"; + + +-- +-- Name: FUNCTION "similarity"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."similarity"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."similarity"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."similarity"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."similarity"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "similarity_dist"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."similarity_dist"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."similarity_dist"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."similarity_dist"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."similarity_dist"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "similarity_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."similarity_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."similarity_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."similarity_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."similarity_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "strict_word_similarity"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "strict_word_similarity_commutator_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_commutator_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_commutator_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_commutator_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_commutator_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "strict_word_similarity_dist_commutator_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_commutator_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_commutator_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_commutator_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_commutator_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "strict_word_similarity_dist_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_dist_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "strict_word_similarity_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."strict_word_similarity_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "uuid_generate_v1"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_generate_v1"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v1"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v1"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_generate_v1mc"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_generate_v1mc"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v1mc"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v1mc"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_generate_v3"("namespace" "uuid", "name" "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_generate_v3"("namespace" "uuid", "name" "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v3"("namespace" "uuid", "name" "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v3"("namespace" "uuid", "name" "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_generate_v4"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_generate_v4"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v4"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v4"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_generate_v5"("namespace" "uuid", "name" "text"); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_generate_v5"("namespace" "uuid", "name" "text") FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v5"("namespace" "uuid", "name" "text") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_generate_v5"("namespace" "uuid", "name" "text") TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_nil"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_nil"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_nil"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_nil"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_ns_dns"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_ns_dns"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_dns"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_dns"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_ns_oid"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_ns_oid"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_oid"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_oid"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_ns_url"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_ns_url"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_url"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_url"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "uuid_ns_x500"(); Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON FUNCTION "extensions"."uuid_ns_x500"() FROM "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_x500"() TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "extensions"."uuid_ns_x500"() TO "dashboard_user"; + + +-- +-- Name: FUNCTION "word_similarity"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."word_similarity"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "word_similarity_commutator_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_commutator_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_commutator_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_commutator_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_commutator_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "word_similarity_dist_commutator_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_commutator_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_commutator_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_commutator_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_commutator_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "word_similarity_dist_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_dist_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "word_similarity_op"("text", "text"); Type: ACL; Schema: extensions; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_op"("text", "text") TO "postgres"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_op"("text", "text") TO "anon"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_op"("text", "text") TO "authenticated"; +-- GRANT ALL ON FUNCTION "extensions"."word_similarity_op"("text", "text") TO "service_role"; + + +-- +-- Name: FUNCTION "graphql"("operationName" "text", "query" "text", "variables" "jsonb", "extensions" "jsonb"); Type: ACL; Schema: graphql_public; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "graphql_public"."graphql"("operationName" "text", "query" "text", "variables" "jsonb", "extensions" "jsonb") TO "postgres"; +-- GRANT ALL ON FUNCTION "graphql_public"."graphql"("operationName" "text", "query" "text", "variables" "jsonb", "extensions" "jsonb") TO "anon"; +-- GRANT ALL ON FUNCTION "graphql_public"."graphql"("operationName" "text", "query" "text", "variables" "jsonb", "extensions" "jsonb") TO "authenticated"; +-- GRANT ALL ON FUNCTION "graphql_public"."graphql"("operationName" "text", "query" "text", "variables" "jsonb", "extensions" "jsonb") TO "service_role"; + + +-- +-- Name: FUNCTION "crypto_aead_det_decrypt"("message" "bytea", "additional" "bytea", "key_uuid" "uuid", "nonce" "bytea"); Type: ACL; Schema: pgsodium; Owner: pgsodium_keymaker +-- + +-- GRANT ALL ON FUNCTION "pgsodium"."crypto_aead_det_decrypt"("message" "bytea", "additional" "bytea", "key_uuid" "uuid", "nonce" "bytea") TO "service_role"; + + +-- +-- Name: FUNCTION "crypto_aead_det_encrypt"("message" "bytea", "additional" "bytea", "key_uuid" "uuid", "nonce" "bytea"); Type: ACL; Schema: pgsodium; Owner: pgsodium_keymaker +-- + +-- GRANT ALL ON FUNCTION "pgsodium"."crypto_aead_det_encrypt"("message" "bytea", "additional" "bytea", "key_uuid" "uuid", "nonce" "bytea") TO "service_role"; + + +-- +-- Name: FUNCTION "crypto_aead_det_keygen"(); Type: ACL; Schema: pgsodium; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "pgsodium"."crypto_aead_det_keygen"() TO "service_role"; + + +-- +-- Name: FUNCTION "add_feed_credentials"("p_subscription_id" "uuid", "p_auth_type" "text", "p_credential" "text"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."add_feed_credentials"("p_subscription_id" "uuid", "p_auth_type" "text", "p_credential" "text") TO "anon"; +GRANT ALL ON FUNCTION "public"."add_feed_credentials"("p_subscription_id" "uuid", "p_auth_type" "text", "p_credential" "text") TO "authenticated"; +GRANT ALL ON FUNCTION "public"."add_feed_credentials"("p_subscription_id" "uuid", "p_auth_type" "text", "p_credential" "text") TO "service_role"; + + +-- +-- Name: FUNCTION "check_custom_feed_limit"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."check_custom_feed_limit"() TO "anon"; +GRANT ALL ON FUNCTION "public"."check_custom_feed_limit"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."check_custom_feed_limit"() TO "service_role"; + + +-- +-- Name: FUNCTION "check_folder_limit"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."check_folder_limit"() TO "anon"; +GRANT ALL ON FUNCTION "public"."check_folder_limit"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."check_folder_limit"() TO "service_role"; + + +-- +-- Name: FUNCTION "check_highlight_limit"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."check_highlight_limit"() TO "anon"; +GRANT ALL ON FUNCTION "public"."check_highlight_limit"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."check_highlight_limit"() TO "service_role"; + + +-- +-- Name: FUNCTION "check_muted_keyword_limit"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."check_muted_keyword_limit"() TO "anon"; +GRANT ALL ON FUNCTION "public"."check_muted_keyword_limit"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."check_muted_keyword_limit"() TO "service_role"; + + +-- +-- Name: FUNCTION "check_rate_limit"("p_identifier" "text", "p_limit" integer, "p_window_seconds" integer); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."check_rate_limit"("p_identifier" "text", "p_limit" integer, "p_window_seconds" integer) TO "anon"; +GRANT ALL ON FUNCTION "public"."check_rate_limit"("p_identifier" "text", "p_limit" integer, "p_window_seconds" integer) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."check_rate_limit"("p_identifier" "text", "p_limit" integer, "p_window_seconds" integer) TO "service_role"; + + +-- +-- Name: FUNCTION "check_subscription_limit"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."check_subscription_limit"() TO "anon"; +GRANT ALL ON FUNCTION "public"."check_subscription_limit"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."check_subscription_limit"() TO "service_role"; + + +-- +-- Name: FUNCTION "cleanup_stale_entries"(); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."cleanup_stale_entries"() FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."cleanup_stale_entries"() TO "service_role"; + + +-- +-- Name: FUNCTION "decrement_custom_feed_count"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."decrement_custom_feed_count"() TO "anon"; +GRANT ALL ON FUNCTION "public"."decrement_custom_feed_count"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."decrement_custom_feed_count"() TO "service_role"; + + +-- +-- Name: FUNCTION "decrement_folder_count"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."decrement_folder_count"() TO "anon"; +GRANT ALL ON FUNCTION "public"."decrement_folder_count"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."decrement_folder_count"() TO "service_role"; + + +-- +-- Name: FUNCTION "decrement_highlight_count"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."decrement_highlight_count"() TO "anon"; +GRANT ALL ON FUNCTION "public"."decrement_highlight_count"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."decrement_highlight_count"() TO "service_role"; + + +-- +-- Name: FUNCTION "decrement_muted_keyword_count"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."decrement_muted_keyword_count"() TO "anon"; +GRANT ALL ON FUNCTION "public"."decrement_muted_keyword_count"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."decrement_muted_keyword_count"() TO "service_role"; + + +-- +-- Name: FUNCTION "decrement_subscription_count"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."decrement_subscription_count"() TO "anon"; +GRANT ALL ON FUNCTION "public"."decrement_subscription_count"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."decrement_subscription_count"() TO "service_role"; + + +-- +-- Name: FUNCTION "enqueue_feed_refresh_on_subscribe"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."enqueue_feed_refresh_on_subscribe"() TO "anon"; +GRANT ALL ON FUNCTION "public"."enqueue_feed_refresh_on_subscribe"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."enqueue_feed_refresh_on_subscribe"() TO "service_role"; + + +-- +-- Name: FUNCTION "get_custom_feed"("target_custom_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."get_custom_feed"("target_custom_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone) FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."get_custom_feed"("target_custom_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_custom_feed"("target_custom_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone) TO "service_role"; + + +-- +-- Name: FUNCTION "get_custom_feed_timeline"("p_custom_feed_id" "uuid", "p_result_limit" integer, "p_pagination_cursor" timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."get_custom_feed_timeline"("p_custom_feed_id" "uuid", "p_result_limit" integer, "p_pagination_cursor" timestamp with time zone) FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."get_custom_feed_timeline"("p_custom_feed_id" "uuid", "p_result_limit" integer, "p_pagination_cursor" timestamp with time zone) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_custom_feed_timeline"("p_custom_feed_id" "uuid", "p_result_limit" integer, "p_pagination_cursor" timestamp with time zone) TO "service_role"; + + +-- +-- Name: FUNCTION "get_timeline"("target_folder_id" "uuid", "target_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone, "unread_only" boolean); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."get_timeline"("target_folder_id" "uuid", "target_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone, "unread_only" boolean) FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."get_timeline"("target_folder_id" "uuid", "target_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone, "unread_only" boolean) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_timeline"("target_folder_id" "uuid", "target_feed_id" "uuid", "result_limit" integer, "pagination_cursor" timestamp with time zone, "unread_only" boolean) TO "service_role"; + + +-- +-- Name: FUNCTION "get_unread_counts"(); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."get_unread_counts"() FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."get_unread_counts"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_unread_counts"() TO "service_role"; + + +-- +-- Name: FUNCTION "handle_new_user"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "anon"; +GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "service_role"; + + +-- +-- Name: FUNCTION "mark_all_as_read"("p_feed_id" "uuid", "p_folder_id" "uuid", "p_read_state" boolean); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."mark_all_as_read"("p_feed_id" "uuid", "p_folder_id" "uuid", "p_read_state" boolean) FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."mark_all_as_read"("p_feed_id" "uuid", "p_folder_id" "uuid", "p_read_state" boolean) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."mark_all_as_read"("p_feed_id" "uuid", "p_folder_id" "uuid", "p_read_state" boolean) TO "service_role"; + + +-- +-- Name: FUNCTION "prevent_billing_column_modification"(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."prevent_billing_column_modification"() TO "anon"; +GRANT ALL ON FUNCTION "public"."prevent_billing_column_modification"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."prevent_billing_column_modification"() TO "service_role"; + + +-- +-- Name: FUNCTION "request_feed_refresh"("target_subscription_id" "uuid"); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."request_feed_refresh"("target_subscription_id" "uuid") FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."request_feed_refresh"("target_subscription_id" "uuid") TO "authenticated"; +GRANT ALL ON FUNCTION "public"."request_feed_refresh"("target_subscription_id" "uuid") TO "service_role"; + + +-- +-- Name: FUNCTION "search_entries"("p_query" "text", "p_result_limit" integer); Type: ACL; Schema: public; Owner: postgres +-- + +REVOKE ALL ON FUNCTION "public"."search_entries"("p_query" "text", "p_result_limit" integer) FROM PUBLIC; +GRANT ALL ON FUNCTION "public"."search_entries"("p_query" "text", "p_result_limit" integer) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."search_entries"("p_query" "text", "p_result_limit" integer) TO "service_role"; + + +-- +-- Name: FUNCTION "subscribe_to_feed"("feed_url" "text", "target_folder_id" "uuid", "feed_custom_title" "text", "feed_credential" "text", "feed_auth_type" "text"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."subscribe_to_feed"("feed_url" "text", "target_folder_id" "uuid", "feed_custom_title" "text", "feed_credential" "text", "feed_auth_type" "text") TO "anon"; +GRANT ALL ON FUNCTION "public"."subscribe_to_feed"("feed_url" "text", "target_folder_id" "uuid", "feed_custom_title" "text", "feed_credential" "text", "feed_auth_type" "text") TO "authenticated"; +GRANT ALL ON FUNCTION "public"."subscribe_to_feed"("feed_url" "text", "target_folder_id" "uuid", "feed_custom_title" "text", "feed_credential" "text", "feed_auth_type" "text") TO "service_role"; + + +-- +-- Name: FUNCTION "update_feed_credentials"("p_feed_id" "uuid", "p_auth_type" "text", "p_credential" "text"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."update_feed_credentials"("p_feed_id" "uuid", "p_auth_type" "text", "p_credential" "text") TO "anon"; +GRANT ALL ON FUNCTION "public"."update_feed_credentials"("p_feed_id" "uuid", "p_auth_type" "text", "p_credential" "text") TO "authenticated"; +GRANT ALL ON FUNCTION "public"."update_feed_credentials"("p_feed_id" "uuid", "p_auth_type" "text", "p_credential" "text") TO "service_role"; + + +-- +-- Name: FUNCTION "update_feed_url"("target_feed_id" "uuid", "new_url" "text"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION "public"."update_feed_url"("target_feed_id" "uuid", "new_url" "text") TO "anon"; +GRANT ALL ON FUNCTION "public"."update_feed_url"("target_feed_id" "uuid", "new_url" "text") TO "authenticated"; +GRANT ALL ON FUNCTION "public"."update_feed_url"("target_feed_id" "uuid", "new_url" "text") TO "service_role"; + + +-- +-- Name: FUNCTION "_crypto_aead_det_decrypt"("message" "bytea", "additional" "bytea", "key_id" bigint, "context" "bytea", "nonce" "bytea"); Type: ACL; Schema: vault; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "vault"."_crypto_aead_det_decrypt"("message" "bytea", "additional" "bytea", "key_id" bigint, "context" "bytea", "nonce" "bytea") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "vault"."_crypto_aead_det_decrypt"("message" "bytea", "additional" "bytea", "key_id" bigint, "context" "bytea", "nonce" "bytea") TO "service_role"; + + +-- +-- Name: FUNCTION "create_secret"("new_secret" "text", "new_name" "text", "new_description" "text", "new_key_id" "uuid"); Type: ACL; Schema: vault; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "vault"."create_secret"("new_secret" "text", "new_name" "text", "new_description" "text", "new_key_id" "uuid") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "vault"."create_secret"("new_secret" "text", "new_name" "text", "new_description" "text", "new_key_id" "uuid") TO "service_role"; + + +-- +-- Name: FUNCTION "update_secret"("secret_id" "uuid", "new_secret" "text", "new_name" "text", "new_description" "text", "new_key_id" "uuid"); Type: ACL; Schema: vault; Owner: supabase_admin +-- + +-- GRANT ALL ON FUNCTION "vault"."update_secret"("secret_id" "uuid", "new_secret" "text", "new_name" "text", "new_description" "text", "new_key_id" "uuid") TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON FUNCTION "vault"."update_secret"("secret_id" "uuid", "new_secret" "text", "new_name" "text", "new_description" "text", "new_key_id" "uuid") TO "service_role"; + + +-- +-- Name: TABLE "job"; Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT SELECT ON TABLE "cron"."job" TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: TABLE "job_run_details"; Type: ACL; Schema: cron; Owner: supabase_admin +-- + +-- GRANT ALL ON TABLE "cron"."job_run_details" TO "postgres" WITH GRANT OPTION; + + +-- +-- Name: TABLE "pg_stat_statements"; Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON TABLE "extensions"."pg_stat_statements" FROM "postgres"; +-- GRANT ALL ON TABLE "extensions"."pg_stat_statements" TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON TABLE "extensions"."pg_stat_statements" TO "dashboard_user"; + + +-- +-- Name: TABLE "pg_stat_statements_info"; Type: ACL; Schema: extensions; Owner: postgres +-- + +-- REVOKE ALL ON TABLE "extensions"."pg_stat_statements_info" FROM "postgres"; +-- GRANT ALL ON TABLE "extensions"."pg_stat_statements_info" TO "postgres" WITH GRANT OPTION; +-- GRANT ALL ON TABLE "extensions"."pg_stat_statements_info" TO "dashboard_user"; + + +-- +-- Name: TABLE "decrypted_key"; Type: ACL; Schema: pgsodium; Owner: postgres +-- + +-- GRANT ALL ON TABLE "pgsodium"."decrypted_key" TO "pgsodium_keyholder"; + + +-- +-- Name: TABLE "masking_rule"; Type: ACL; Schema: pgsodium; Owner: supabase_admin +-- + +-- GRANT ALL ON TABLE "pgsodium"."masking_rule" TO "pgsodium_keyholder"; + + +-- +-- Name: TABLE "mask_columns"; Type: ACL; Schema: pgsodium; Owner: supabase_admin +-- + +-- GRANT ALL ON TABLE "pgsodium"."mask_columns" TO "pgsodium_keyholder"; + + +-- +-- Name: TABLE "api_keys"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."api_keys" TO "anon"; +GRANT ALL ON TABLE "public"."api_keys" TO "authenticated"; +GRANT ALL ON TABLE "public"."api_keys" TO "service_role"; + + +-- +-- Name: TABLE "custom_feeds"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."custom_feeds" TO "service_role"; +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE "public"."custom_feeds" TO "authenticated"; + + +-- +-- Name: TABLE "entries"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."entries" TO "service_role"; +GRANT SELECT ON TABLE "public"."entries" TO "authenticated"; + + +-- +-- Name: TABLE "feeds"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."feeds" TO "service_role"; +GRANT SELECT ON TABLE "public"."feeds" TO "authenticated"; + + +-- +-- Name: TABLE "folders"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."folders" TO "service_role"; +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE "public"."folders" TO "authenticated"; + + +-- +-- Name: TABLE "muted_keywords"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."muted_keywords" TO "service_role"; +GRANT SELECT,INSERT,DELETE ON TABLE "public"."muted_keywords" TO "authenticated"; + + +-- +-- Name: TABLE "rate_limits"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."rate_limits" TO "anon"; +GRANT ALL ON TABLE "public"."rate_limits" TO "authenticated"; +GRANT ALL ON TABLE "public"."rate_limits" TO "service_role"; + + +-- +-- Name: TABLE "shared_entries"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."shared_entries" TO "service_role"; +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE "public"."shared_entries" TO "authenticated"; + + +-- +-- Name: TABLE "subscriptions"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."subscriptions" TO "service_role"; +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE "public"."subscriptions" TO "authenticated"; + + +-- +-- Name: TABLE "user_entry_states"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."user_entry_states" TO "service_role"; +GRANT SELECT,INSERT,UPDATE ON TABLE "public"."user_entry_states" TO "authenticated"; + + +-- +-- Name: TABLE "user_highlights"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."user_highlights" TO "service_role"; +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE "public"."user_highlights" TO "authenticated"; + + +-- +-- Name: TABLE "user_profiles"; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE "public"."user_profiles" TO "service_role"; +GRANT SELECT,UPDATE ON TABLE "public"."user_profiles" TO "authenticated"; + + +-- +-- Name: TABLE "secrets"; Type: ACL; Schema: vault; Owner: supabase_admin +-- + +-- GRANT SELECT,REFERENCES,DELETE,TRUNCATE ON TABLE "vault"."secrets" TO "postgres" WITH GRANT OPTION; +-- GRANT SELECT,DELETE ON TABLE "vault"."secrets" TO "service_role"; + + +-- +-- Name: TABLE "decrypted_secrets"; Type: ACL; Schema: vault; Owner: supabase_admin +-- + +-- GRANT SELECT,REFERENCES,DELETE,TRUNCATE ON TABLE "vault"."decrypted_secrets" TO "postgres" WITH GRANT OPTION; +-- GRANT SELECT,DELETE ON TABLE "vault"."decrypted_secrets" TO "service_role"; + + +-- +-- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: postgres +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "service_role"; + + +-- +-- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin +-- + +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "postgres"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "anon"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "authenticated"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "service_role"; + + +-- +-- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: postgres +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "service_role"; + + +-- +-- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin +-- + +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "postgres"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "anon"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "authenticated"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "service_role"; + + +-- +-- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: postgres +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "service_role"; + + +-- +-- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin +-- + +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON TABLES TO "postgres"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON TABLES TO "anon"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON TABLES TO "authenticated"; +-- ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin" IN SCHEMA "public" GRANT ALL ON TABLES TO "service_role"; + + +-- +-- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- + +-- CREATE EVENT TRIGGER "issue_graphql_placeholder" ON "sql_drop" +-- WHEN TAG IN ('DROP EXTENSION') +-- EXECUTE FUNCTION "extensions"."set_graphql_placeholder"(); + + +-- ALTER EVENT TRIGGER "issue_graphql_placeholder" OWNER TO "supabase_admin"; + +-- +-- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- + +-- CREATE EVENT TRIGGER "issue_pg_cron_access" ON "ddl_command_end" +-- WHEN TAG IN ('CREATE EXTENSION') +-- EXECUTE FUNCTION "extensions"."grant_pg_cron_access"(); + + +-- ALTER EVENT TRIGGER "issue_pg_cron_access" OWNER TO "supabase_admin"; + +-- +-- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- + +-- CREATE EVENT TRIGGER "issue_pg_graphql_access" ON "ddl_command_end" +-- WHEN TAG IN ('CREATE FUNCTION') +-- EXECUTE FUNCTION "extensions"."grant_pg_graphql_access"(); + + +-- ALTER EVENT TRIGGER "issue_pg_graphql_access" OWNER TO "supabase_admin"; + +-- +-- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- + +-- CREATE EVENT TRIGGER "issue_pg_net_access" ON "ddl_command_end" +-- WHEN TAG IN ('CREATE EXTENSION') +-- EXECUTE FUNCTION "extensions"."grant_pg_net_access"(); + + +-- ALTER EVENT TRIGGER "issue_pg_net_access" OWNER TO "supabase_admin"; + +-- +-- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- + +-- CREATE EVENT TRIGGER "pgrst_ddl_watch" ON "ddl_command_end" +-- EXECUTE FUNCTION "extensions"."pgrst_ddl_watch"(); + + +-- ALTER EVENT TRIGGER "pgrst_ddl_watch" OWNER TO "supabase_admin"; + +-- +-- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin +-- + +-- CREATE EVENT TRIGGER "pgrst_drop_watch" ON "sql_drop" +-- EXECUTE FUNCTION "extensions"."pgrst_drop_watch"(); + + +-- ALTER EVENT TRIGGER "pgrst_drop_watch" OWNER TO "supabase_admin"; + +-- +-- PostgreSQL database dump complete +-- + +-- \unrestrict 3Un8h9VlAyPuVfPugYV7LM7XGnkxI3KC1nPU1aarC0scGxlDzBQB81bgO7tvPsv - RETURN jsonb_build_object( - 'success', v_current_count <= p_limit, - 'remaining', greatest(p_limit - v_current_count, 0) - ); -END; -$function$; |