PromptBase
Upgrade
Close icon
General
Home
Marketplace
Create
Hire
Login
Chat
Sell
Explore

Prompt Details

Model
(gemini-3.0-pro)
Token size
376
Example input
[Insert App/Web Concept]: A platform where experts can publish exclusive technical articles. Features include: tiered monthly subscriptions (Free/Pro), individual pay-per-view for premium posts, a bookmarking system for readers, and a creator dashboard to track monthly revenue.
Example output
This schema is designed for a high-scale technical publishing platform. It leverages PostgreSQL's robust relational features and Supabase's native security model (RLS) to handle complex access control—specifically the intersection of **tiered subscriptions** and **one-time purchases (PPV)**. --- ## 1. ENTITY RELATIONSHIP MAP ```mermaid erDiagram profiles ||--o{ articles : "authors" profiles ||--o{ subscriptions : "subscriber" profiles ||--o{ purchases : "buyer" profiles ||--o{ bookmarks : "saves" articles ||--o{ purchases : "sold as PPV" articles ||--o{ bookmarks : "bookmarked by" profiles { uuid id PK string username text bio enum tier "free | pro" } articles { uuid id PK uuid author_id FK text content enum access_level "free | pro | ppv" decimal price } ``` --- ## 2. SQL DDL SCRIPT This script sets up the core architecture, including custom types and automated timestamp management. ```sql -- 1. EXTENSIONS & TYPES create extension if not exists "uuid-ossp"; create type membership_tier as enum ('free', 'pro'); create type article_access as enum ('free', 'pro', 'ppv'); create type publish_status as enum ('draft', 'published', 'archived'); -- 2. TABLES -- Profiles (Extends Supabase Auth) create table profiles ( id uuid references auth.users on delete cascade primary key, username text unique not null, full_name text, avatar_url text, bio text, website text, tier membership_tier default 'free' not null, is_expert boolean default false, created_at timestamptz default now(), updated_at timestamptz default now() ); -- Articles create table articles ( id uuid default uuid_generate_v4() primary key, author_id uuid references profiles(id) on delete cascade not null, title text not null, slug text unique not null, excerpt text, content text, -- Use JSONB if using a block editor like TipTap status publish_status default 'draft' not null, access_level article_access default 'free' not null, price numeric(10, 2) default 0.00 check (price >= 0), created_at timestamptz default now(), updated_at timestamptz default now() ); -- Individual Purchases (Pay-Per-View) create table purchases ( id uuid default uuid_generate_v4() primary key, user_id uuid references profiles(id) on delete cascade not null, article_id uuid references articles(id) on delete cascade not null, amount_paid numeric(10, 2) not null, purchased_at timestamptz default now(), -- Ensure a user can't buy the same article twice unique(user_id, article_id) ); -- Bookmarks create table bookmarks ( user_id uuid references profiles(id) on delete cascade not null, article_id uuid references articles(id) on delete cascade not null, created_at timestamptz default now(), primary key (user_id, article_id) ); -- 3. FUNCTIONS & TRIGGERS -- Automatically update timestamps create or replace function update_updated_at_column() returns trigger as $$ begin new.updated_at = now(); return new; end; $$ language plpgsql; create trigger update_profiles_modtime before update on profiles for each row execute procedure update_updated_at_column(); create trigger update_articles_modtime before update on articles for each row execute procedure update_updated_at_column(); -- Automatically create profile on signup create or replace function public.handle_new_user() returns trigger as $$ begin insert into public.profiles (id, username, full_name, avatar_url) values (new.id, new.raw_user_meta_data->>'username', new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url'); return new; end; $$ language plpgsql security definer; create trigger on_auth_user_created after insert on auth.users for each row execute procedure public.handle_new_user(); ``` --- ## 3. RLS SECURITY POLICIES This is the most critical part: ensuring users can only see content they have paid for or have the right subscription tier for. ```sql -- Enable RLS alter table profiles enable row level security; alter table articles enable row level security; alter table purchases enable row level security; alter table bookmarks enable row level security; -- PROFILES: Public can view, only owner can edit create policy "Public profiles are viewable by everyone" on profiles for select using (true); create policy "Users can update own profile" on profiles for update using (auth.uid() = id); -- ARTICLES: The Core Logic -- 1. Anyone can see the metadata of published articles create policy "Anyone can view published article metadata" on articles for select using (status = 'published'); -- 2. Authors can do everything with their own articles create policy "Authors have full control of own articles" on articles for all using (auth.uid() = author_id); -- PURCHASES: Only the buyer can see their purchase history create policy "Users can view own purchases" on purchases for select using (auth.uid() = user_id); -- BOOKMARKS: Private to user create policy "Users can manage own bookmarks" on bookmarks for all using (auth.uid() = user_id); -- ACCESS CONTROL FUNCTION (For Article Content) -- Use this in your API calls to determine if content should be returned create or replace function can_access_article(article_id uuid) returns boolean as $$ declare v_access_level article_access; v_author_id uuid; v_user_tier membership_tier; v_has_purchased boolean; begin -- Get article details select access_level, author_id into v_access_level, v_author_id from articles where id = article_id; -- Author always has access if auth.uid() = v_author_id then return true; end if; -- Free content if v_access_level = 'free' then return true; end if; -- Pro content (Check user tier) if v_access_level = 'pro' then select tier into v_user_tier from profiles where id = auth.uid(); if v_user_tier = 'pro' then return true; end if; end if; -- PPV content (Check purchases) if v_access_level = 'ppv' then select exists(select 1 from purchases where user_id = auth.uid() and article_id = article_id) into v_has_purchased; if v_has_purchased then return true; end if; end if; return false; end; $$ language plpgsql security definer; ``` --- ## 4. IMPLEMENTATION NOTES * **Content Masking:** Do not return the `content` column in your initial "list articles" query. Only fetch the content after calling the `can_access_article` function or checking the user's status on the client side to save bandwidth and improve security. * **Stripe Integration:** Use **Supabase Edge Functions** to handle Stripe Webhooks. When a `checkout.session.completed` event occurs for a subscription, update `profiles.tier`. For a one-time payment, insert a record into the `purchases` table. * **Creator Dashboard:** To calculate revenue, query the `purchases` table: ```sql select article_id, sum(amount_paid) from purchases where article_id in (select id from articles where author_id = auth.uid()) group by article_id; ``` * **Performance:** I have included a `slug` column. Ensure you add an index on `slug` and `author_id` if you expect millions of articles, as these will be your primary lookup keys. **Would you like me to generate the Stripe Webhook logic (Edge Function) to handle these subscription and PPV updates?**
♊️ Gemini

Supabase Postgresql Architect

Add to Cart
Instant accessInstant access
Usage rightsCommercial use
Money-back guaranteeMoney‑back
By purchasing this prompt, you agree to our terms of service
GEMINI-3.0-PRO
Tested icon
Guide icon
4 examples icon
Free credits icon
The Supabase & PostgreSQL Architect is a professional-grade engine that converts your side-hustle idea into a bulletproof, scalable database schema. Whether you’re building a niche marketplace or a subscription service, this engine ensures your backend is architecturally sound and ready to scale.
...more
Added over 1 month ago
Report
Browse Marketplace