scaffold: Initial database migration — 24-table multi-tenant schema

Full PostgreSQL schema with license_id tenant scoping on every table,
4 default RBAC roles (Owner, Head Admin, Moderator, Viewer), JSONB
configs for wipe profiles, webstore, and notification channels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-14 21:42:10 -05:00
parent e5ed25a86a
commit 5b18a52634

View File

@@ -0,0 +1,599 @@
-- Corrosion Platform — Initial Schema
-- All tables scoped by license_id for multi-tenant isolation
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-----------------------------------------------------------
-- USERS & AUTH
-----------------------------------------------------------
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email VARCHAR(255) NOT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
totp_secret TEXT,
totp_enabled BOOLEAN NOT NULL DEFAULT false,
backup_codes TEXT[],
email_verified BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_login_at TIMESTAMPTZ
);
CREATE INDEX idx_users_email ON users(email);
-----------------------------------------------------------
-- LICENSES
-----------------------------------------------------------
CREATE TABLE licenses (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_key VARCHAR(64) NOT NULL UNIQUE,
status VARCHAR(20) NOT NULL DEFAULT 'active'
CHECK (status IN ('active', 'suspended', 'expired', 'revoked')),
owner_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
server_name VARCHAR(100),
subdomain VARCHAR(63) UNIQUE,
custom_domain VARCHAR(255) UNIQUE,
modules_enabled TEXT[] DEFAULT '{}',
webstore_active BOOLEAN NOT NULL DEFAULT false,
webstore_subscription_id VARCHAR(100),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ
);
CREATE INDEX idx_licenses_key ON licenses(license_key);
CREATE INDEX idx_licenses_owner ON licenses(owner_user_id);
CREATE INDEX idx_licenses_subdomain ON licenses(subdomain);
-----------------------------------------------------------
-- TEAM & RBAC
-----------------------------------------------------------
CREATE TABLE roles (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID REFERENCES licenses(id) ON DELETE CASCADE,
role_name VARCHAR(50) NOT NULL,
is_system_default BOOLEAN NOT NULL DEFAULT false,
is_cloned_from UUID REFERENCES roles(id) ON DELETE SET NULL,
permissions JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_roles_license ON roles(license_id);
CREATE TABLE team_members (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE RESTRICT,
invited_by UUID NOT NULL REFERENCES users(id),
accepted_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(license_id, user_id)
);
CREATE INDEX idx_team_license ON team_members(license_id);
CREATE INDEX idx_team_user ON team_members(user_id);
-----------------------------------------------------------
-- SERVER CONNECTION & CONFIG
-----------------------------------------------------------
CREATE TABLE server_connections (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL UNIQUE REFERENCES licenses(id) ON DELETE CASCADE,
connection_type VARCHAR(20) NOT NULL
CHECK (connection_type IN ('amp', 'pterodactyl', 'bare_metal')),
panel_api_endpoint TEXT,
panel_api_key_encrypted TEXT,
panel_server_identifier VARCHAR(255),
companion_agent_token VARCHAR(128),
companion_last_seen TIMESTAMPTZ,
plugin_last_seen TIMESTAMPTZ,
server_ip VARCHAR(45),
server_port INTEGER,
game_port INTEGER,
connection_status VARCHAR(20) NOT NULL DEFAULT 'offline'
CHECK (connection_status IN ('connected', 'degraded', 'offline')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_server_conn_license ON server_connections(license_id);
CREATE TABLE server_config (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL UNIQUE REFERENCES licenses(id) ON DELETE CASCADE,
server_name VARCHAR(255) NOT NULL DEFAULT '',
max_players INTEGER,
world_size INTEGER,
current_seed INTEGER,
current_map_id UUID,
server_description TEXT,
server_url TEXT,
server_header_image TEXT,
tags TEXT[] DEFAULT '{}',
auto_restart_enabled BOOLEAN NOT NULL DEFAULT false,
auto_restart_cron VARCHAR(100),
auto_restart_timezone VARCHAR(50),
crash_recovery_enabled BOOLEAN NOT NULL DEFAULT true,
crash_recovery_max_attempts INTEGER NOT NULL DEFAULT 3,
crash_recovery_cooldown_minutes INTEGER NOT NULL DEFAULT 10,
force_wipe_eligible BOOLEAN NOT NULL DEFAULT true,
auto_update_on_force_wipe BOOLEAN NOT NULL DEFAULT true,
config_overrides JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_server_config_license ON server_config(license_id);
-----------------------------------------------------------
-- GAME ADMINS (in-game SteamID-based)
-----------------------------------------------------------
CREATE TABLE game_admins (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
steam_id VARCHAR(20) NOT NULL,
display_name VARCHAR(100) NOT NULL DEFAULT '',
admin_level VARCHAR(20) NOT NULL DEFAULT 'admin'
CHECK (admin_level IN ('owner', 'admin', 'moderator')),
permissions JSONB DEFAULT '{}',
added_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(license_id, steam_id)
);
CREATE INDEX idx_game_admins_license ON game_admins(license_id);
-----------------------------------------------------------
-- WIPE PROFILES & SCHEDULES
-----------------------------------------------------------
CREATE TABLE wipe_profiles (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
profile_name VARCHAR(100) NOT NULL,
description TEXT,
pre_wipe_config JSONB NOT NULL DEFAULT '{
"enabled": true,
"backup_before_wipe": true,
"countdown_warnings": [30, 15, 5, 1],
"countdown_unit": "minutes",
"countdown_messages": {},
"kick_players_before_wipe": true,
"kick_message": "Server is wiping. Be back shortly!",
"run_final_save": true,
"discord_pre_announce": true,
"pushbullet_notify": false,
"custom_commands_before": []
}',
post_wipe_config JSONB NOT NULL DEFAULT '{
"enabled": true,
"verify_server_started": true,
"verify_correct_map": true,
"verify_plugins_loaded": true,
"verify_player_slots_open": true,
"max_restart_attempts": 3,
"health_check_timeout_seconds": 120,
"discord_post_announce": true,
"pushbullet_notify": false,
"rollback_on_failure": true,
"post_wipe_commands": []
}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_wipe_profiles_license ON wipe_profiles(license_id);
CREATE TABLE wipe_schedules (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
wipe_profile_id UUID NOT NULL REFERENCES wipe_profiles(id) ON DELETE CASCADE,
schedule_name VARCHAR(100) NOT NULL,
wipe_type VARCHAR(20) NOT NULL
CHECK (wipe_type IN ('map', 'blueprint', 'full')),
cron_expression VARCHAR(100) NOT NULL,
timezone VARCHAR(50) NOT NULL DEFAULT 'America/New_York',
wipe_blueprints BOOLEAN NOT NULL DEFAULT false,
is_active BOOLEAN NOT NULL DEFAULT true,
next_scheduled_run TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_wipe_schedules_license ON wipe_schedules(license_id);
CREATE INDEX idx_wipe_schedules_next_run ON wipe_schedules(next_scheduled_run) WHERE is_active = true;
-----------------------------------------------------------
-- MAP LIBRARY & ROTATION
-----------------------------------------------------------
CREATE TABLE map_library (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
filename VARCHAR(255) NOT NULL,
display_name VARCHAR(255) NOT NULL,
storage_path TEXT NOT NULL,
file_size_bytes BIGINT NOT NULL DEFAULT 0,
map_type VARCHAR(20) NOT NULL DEFAULT 'custom'
CHECK (map_type IN ('custom', 'procedural')),
seed INTEGER,
world_size INTEGER,
thumbnail_path TEXT,
checksum VARCHAR(64) NOT NULL,
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_map_library_license ON map_library(license_id);
-- FK for server_config.current_map_id (deferred because map_library created after server_config)
ALTER TABLE server_config
ADD CONSTRAINT fk_server_config_map
FOREIGN KEY (current_map_id) REFERENCES map_library(id) ON DELETE SET NULL;
CREATE TABLE map_rotations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
map_id UUID NOT NULL REFERENCES map_library(id) ON DELETE CASCADE,
rotation_order INTEGER NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT true,
UNIQUE(license_id, rotation_order)
);
CREATE INDEX idx_map_rotations_license ON map_rotations(license_id);
-----------------------------------------------------------
-- PLUGIN REGISTRY
-----------------------------------------------------------
CREATE TABLE plugin_registry (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
plugin_name VARCHAR(255) NOT NULL,
plugin_version VARCHAR(50),
source VARCHAR(20) NOT NULL DEFAULT 'manual'
CHECK (source IN ('umod', 'corrosion_module', 'manual')),
umod_slug VARCHAR(255),
is_installed BOOLEAN NOT NULL DEFAULT false,
is_loaded BOOLEAN NOT NULL DEFAULT false,
config_json JSONB,
data_path TEXT,
wipe_on_map BOOLEAN NOT NULL DEFAULT false,
wipe_on_bp BOOLEAN NOT NULL DEFAULT false,
wipe_on_full BOOLEAN NOT NULL DEFAULT false,
never_wipe BOOLEAN NOT NULL DEFAULT false,
installed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(license_id, plugin_name)
);
CREATE INDEX idx_plugin_registry_license ON plugin_registry(license_id);
-----------------------------------------------------------
-- WIPE HISTORY
-----------------------------------------------------------
CREATE TABLE wipe_history (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
wipe_schedule_id UUID REFERENCES wipe_schedules(id) ON DELETE SET NULL,
wipe_profile_id UUID NOT NULL REFERENCES wipe_profiles(id),
wipe_type VARCHAR(20) NOT NULL,
trigger_type VARCHAR(20) NOT NULL
CHECK (trigger_type IN ('scheduled', 'manual', 'force_wipe')),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'pre_wipe', 'wiping', 'post_wipe', 'success', 'failed', 'rolled_back')),
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
map_used VARCHAR(255),
plugins_wiped TEXT[],
plugins_preserved TEXT[],
backup_reference TEXT,
error_message TEXT,
execution_log JSONB DEFAULT '[]',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_wipe_history_license ON wipe_history(license_id);
CREATE INDEX idx_wipe_history_created ON wipe_history(created_at DESC);
-----------------------------------------------------------
-- SCHEDULED TASKS (restarts, announcements, etc.)
-----------------------------------------------------------
CREATE TABLE scheduled_tasks (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
task_type VARCHAR(30) NOT NULL
CHECK (task_type IN ('restart', 'announcement', 'command', 'plugin_reload')),
task_name VARCHAR(100) NOT NULL,
cron_expression VARCHAR(100) NOT NULL,
timezone VARCHAR(50) NOT NULL DEFAULT 'America/New_York',
task_config JSONB NOT NULL DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT true,
next_run TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_scheduled_tasks_license ON scheduled_tasks(license_id);
-----------------------------------------------------------
-- NOTIFICATIONS
-----------------------------------------------------------
CREATE TABLE notifications_config (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL UNIQUE REFERENCES licenses(id) ON DELETE CASCADE,
discord_webhook_url TEXT,
discord_enabled BOOLEAN NOT NULL DEFAULT false,
pushbullet_api_key TEXT,
pushbullet_enabled BOOLEAN NOT NULL DEFAULT false,
email_alerts_enabled BOOLEAN NOT NULL DEFAULT true,
notify_wipe_start BOOLEAN NOT NULL DEFAULT true,
notify_wipe_complete BOOLEAN NOT NULL DEFAULT true,
notify_wipe_failed BOOLEAN NOT NULL DEFAULT true,
notify_server_crash BOOLEAN NOT NULL DEFAULT true,
notify_server_offline BOOLEAN NOT NULL DEFAULT true,
notify_store_purchase BOOLEAN NOT NULL DEFAULT true,
notify_player_report BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-----------------------------------------------------------
-- CHAT LOGS & PLAYER ACTIONS
-----------------------------------------------------------
CREATE TABLE chat_logs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
steam_id VARCHAR(20) NOT NULL,
player_name VARCHAR(100) NOT NULL,
channel VARCHAR(20) NOT NULL DEFAULT 'global'
CHECK (channel IN ('global', 'team', 'server')),
message TEXT NOT NULL,
flagged BOOLEAN NOT NULL DEFAULT false,
flagged_by UUID REFERENCES users(id),
flag_reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_chat_logs_license ON chat_logs(license_id);
CREATE INDEX idx_chat_logs_created ON chat_logs(created_at DESC);
CREATE INDEX idx_chat_logs_steam ON chat_logs(license_id, steam_id);
CREATE TABLE player_actions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
steam_id VARCHAR(20) NOT NULL,
player_name VARCHAR(100) NOT NULL,
action_type VARCHAR(20) NOT NULL
CHECK (action_type IN ('kick', 'ban', 'unban', 'warn', 'note')),
reason TEXT,
duration_minutes INTEGER,
performed_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_player_actions_license ON player_actions(license_id);
CREATE INDEX idx_player_actions_steam ON player_actions(license_id, steam_id);
-----------------------------------------------------------
-- SERVER STATS (time-series)
-----------------------------------------------------------
CREATE TABLE server_stats (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
player_count INTEGER NOT NULL DEFAULT 0,
max_players INTEGER NOT NULL DEFAULT 0,
fps DOUBLE PRECISION NOT NULL DEFAULT 0,
entity_count INTEGER NOT NULL DEFAULT 0,
uptime_seconds INTEGER NOT NULL DEFAULT 0,
memory_usage_mb INTEGER NOT NULL DEFAULT 0,
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_server_stats_license_time ON server_stats(license_id, recorded_at DESC);
CREATE TABLE server_stats_hourly (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
hour TIMESTAMPTZ NOT NULL,
avg_players DOUBLE PRECISION NOT NULL DEFAULT 0,
max_players INTEGER NOT NULL DEFAULT 0,
avg_fps DOUBLE PRECISION NOT NULL DEFAULT 0,
min_fps DOUBLE PRECISION NOT NULL DEFAULT 0,
avg_entities INTEGER NOT NULL DEFAULT 0,
uptime_percentage DOUBLE PRECISION NOT NULL DEFAULT 0,
UNIQUE(license_id, hour)
);
CREATE INDEX idx_stats_hourly_license ON server_stats_hourly(license_id, hour DESC);
-----------------------------------------------------------
-- PUBLIC SITE CONFIG
-----------------------------------------------------------
CREATE TABLE public_site_config (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL UNIQUE REFERENCES licenses(id) ON DELETE CASCADE,
site_enabled BOOLEAN NOT NULL DEFAULT true,
show_on_status_page BOOLEAN NOT NULL DEFAULT false,
steam_connect_url VARCHAR(255),
motd TEXT,
public_mods TEXT[] DEFAULT '{}',
header_image_url TEXT,
theme_color VARCHAR(7) DEFAULT '#ef4444',
custom_css TEXT,
discord_invite_url TEXT,
show_player_count BOOLEAN NOT NULL DEFAULT true,
show_wipe_schedule BOOLEAN NOT NULL DEFAULT true,
show_wipe_countdown BOOLEAN NOT NULL DEFAULT true,
show_mod_list BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-----------------------------------------------------------
-- WEBSTORE
-----------------------------------------------------------
CREATE TABLE webstore_config (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL UNIQUE REFERENCES licenses(id) ON DELETE CASCADE,
is_active BOOLEAN NOT NULL DEFAULT false,
paypal_client_id TEXT,
paypal_secret TEXT,
paypal_mode VARCHAR(10) NOT NULL DEFAULT 'sandbox'
CHECK (paypal_mode IN ('sandbox', 'live')),
store_name VARCHAR(100),
store_description TEXT,
currency VARCHAR(3) NOT NULL DEFAULT 'USD',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE webstore_categories (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
category_name VARCHAR(100) NOT NULL,
display_order INTEGER NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT true
);
CREATE INDEX idx_webstore_cats_license ON webstore_categories(license_id);
CREATE TABLE webstore_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
category_id UUID NOT NULL REFERENCES webstore_categories(id) ON DELETE CASCADE,
item_name VARCHAR(100) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
image_url TEXT,
item_type VARCHAR(20) NOT NULL DEFAULT 'kit'
CHECK (item_type IN ('kit', 'rank', 'currency', 'custom_command')),
delivery_config JSONB NOT NULL DEFAULT '{"commands": []}',
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_webstore_items_license ON webstore_items(license_id);
CREATE INDEX idx_webstore_items_category ON webstore_items(category_id);
CREATE TABLE webstore_transactions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
item_id UUID NOT NULL REFERENCES webstore_items(id),
buyer_steam_id VARCHAR(20) NOT NULL,
buyer_name VARCHAR(100),
amount DECIMAL(10,2) NOT NULL,
currency VARCHAR(3) NOT NULL DEFAULT 'USD',
paypal_transaction_id VARCHAR(100),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'paid', 'delivered', 'failed', 'refunded')),
delivered_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_webstore_txn_license ON webstore_transactions(license_id);
CREATE INDEX idx_webstore_txn_status ON webstore_transactions(license_id, status);
-----------------------------------------------------------
-- MIGRATION EXPORTS
-----------------------------------------------------------
CREATE TABLE migration_exports (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
export_type VARCHAR(20) NOT NULL DEFAULT 'full'
CHECK (export_type IN ('full', 'config_only', 'store_only')),
storage_path TEXT NOT NULL,
file_size_bytes BIGINT NOT NULL DEFAULT 0,
created_by UUID NOT NULL REFERENCES users(id),
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-----------------------------------------------------------
-- PLATFORM CHANGELOG
-----------------------------------------------------------
CREATE TABLE platform_changelog (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
version VARCHAR(20) NOT NULL,
title VARCHAR(200) NOT NULL,
body TEXT NOT NULL,
category VARCHAR(20) NOT NULL DEFAULT 'feature'
CHECK (category IN ('feature', 'bugfix', 'module', 'security')),
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-----------------------------------------------------------
-- MODULE STORE
-----------------------------------------------------------
CREATE TABLE module_store (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
module_slug VARCHAR(50) NOT NULL UNIQUE,
module_name VARCHAR(100) NOT NULL,
description TEXT,
long_description TEXT,
price DECIMAL(10,2) NOT NULL DEFAULT 0,
price_type VARCHAR(20) NOT NULL DEFAULT 'one_time'
CHECK (price_type IN ('one_time', 'monthly')),
monthly_price DECIMAL(10,2),
version VARCHAR(20) NOT NULL DEFAULT '1.0.0',
download_path TEXT,
thumbnail_url TEXT,
screenshots TEXT[] DEFAULT '{}',
category VARCHAR(50),
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-----------------------------------------------------------
-- DEFAULT SYSTEM ROLES
-----------------------------------------------------------
INSERT INTO roles (id, license_id, role_name, is_system_default, permissions) VALUES
(uuid_generate_v4(), NULL, 'Owner', true, '{
"server.*": true,
"wipe.*": true,
"plugins.*": true,
"players.*": true,
"team.*": true,
"billing.*": true,
"store.*": true,
"settings.*": true,
"public_site.*": true
}'),
(uuid_generate_v4(), NULL, 'Head Admin', true, '{
"server.*": true,
"wipe.*": true,
"plugins.*": true,
"players.*": true,
"team.view": true,
"team.invite": true,
"store.*": true,
"settings.server": true,
"public_site.*": true
}'),
(uuid_generate_v4(), NULL, 'Moderator', true, '{
"server.view": true,
"server.console": true,
"players.kick": true,
"players.ban": true,
"players.chat_log": true,
"wipe.view": true,
"store.view": true
}'),
(uuid_generate_v4(), NULL, 'Viewer', true, '{
"server.view": true,
"players.view": true,
"wipe.view": true,
"store.view": true
}');