2025-12-29 17:13:35 -08:00
|
|
|
-- SEO Database Initialization
|
|
|
|
|
|
|
|
|
|
CREATE SCHEMA IF NOT EXISTS seo;
|
|
|
|
|
|
2025-12-30 17:51:03 -08:00
|
|
|
-- Domain configurations
|
2025-12-29 17:13:35 -08:00
|
|
|
CREATE TABLE IF NOT EXISTS seo.domain_configs (
|
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
|
domain VARCHAR(255) NOT NULL UNIQUE,
|
|
|
|
|
default_locale VARCHAR(10) NOT NULL DEFAULT 'en',
|
|
|
|
|
supported_locales TEXT[] NOT NULL DEFAULT ARRAY['en'],
|
|
|
|
|
site_name VARCHAR(255) NOT NULL,
|
|
|
|
|
twitter_handle VARCHAR(100),
|
|
|
|
|
default_og_image TEXT,
|
|
|
|
|
auto_generate BOOLEAN DEFAULT TRUE,
|
2025-12-30 17:51:03 -08:00
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
2025-12-29 17:13:35 -08:00
|
|
|
);
|
|
|
|
|
|
2025-12-30 17:51:03 -08:00
|
|
|
-- Page SEO configurations
|
2025-12-29 17:13:35 -08:00
|
|
|
CREATE TABLE IF NOT EXISTS seo.page_configs (
|
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
|
domain_id INTEGER NOT NULL REFERENCES seo.domain_configs(id) ON DELETE CASCADE,
|
|
|
|
|
path VARCHAR(500) NOT NULL,
|
|
|
|
|
page_type VARCHAR(100) NOT NULL DEFAULT 'page',
|
|
|
|
|
variables JSONB DEFAULT '{}',
|
2025-12-30 17:51:03 -08:00
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
2025-12-29 17:13:35 -08:00
|
|
|
UNIQUE(domain_id, path)
|
|
|
|
|
);
|
|
|
|
|
|
2025-12-30 17:51:03 -08:00
|
|
|
-- Metadata overrides per locale
|
2025-12-29 17:13:35 -08:00
|
|
|
CREATE TABLE IF NOT EXISTS seo.metadata_overrides (
|
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
|
page_config_id INTEGER NOT NULL REFERENCES seo.page_configs(id) ON DELETE CASCADE,
|
|
|
|
|
locale VARCHAR(10) NOT NULL,
|
|
|
|
|
title VARCHAR(255),
|
|
|
|
|
description TEXT,
|
|
|
|
|
keywords TEXT[],
|
|
|
|
|
og_title VARCHAR(255),
|
|
|
|
|
og_description TEXT,
|
|
|
|
|
og_image TEXT,
|
|
|
|
|
og_type VARCHAR(50) DEFAULT 'website',
|
|
|
|
|
canonical_url TEXT,
|
|
|
|
|
robots VARCHAR(100) DEFAULT 'index,follow',
|
2025-12-30 17:51:03 -08:00
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
2025-12-29 17:13:35 -08:00
|
|
|
UNIQUE(page_config_id, locale)
|
|
|
|
|
);
|
|
|
|
|
|
2025-12-30 17:51:03 -08:00
|
|
|
-- Generated SEO cache
|
2025-12-29 17:13:35 -08:00
|
|
|
CREATE TABLE IF NOT EXISTS seo.generated_cache (
|
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
|
domain VARCHAR(255) NOT NULL,
|
|
|
|
|
path VARCHAR(500) NOT NULL,
|
|
|
|
|
locale VARCHAR(10) NOT NULL,
|
|
|
|
|
metadata JSONB NOT NULL,
|
|
|
|
|
source VARCHAR(50) NOT NULL DEFAULT 'generated',
|
|
|
|
|
truth_validation JSONB,
|
2025-12-30 17:51:03 -08:00
|
|
|
generated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
|
expires_at TIMESTAMPTZ DEFAULT NOW() + INTERVAL '24 hours',
|
2025-12-29 17:13:35 -08:00
|
|
|
UNIQUE(domain, path, locale)
|
|
|
|
|
);
|
|
|
|
|
|
2025-12-30 17:51:03 -08:00
|
|
|
-- Indexes
|
2025-12-29 17:13:35 -08:00
|
|
|
CREATE INDEX IF NOT EXISTS idx_domain_configs_domain ON seo.domain_configs(domain);
|
2025-12-30 17:51:03 -08:00
|
|
|
CREATE INDEX IF NOT EXISTS idx_page_configs_lookup ON seo.page_configs(domain_id, path);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_metadata_page_locale ON seo.metadata_overrides(page_config_id, locale);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_cache_lookup ON seo.generated_cache(domain, path, locale);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_cache_expiry ON seo.generated_cache(expires_at);
|
2025-12-29 17:13:35 -08:00
|
|
|
|
2025-12-30 17:51:03 -08:00
|
|
|
-- Permissions
|
|
|
|
|
GRANT ALL PRIVILEGES ON SCHEMA seo TO lilith;
|
|
|
|
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA seo TO lilith;
|
|
|
|
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA seo TO lilith;
|
|
|
|
|
|
|
|
|
|
DO $$ BEGIN RAISE NOTICE 'SEO database initialized'; END $$;
|