-- PostgreSQL schema scaffold for dvv tunnel SaaS platform. create extension if not exists pgcrypto; create table if not exists users ( id uuid primary key default gen_random_uuid(), email text unique not null, password_hash text, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table if not exists plans ( id text primary key, name text not null, max_tunnels integer not null, custom_subdomain boolean not null default false, idle_timeout_seconds integer, bandwidth_cap_kbps integer, priority_routing boolean not null default false, created_at timestamptz not null default now() ); create table if not exists subscriptions ( id uuid primary key default gen_random_uuid(), user_id uuid not null references users(id) on delete cascade, plan_id text not null references plans(id), provider text not null default 'stripe', provider_customer_id text, provider_subscription_id text, status text not null, current_period_end timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create index if not exists subscriptions_user_id_idx on subscriptions(user_id); create unique index if not exists subscriptions_provider_subscription_id_uidx on subscriptions(provider_subscription_id) where provider_subscription_id is not null; create table if not exists tunnels ( id uuid primary key default gen_random_uuid(), user_id uuid not null references users(id) on delete cascade, region text not null check (region in ('eu', 'us', 'asia')), subdomain text not null, custom_domain boolean not null default false, active boolean not null default true, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (region, subdomain) ); create index if not exists tunnels_user_id_idx on tunnels(user_id); create table if not exists usage_rollups_hourly ( user_id uuid not null references users(id) on delete cascade, hour_bucket timestamptz not null, bytes_in bigint not null default 0, bytes_out bigint not null default 0, player_connections integer not null default 0, primary key (user_id, hour_bucket) ); insert into plans (id, name, max_tunnels, custom_subdomain, idle_timeout_seconds, bandwidth_cap_kbps, priority_routing) values ('free', 'Free', 1, false, 900, 512, false), ('pro', 'Pro', 5, true, null, 8192, true), ('team', 'Team', 25, true, null, null, true) on conflict (id) do nothing;