Production-ready deployment package with database schema and server setup guide. NEW FILES: 1. DEPLOYMENT_GUIDE.md - Complete server deployment instructions 2. database/init.sql - PostgreSQL database initialization script DEPLOYMENT_GUIDE.md includes: ✓ Server deployment steps (rsync/scp) ✓ PostgreSQL setup (Docker or native) ✓ Python environment setup ✓ Configuration guide ✓ Cron job examples ✓ OAuth2 vs mTLS setup instructions ✓ Testing checklist ✓ Troubleshooting guide ✓ Production checklist ✓ Security hardening steps database/init.sql includes: ✓ Complete schema with all 35 columns in master_assets ✓ All 4 tables (master_assets, derivative_assets, asset_events, workflow_state) ✓ Campaign relationship fields (global_master_campaign_id, global_master_folder_id, local_campaign_id) ✓ JSONB full_metadata column ✓ 12 indexes for performance ✓ 4 triggers (auto-update timestamps, event logging) ✓ 2 helper functions ✓ Proper permissions for ferrero_user KEY FEATURES: - Self-contained deployment - Works with Docker or native PostgreSQL - Includes all schema additions from this session - OAuth2 and mTLS configuration documented - Cron job templates provided - Security checklist included DEPLOYMENT: 1. Copy Python-Version/ folder to server 2. Run database init.sql 3. Install Python dependencies 4. Update .env for server 5. Test connections 6. Set up cron jobs Everything needed for production deployment is included. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
308 lines
10 KiB
PL/PgSQL
308 lines
10 KiB
PL/PgSQL
-- Ferrero Asset Tracking Database - Initialization Script
|
|
-- PostgreSQL 15+
|
|
-- Last Updated: November 5, 2025
|
|
|
|
\echo 'Creating Ferrero Asset Tracking database structure...'
|
|
|
|
-- Create extensions
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- For text search
|
|
|
|
-- Set timezone
|
|
SET timezone = 'UTC';
|
|
|
|
\echo 'Creating tables...'
|
|
|
|
-- ============================================================================
|
|
-- Table: master_assets
|
|
-- Purpose: Stores master assets downloaded from DAM with tracking IDs
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS master_assets (
|
|
-- Primary Key
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
-- Tracking & Identification
|
|
tracking_id VARCHAR(6) UNIQUE NOT NULL,
|
|
opentext_id VARCHAR(255) NOT NULL,
|
|
|
|
-- File Information
|
|
original_filename VARCHAR(500) NOT NULL,
|
|
file_extension VARCHAR(20),
|
|
file_size_bytes BIGINT,
|
|
mime_type VARCHAR(100),
|
|
|
|
-- Metadata Fields (extracted for quick access)
|
|
brand_code VARCHAR(5),
|
|
brand_name VARCHAR(255),
|
|
country_code VARCHAR(2),
|
|
country_name VARCHAR(255),
|
|
language_code VARCHAR(3),
|
|
language_name VARCHAR(100),
|
|
subject_title VARCHAR(255),
|
|
asset_type VARCHAR(3),
|
|
asset_type_name VARCHAR(255),
|
|
duration_seconds INTEGER,
|
|
aspect_ratio VARCHAR(10),
|
|
width_px INTEGER,
|
|
height_px INTEGER,
|
|
|
|
-- Campaign Relationships
|
|
global_master_campaign_id VARCHAR(50),
|
|
global_master_folder_id VARCHAR(255),
|
|
local_campaign_id VARCHAR(50),
|
|
|
|
-- Workflow Information
|
|
upload_directory VARCHAR(1000),
|
|
description TEXT,
|
|
|
|
-- Full Metadata Storage (JSONB - Complete metadata, no truncation)
|
|
full_metadata JSONB,
|
|
|
|
-- Tags and Categories
|
|
tags TEXT[],
|
|
categories TEXT[],
|
|
|
|
-- Status
|
|
status VARCHAR(50) DEFAULT 'active',
|
|
is_deleted BOOLEAN DEFAULT FALSE,
|
|
deleted_at TIMESTAMP,
|
|
|
|
-- Tracking
|
|
ingested_by VARCHAR(255),
|
|
ingested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
\echo 'Table master_assets created'
|
|
|
|
-- ============================================================================
|
|
-- Table: derivative_assets
|
|
-- Purpose: Tracks derivative assets (localized versions) from master assets
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS derivative_assets (
|
|
-- Primary Key
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
-- Link to Master Asset
|
|
tracking_id VARCHAR(6) NOT NULL REFERENCES master_assets(tracking_id),
|
|
master_asset_id INTEGER REFERENCES master_assets(id) ON DELETE CASCADE,
|
|
|
|
-- Derivative File Information
|
|
derivative_filename VARCHAR(500),
|
|
file_extension VARCHAR(20),
|
|
dam_asset_id VARCHAR(255),
|
|
|
|
-- Upload Information
|
|
upload_status VARCHAR(50) DEFAULT 'pending',
|
|
upload_error TEXT,
|
|
uploaded_by VARCHAR(255),
|
|
uploaded_at TIMESTAMP,
|
|
|
|
-- Status
|
|
status VARCHAR(50) DEFAULT 'active',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
\echo 'Table derivative_assets created'
|
|
|
|
-- ============================================================================
|
|
-- Table: asset_events
|
|
-- Purpose: Audit log for all asset operations
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS asset_events (
|
|
id SERIAL PRIMARY KEY,
|
|
tracking_id VARCHAR(6),
|
|
event_type VARCHAR(100) NOT NULL,
|
|
event_data JSONB,
|
|
event_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
created_by VARCHAR(255)
|
|
);
|
|
|
|
\echo 'Table asset_events created'
|
|
|
|
-- ============================================================================
|
|
-- Table: workflow_state
|
|
-- Purpose: Tracks workflow execution state for monitoring
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS workflow_state (
|
|
id SERIAL PRIMARY KEY,
|
|
workflow_name VARCHAR(100) NOT NULL,
|
|
campaign_id VARCHAR(255),
|
|
last_run_at TIMESTAMP,
|
|
last_status VARCHAR(50),
|
|
error_message TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
\echo 'Tables created successfully'
|
|
|
|
-- ============================================================================
|
|
-- INDEXES
|
|
-- ============================================================================
|
|
|
|
\echo 'Creating indexes...'
|
|
|
|
-- master_assets indexes
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_tracking_id ON master_assets(tracking_id);
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_opentext_id ON master_assets(opentext_id);
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_status ON master_assets(status);
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_brand_code ON master_assets(brand_code);
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_created_at ON master_assets(created_at);
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_global_master ON master_assets(global_master_campaign_id);
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_local_campaign ON master_assets(local_campaign_id);
|
|
CREATE INDEX IF NOT EXISTS idx_master_assets_opentext_local ON master_assets(opentext_id, local_campaign_id);
|
|
|
|
-- derivative_assets indexes
|
|
CREATE INDEX IF NOT EXISTS idx_derivative_tracking_id ON derivative_assets(tracking_id);
|
|
CREATE INDEX IF NOT EXISTS idx_derivative_status ON derivative_assets(upload_status);
|
|
CREATE INDEX IF NOT EXISTS idx_derivative_created_at ON derivative_assets(created_at);
|
|
|
|
-- asset_events indexes
|
|
CREATE INDEX IF NOT EXISTS idx_events_tracking_id ON asset_events(tracking_id);
|
|
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON asset_events(event_timestamp);
|
|
CREATE INDEX IF NOT EXISTS idx_events_type ON asset_events(event_type);
|
|
|
|
-- workflow_state indexes
|
|
CREATE INDEX IF NOT EXISTS idx_workflow_name ON workflow_state(workflow_name);
|
|
CREATE INDEX IF NOT EXISTS idx_workflow_campaign ON workflow_state(campaign_id);
|
|
|
|
\echo 'Indexes created successfully'
|
|
|
|
-- ============================================================================
|
|
-- FUNCTIONS
|
|
-- ============================================================================
|
|
|
|
\echo 'Creating functions...'
|
|
|
|
-- Function: Update updated_at timestamp
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Function: Log master asset events
|
|
CREATE OR REPLACE FUNCTION log_master_asset_event()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF (TG_OP = 'INSERT') THEN
|
|
INSERT INTO asset_events (tracking_id, event_type, event_data)
|
|
VALUES (NEW.tracking_id, 'master_asset_created', row_to_json(NEW)::jsonb);
|
|
RETURN NEW;
|
|
ELSIF (TG_OP = 'UPDATE') THEN
|
|
INSERT INTO asset_events (tracking_id, event_type, event_data)
|
|
VALUES (NEW.tracking_id, 'master_asset_updated',
|
|
jsonb_build_object('old', row_to_json(OLD)::jsonb, 'new', row_to_json(NEW)::jsonb));
|
|
RETURN NEW;
|
|
ELSIF (TG_OP = 'DELETE') THEN
|
|
INSERT INTO asset_events (tracking_id, event_type, event_data)
|
|
VALUES (OLD.tracking_id, 'master_asset_deleted', row_to_json(OLD)::jsonb);
|
|
RETURN OLD;
|
|
END IF;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
\echo 'Functions created successfully'
|
|
|
|
-- ============================================================================
|
|
-- TRIGGERS
|
|
-- ============================================================================
|
|
|
|
\echo 'Creating triggers...'
|
|
|
|
-- Trigger: Auto-update updated_at on master_assets
|
|
DROP TRIGGER IF EXISTS update_master_assets_updated_at ON master_assets;
|
|
CREATE TRIGGER update_master_assets_updated_at
|
|
BEFORE UPDATE ON master_assets
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- Trigger: Log master asset changes
|
|
DROP TRIGGER IF EXISTS log_master_asset_changes ON master_assets;
|
|
CREATE TRIGGER log_master_asset_changes
|
|
AFTER INSERT OR UPDATE OR DELETE ON master_assets
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION log_master_asset_event();
|
|
|
|
-- Trigger: Auto-update updated_at on derivative_assets
|
|
DROP TRIGGER IF EXISTS update_derivative_assets_updated_at ON derivative_assets;
|
|
CREATE TRIGGER update_derivative_assets_updated_at
|
|
BEFORE UPDATE ON derivative_assets
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- Trigger: Auto-update updated_at on workflow_state
|
|
DROP TRIGGER IF EXISTS update_workflow_state_updated_at ON workflow_state;
|
|
CREATE TRIGGER update_workflow_state_updated_at
|
|
BEFORE UPDATE ON workflow_state
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
\echo 'Triggers created successfully'
|
|
|
|
-- ============================================================================
|
|
-- GRANTS
|
|
-- ============================================================================
|
|
|
|
\echo 'Setting up permissions...'
|
|
|
|
-- Grant all privileges on tables
|
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ferrero_user;
|
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ferrero_user;
|
|
|
|
-- Grant usage on schema
|
|
GRANT USAGE ON SCHEMA public TO ferrero_user;
|
|
|
|
\echo 'Permissions granted'
|
|
|
|
-- ============================================================================
|
|
-- VERIFICATION
|
|
-- ============================================================================
|
|
|
|
\echo ''
|
|
\echo '============================================================'
|
|
\echo 'Database initialization complete!'
|
|
\echo '============================================================'
|
|
\echo ''
|
|
\echo 'Tables created:'
|
|
\echo ' - master_assets (35 columns)'
|
|
\echo ' - derivative_assets'
|
|
\echo ' - asset_events'
|
|
\echo ' - workflow_state'
|
|
\echo ''
|
|
\echo 'Indexes created: 12'
|
|
\echo 'Triggers created: 4'
|
|
\echo 'Functions created: 2'
|
|
\echo ''
|
|
\echo 'Ready to use!'
|
|
\echo '============================================================'
|
|
|
|
-- Show table counts
|
|
SELECT
|
|
'master_assets' as table_name,
|
|
COUNT(*) as row_count
|
|
FROM master_assets
|
|
UNION ALL
|
|
SELECT
|
|
'derivative_assets' as table_name,
|
|
COUNT(*) as row_count
|
|
FROM derivative_assets
|
|
UNION ALL
|
|
SELECT
|
|
'asset_events' as table_name,
|
|
COUNT(*) as row_count
|
|
FROM asset_events
|
|
UNION ALL
|
|
SELECT
|
|
'workflow_state' as table_name,
|
|
COUNT(*) as row_count
|
|
FROM workflow_state;
|