keys-for-all/keys-api/migrations/001_initial_schema.sql
2025-07-22 18:27:21 -07:00

159 lines
No EOL
5.3 KiB
PL/PgSQL

-- Initial database schema for Keys for All
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Apps table
CREATE TABLE IF NOT EXISTS apps (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
app_id VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
api_key VARCHAR(255) UNIQUE NOT NULL,
webhook_url VARCHAR(500),
settings JSONB DEFAULT '{}',
role VARCHAR(50) DEFAULT 'app',
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_apps_api_key ON apps(api_key);
-- Keys table
CREATE TABLE IF NOT EXISTS keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key VARCHAR(19) UNIQUE NOT NULL,
app_id UUID REFERENCES apps(id),
level INTEGER NOT NULL DEFAULT 1,
type VARCHAR(50) NOT NULL DEFAULT 'purchase',
status VARCHAR(50) NOT NULL DEFAULT 'unused',
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP WITH TIME ZONE,
first_activated_at TIMESTAMP WITH TIME ZONE,
last_used_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_keys_key ON keys(key);
CREATE INDEX idx_keys_status ON keys(status);
CREATE INDEX idx_keys_app_id ON keys(app_id);
-- Activations table
CREATE TABLE IF NOT EXISTS activations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key_id UUID REFERENCES keys(id),
app_id UUID REFERENCES apps(id),
device_id VARCHAR(255),
device_info JSONB DEFAULT '{}',
ip_address INET,
activated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
last_seen_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_activations_key_id ON activations(key_id);
CREATE INDEX idx_activations_device_id ON activations(device_id);
-- Purchases table
CREATE TABLE IF NOT EXISTS purchases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
app_id UUID REFERENCES apps(id),
transaction_id VARCHAR(255) UNIQUE NOT NULL,
product_id VARCHAR(255) NOT NULL,
keys_granted INTEGER NOT NULL,
price DECIMAL(10, 2),
currency VARCHAR(3) DEFAULT 'USD',
receipt_data TEXT,
verified_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
metadata JSONB DEFAULT '{}'
);
CREATE INDEX idx_purchases_transaction_id ON purchases(transaction_id);
CREATE INDEX idx_purchases_app_id ON purchases(app_id);
-- Purchase keys link table
CREATE TABLE IF NOT EXISTS purchase_keys (
purchase_id UUID REFERENCES purchases(id),
key_id UUID REFERENCES keys(id),
PRIMARY KEY (purchase_id, key_id)
);
-- Shares table
CREATE TABLE IF NOT EXISTS shares (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
share_code VARCHAR(50) UNIQUE NOT NULL,
from_key_id UUID REFERENCES keys(id),
to_key_id UUID REFERENCES keys(id),
keys_shared INTEGER NOT NULL DEFAULT 1,
message TEXT,
status VARCHAR(50) DEFAULT 'pending',
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP WITH TIME ZONE,
claimed_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_shares_code ON shares(share_code);
CREATE INDEX idx_shares_status ON shares(status);
-- Community pool table
CREATE TABLE IF NOT EXISTS community_pool (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key_id UUID REFERENCES keys(id) UNIQUE,
donor_message TEXT,
anonymous BOOLEAN DEFAULT true,
donated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
claimed_at TIMESTAMP WITH TIME ZONE,
claimed_by_request_id UUID
);
CREATE INDEX idx_pool_claimed ON community_pool(claimed_at) WHERE claimed_at IS NULL;
-- Community requests table
CREATE TABLE IF NOT EXISTS community_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
app_id UUID REFERENCES apps(id),
email VARCHAR(255) NOT NULL,
reason TEXT,
status VARCHAR(50) DEFAULT 'pending',
ip_address INET,
requested_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
fulfilled_at TIMESTAMP WITH TIME ZONE,
key_id UUID REFERENCES keys(id)
);
CREATE INDEX idx_requests_status ON community_requests(status);
CREATE INDEX idx_requests_email ON community_requests(email);
-- Analytics events table
CREATE TABLE IF NOT EXISTS analytics_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
app_id UUID REFERENCES apps(id),
event_type VARCHAR(50) NOT NULL,
key_id UUID REFERENCES keys(id),
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_analytics_app_id ON analytics_events(app_id);
CREATE INDEX idx_analytics_event_type ON analytics_events(event_type);
CREATE INDEX idx_analytics_created_at ON analytics_events(created_at);
-- Rate limits table
CREATE TABLE IF NOT EXISTS rate_limits (
id VARCHAR(255) PRIMARY KEY,
count INTEGER NOT NULL DEFAULT 0,
window_start TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_rate_limits_window ON rate_limits(window_start);
-- Updated timestamp trigger
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_apps_updated_at BEFORE UPDATE ON apps
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();