Includes: API (Express/TypeScript), Astro site, Python workers, document generators, FCC compliance tools, Canada CRTC formation, Ansible infrastructure, and deployment scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
84 lines
3 KiB
PL/PgSQL
84 lines
3 KiB
PL/PgSQL
-- 001_core_tables.sql
|
|
-- Core tables for Performance West API: subscribers, tickets, quotes, orders.
|
|
-- Run: psql $DATABASE_URL < migrations/001_core_tables.sql
|
|
|
|
BEGIN;
|
|
|
|
-- Mailing list subscribers
|
|
CREATE TABLE IF NOT EXISTS subscribers (
|
|
id SERIAL PRIMARY KEY,
|
|
email TEXT NOT NULL UNIQUE,
|
|
name TEXT,
|
|
company TEXT,
|
|
consent_text TEXT NOT NULL,
|
|
consent_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
ip_address TEXT,
|
|
confirmed BOOLEAN DEFAULT FALSE,
|
|
unsubscribed BOOLEAN DEFAULT FALSE,
|
|
source TEXT DEFAULT 'website',
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_subscribers_email ON subscribers(email);
|
|
CREATE INDEX IF NOT EXISTS idx_subscribers_created ON subscribers(created_at DESC);
|
|
|
|
-- Support tickets (local fallback when Zammad is unavailable)
|
|
CREATE TABLE IF NOT EXISTS tickets (
|
|
id SERIAL PRIMARY KEY,
|
|
category TEXT NOT NULL CHECK (category IN (
|
|
'question', 'support', 'issue', 'service_request', 'quote'
|
|
)),
|
|
subject TEXT NOT NULL,
|
|
message TEXT NOT NULL,
|
|
email TEXT,
|
|
name TEXT,
|
|
page TEXT,
|
|
ip_address TEXT,
|
|
zammad_ticket_id TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_category ON tickets(category);
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_created ON tickets(created_at DESC);
|
|
|
|
-- Quote requests (for custom-priced services)
|
|
CREATE TABLE IF NOT EXISTS quotes (
|
|
id SERIAL PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
email TEXT NOT NULL,
|
|
company TEXT,
|
|
phone TEXT,
|
|
service_slug TEXT NOT NULL,
|
|
details TEXT,
|
|
status TEXT DEFAULT 'pending' CHECK (status IN (
|
|
'pending', 'sent', 'accepted', 'declined', 'expired'
|
|
)),
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
updated_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_quotes_status ON quotes(status);
|
|
CREATE INDEX IF NOT EXISTS idx_quotes_email ON quotes(email);
|
|
|
|
-- Service orders (fixed-price and accepted quotes)
|
|
CREATE TABLE IF NOT EXISTS orders (
|
|
id SERIAL PRIMARY KEY,
|
|
order_number TEXT UNIQUE NOT NULL,
|
|
quote_id INTEGER REFERENCES quotes(id),
|
|
service_slug TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
email TEXT NOT NULL,
|
|
company TEXT,
|
|
status TEXT DEFAULT 'received' CHECK (status IN (
|
|
'received', 'processing', 'review', 'delivered', 'cancelled'
|
|
)),
|
|
amount_cents INTEGER,
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
delivered_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
|
|
CREATE INDEX IF NOT EXISTS idx_orders_number ON orders(order_number);
|
|
CREATE INDEX IF NOT EXISTS idx_orders_email ON orders(email);
|
|
|
|
COMMIT;
|