- Create trusted_origins table for DB-backed origin management - Add API routes for CRUD operations on trusted origins - Add tabbed interface on /users page with Users and Trusted Origins tabs - Seeds default trusted origins (cannaiq.co, findadispo.com, findagram.co, etc.) - Fix TypeScript error in WorkersDashboard fingerprint type 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
93 lines
4.2 KiB
PL/PgSQL
93 lines
4.2 KiB
PL/PgSQL
-- Migration: 110_trusted_origins.sql
|
|
-- Description: Trusted origins for API access without token
|
|
-- Created: 2024-12-14
|
|
--
|
|
-- Manages which domains, IPs, and patterns can access the API without a Bearer token.
|
|
-- Used by auth middleware to grant 'internal' role to trusted requests.
|
|
|
|
-- ============================================================
|
|
-- TRUSTED ORIGINS TABLE
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS trusted_origins (
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
-- Origin identification
|
|
name VARCHAR(100) NOT NULL, -- Friendly name (e.g., "CannaIQ Production")
|
|
origin_type VARCHAR(20) NOT NULL, -- 'domain', 'ip', or 'pattern'
|
|
origin_value VARCHAR(255) NOT NULL, -- The actual value to match
|
|
|
|
-- Metadata
|
|
description TEXT, -- Optional notes
|
|
active BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Tracking
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by INTEGER REFERENCES users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT valid_origin_type CHECK (origin_type IN ('domain', 'ip', 'pattern')),
|
|
UNIQUE(origin_type, origin_value)
|
|
);
|
|
|
|
-- Index for active lookups (used by auth middleware)
|
|
CREATE INDEX IF NOT EXISTS idx_trusted_origins_active
|
|
ON trusted_origins(active) WHERE active = TRUE;
|
|
|
|
-- Updated at trigger
|
|
CREATE OR REPLACE FUNCTION update_trusted_origins_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS trusted_origins_updated_at ON trusted_origins;
|
|
CREATE TRIGGER trusted_origins_updated_at
|
|
BEFORE UPDATE ON trusted_origins
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_trusted_origins_updated_at();
|
|
|
|
-- ============================================================
|
|
-- SEED DEFAULT TRUSTED ORIGINS
|
|
-- These match the hardcoded fallbacks in middleware.ts
|
|
-- ============================================================
|
|
|
|
-- Production domains
|
|
INSERT INTO trusted_origins (name, origin_type, origin_value, description) VALUES
|
|
('CannaIQ Production', 'domain', 'https://cannaiq.co', 'Main CannaIQ dashboard'),
|
|
('CannaIQ Production (www)', 'domain', 'https://www.cannaiq.co', 'Main CannaIQ dashboard with www'),
|
|
('FindADispo Production', 'domain', 'https://findadispo.com', 'Consumer dispensary finder'),
|
|
('FindADispo Production (www)', 'domain', 'https://www.findadispo.com', 'Consumer dispensary finder with www'),
|
|
('Findagram Production', 'domain', 'https://findagram.co', 'Instagram-style cannabis discovery'),
|
|
('Findagram Production (www)', 'domain', 'https://www.findagram.co', 'Instagram-style cannabis discovery with www')
|
|
ON CONFLICT (origin_type, origin_value) DO NOTHING;
|
|
|
|
-- Wildcard patterns
|
|
INSERT INTO trusted_origins (name, origin_type, origin_value, description) VALUES
|
|
('CannaBrands Subdomains', 'pattern', '^https://.*\\.cannabrands\\.app$', 'All *.cannabrands.app subdomains'),
|
|
('CannaIQ Subdomains', 'pattern', '^https://.*\\.cannaiq\\.co$', 'All *.cannaiq.co subdomains')
|
|
ON CONFLICT (origin_type, origin_value) DO NOTHING;
|
|
|
|
-- Local development
|
|
INSERT INTO trusted_origins (name, origin_type, origin_value, description) VALUES
|
|
('Local API', 'domain', 'http://localhost:3010', 'Local backend API'),
|
|
('Local Admin', 'domain', 'http://localhost:8080', 'Local admin dashboard'),
|
|
('Local Vite Dev', 'domain', 'http://localhost:5173', 'Vite dev server')
|
|
ON CONFLICT (origin_type, origin_value) DO NOTHING;
|
|
|
|
-- Trusted IPs (localhost)
|
|
INSERT INTO trusted_origins (name, origin_type, origin_value, description) VALUES
|
|
('Localhost IPv4', 'ip', '127.0.0.1', 'Local machine'),
|
|
('Localhost IPv6', 'ip', '::1', 'Local machine IPv6'),
|
|
('Localhost IPv6 Mapped', 'ip', '::ffff:127.0.0.1', 'IPv6-mapped IPv4 localhost')
|
|
ON CONFLICT (origin_type, origin_value) DO NOTHING;
|
|
|
|
-- ============================================================
|
|
-- COMMENTS
|
|
-- ============================================================
|
|
COMMENT ON TABLE trusted_origins IS 'Domains, IPs, and patterns that can access API without token';
|
|
COMMENT ON COLUMN trusted_origins.origin_type IS 'domain = exact URL match, ip = IP address, pattern = regex pattern';
|
|
COMMENT ON COLUMN trusted_origins.origin_value IS 'For domain: full URL. For ip: IP address. For pattern: regex string';
|