feat: add workflow studio and postgres support
This commit is contained in:
@@ -0,0 +1,868 @@
|
||||
-- BoosAPI initial schema
|
||||
-- Generated from the existing SQLite database (hub.db)
|
||||
-- This migration creates all tables, indexes, and constraints.
|
||||
|
||||
-- ============================================================
|
||||
-- SITES
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS sites (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
platform TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
api_key TEXT,
|
||||
proxy_url TEXT,
|
||||
use_system_proxy BOOLEAN DEFAULT FALSE,
|
||||
custom_headers TEXT,
|
||||
external_checkin_url TEXT,
|
||||
global_weight DOUBLE PRECISION DEFAULT 1,
|
||||
post_refresh_probe_enabled BOOLEAN DEFAULT FALSE,
|
||||
post_refresh_probe_model TEXT DEFAULT '',
|
||||
post_refresh_probe_scope TEXT DEFAULT 'single',
|
||||
post_refresh_probe_latency_threshold_ms BIGINT DEFAULT 0,
|
||||
is_pinned BOOLEAN DEFAULT FALSE,
|
||||
sort_order BIGINT DEFAULT 0,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS sites_platform_url_unique
|
||||
ON sites (platform, url);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS sites_status_idx
|
||||
ON sites (status);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- USERS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'user',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS users_email_unique
|
||||
ON users (email);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS users_status_idx
|
||||
ON users (status);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- TOKEN ROUTES
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS token_routes (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
model_pattern TEXT NOT NULL,
|
||||
model_mapping TEXT,
|
||||
display_name TEXT,
|
||||
display_icon TEXT,
|
||||
decision_snapshot TEXT,
|
||||
decision_refreshed_at TEXT,
|
||||
routing_strategy TEXT DEFAULT 'weighted',
|
||||
route_mode TEXT DEFAULT 'pattern',
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS token_routes_model_pattern_idx
|
||||
ON token_routes (model_pattern);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS token_routes_enabled_idx
|
||||
ON token_routes (enabled);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- ACCOUNTS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
username TEXT,
|
||||
access_token TEXT NOT NULL,
|
||||
api_token TEXT,
|
||||
balance DOUBLE PRECISION DEFAULT 0,
|
||||
balance_used DOUBLE PRECISION DEFAULT 0,
|
||||
quota DOUBLE PRECISION DEFAULT 0,
|
||||
unit_cost DOUBLE PRECISION,
|
||||
value_score DOUBLE PRECISION DEFAULT 0,
|
||||
status TEXT DEFAULT 'active',
|
||||
checkin_enabled BOOLEAN DEFAULT TRUE,
|
||||
last_checkin_at TEXT,
|
||||
last_balance_refresh TEXT,
|
||||
extra_config TEXT,
|
||||
is_pinned BOOLEAN DEFAULT FALSE,
|
||||
sort_order BIGINT DEFAULT 0,
|
||||
oauth_provider TEXT,
|
||||
oauth_account_key TEXT,
|
||||
oauth_project_id TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS accounts_site_id_idx
|
||||
ON accounts (site_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS accounts_status_idx
|
||||
ON accounts (status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS accounts_site_status_idx
|
||||
ON accounts (site_id, status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS accounts_oauth_provider_idx
|
||||
ON accounts (oauth_provider);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS accounts_oauth_identity_idx
|
||||
ON accounts (oauth_provider, oauth_account_key, oauth_project_id);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- DOWNSTREAM API KEYS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS downstream_api_keys (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
description TEXT,
|
||||
group_name TEXT,
|
||||
tags TEXT,
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
expires_at TEXT,
|
||||
max_cost DOUBLE PRECISION,
|
||||
used_cost DOUBLE PRECISION DEFAULT 0,
|
||||
max_requests BIGINT,
|
||||
used_requests BIGINT DEFAULT 0,
|
||||
supported_models TEXT,
|
||||
allowed_route_ids TEXT,
|
||||
site_weight_multipliers TEXT,
|
||||
excluded_site_ids TEXT,
|
||||
excluded_credential_refs TEXT,
|
||||
last_used_at TEXT,
|
||||
user_id BIGINT REFERENCES users(id),
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS downstream_api_keys_key_unique
|
||||
ON downstream_api_keys (key);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS downstream_api_keys_name_idx
|
||||
ON downstream_api_keys (name);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS downstream_api_keys_enabled_idx
|
||||
ON downstream_api_keys (enabled);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS downstream_api_keys_expires_at_idx
|
||||
ON downstream_api_keys (expires_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- SITE DISABLED MODELS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS site_disabled_models (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
model_name TEXT NOT NULL,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS site_disabled_models_site_model_unique
|
||||
ON site_disabled_models (site_id, model_name);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_disabled_models_site_id_idx
|
||||
ON site_disabled_models (site_id);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- SITE API ENDPOINTS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS site_api_endpoints (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
sort_order BIGINT DEFAULT 0,
|
||||
cooldown_until TEXT,
|
||||
last_selected_at TEXT,
|
||||
last_failed_at TEXT,
|
||||
last_failure_reason TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS site_api_endpoints_site_url_unique
|
||||
ON site_api_endpoints (site_id, url);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_api_endpoints_site_enabled_sort_idx
|
||||
ON site_api_endpoints (site_id, enabled, sort_order);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_api_endpoints_site_cooldown_idx
|
||||
ON site_api_endpoints (site_id, cooldown_until);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- ACCOUNT TOKENS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS account_tokens (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
source TEXT DEFAULT 'manual',
|
||||
token_group TEXT,
|
||||
value_status TEXT NOT NULL DEFAULT 'ready',
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
is_default BOOLEAN DEFAULT FALSE,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS account_tokens_account_id_idx
|
||||
ON account_tokens (account_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS account_tokens_account_enabled_idx
|
||||
ON account_tokens (account_id, enabled);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS account_tokens_enabled_idx
|
||||
ON account_tokens (enabled);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- CHECKIN LOGS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS checkin_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
message TEXT,
|
||||
reward TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS checkin_logs_account_created_at_idx
|
||||
ON checkin_logs (account_id, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS checkin_logs_created_at_idx
|
||||
ON checkin_logs (created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS checkin_logs_status_idx
|
||||
ON checkin_logs (status);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- MODEL AVAILABILITY
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS model_availability (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
model_name TEXT NOT NULL,
|
||||
available BIGINT,
|
||||
latency_ms BIGINT,
|
||||
checked_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
is_manual BOOLEAN DEFAULT FALSE,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS model_availability_account_model_unique
|
||||
ON model_availability (account_id, model_name);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS model_availability_account_available_idx
|
||||
ON model_availability (account_id, available);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS model_availability_model_name_idx
|
||||
ON model_availability (model_name);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- TOKEN MODEL AVAILABILITY
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS token_model_availability (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
token_id BIGINT NOT NULL,
|
||||
model_name TEXT NOT NULL,
|
||||
available BIGINT,
|
||||
latency_ms BIGINT,
|
||||
checked_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (token_id) REFERENCES account_tokens(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS token_model_availability_token_model_unique
|
||||
ON token_model_availability (token_id, model_name);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS token_model_availability_token_available_idx
|
||||
ON token_model_availability (token_id, available);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS token_model_availability_model_name_idx
|
||||
ON token_model_availability (model_name);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS token_model_availability_available_idx
|
||||
ON token_model_availability (available);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- ROUTE CHANNELS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS route_channels (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
route_id BIGINT NOT NULL,
|
||||
account_id BIGINT NOT NULL,
|
||||
token_id BIGINT,
|
||||
oauth_route_unit_id BIGINT,
|
||||
source_model TEXT,
|
||||
priority BIGINT DEFAULT 0,
|
||||
weight BIGINT DEFAULT 10,
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
manual_override BOOLEAN DEFAULT FALSE,
|
||||
success_count BIGINT DEFAULT 0,
|
||||
fail_count BIGINT DEFAULT 0,
|
||||
total_latency_ms BIGINT DEFAULT 0,
|
||||
total_cost DOUBLE PRECISION DEFAULT 0,
|
||||
last_used_at TEXT,
|
||||
last_selected_at TEXT,
|
||||
last_fail_at TEXT,
|
||||
consecutive_fail_count BIGINT NOT NULL DEFAULT 0,
|
||||
cooldown_level BIGINT NOT NULL DEFAULT 0,
|
||||
cooldown_until TEXT,
|
||||
FOREIGN KEY (route_id) REFERENCES token_routes(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (token_id) REFERENCES account_tokens(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS route_channels_route_id_idx
|
||||
ON route_channels (route_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS route_channels_account_id_idx
|
||||
ON route_channels (account_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS route_channels_token_id_idx
|
||||
ON route_channels (token_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS route_channels_route_enabled_idx
|
||||
ON route_channels (route_id, enabled);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS route_channels_route_token_idx
|
||||
ON route_channels (route_id, token_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS route_channels_oauth_route_unit_id_idx
|
||||
ON route_channels (oauth_route_unit_id);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- ROUTE GROUP SOURCES
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS route_group_sources (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_route_id BIGINT NOT NULL,
|
||||
source_route_id BIGINT NOT NULL,
|
||||
FOREIGN KEY (group_route_id) REFERENCES token_routes(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (source_route_id) REFERENCES token_routes(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS route_group_sources_group_source_unique
|
||||
ON route_group_sources (group_route_id, source_route_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS route_group_sources_source_route_id_idx
|
||||
ON route_group_sources (source_route_id);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- PROXY LOGS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS proxy_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
route_id BIGINT,
|
||||
channel_id BIGINT,
|
||||
account_id BIGINT,
|
||||
downstream_api_key_id BIGINT,
|
||||
model_requested TEXT,
|
||||
model_actual TEXT,
|
||||
status TEXT,
|
||||
http_status BIGINT,
|
||||
latency_ms BIGINT,
|
||||
first_byte_latency_ms BIGINT,
|
||||
prompt_tokens BIGINT,
|
||||
completion_tokens BIGINT,
|
||||
total_tokens BIGINT,
|
||||
estimated_cost DOUBLE PRECISION,
|
||||
error_message TEXT,
|
||||
retry_count BIGINT DEFAULT 0,
|
||||
is_stream BIGINT,
|
||||
client_family TEXT,
|
||||
client_app_id TEXT,
|
||||
client_app_name TEXT,
|
||||
client_confidence TEXT,
|
||||
billing_details TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_logs_created_at_idx
|
||||
ON proxy_logs (created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_logs_account_created_at_idx
|
||||
ON proxy_logs (account_id, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_logs_status_created_at_idx
|
||||
ON proxy_logs (status, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_logs_model_actual_created_at_idx
|
||||
ON proxy_logs (model_actual, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_logs_downstream_api_key_created_at_idx
|
||||
ON proxy_logs (downstream_api_key_id, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_logs_client_app_id_created_at_idx
|
||||
ON proxy_logs (client_app_id, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_logs_client_family_created_at_idx
|
||||
ON proxy_logs (client_family, created_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- PROXY VIDEO TASKS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS proxy_video_tasks (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
public_id TEXT NOT NULL,
|
||||
upstream_video_id TEXT NOT NULL,
|
||||
site_url TEXT NOT NULL,
|
||||
token_value TEXT NOT NULL,
|
||||
requested_model TEXT,
|
||||
actual_model TEXT,
|
||||
channel_id BIGINT,
|
||||
account_id BIGINT,
|
||||
status_snapshot TEXT,
|
||||
upstream_response_meta TEXT,
|
||||
last_upstream_status BIGINT,
|
||||
last_polled_at TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS proxy_video_tasks_public_id_unique
|
||||
ON proxy_video_tasks (public_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_video_tasks_upstream_video_id_idx
|
||||
ON proxy_video_tasks (upstream_video_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_video_tasks_created_at_idx
|
||||
ON proxy_video_tasks (created_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- PROXY FILES
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS proxy_files (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
public_id TEXT NOT NULL,
|
||||
owner_type TEXT NOT NULL,
|
||||
owner_id TEXT NOT NULL,
|
||||
filename TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
purpose TEXT,
|
||||
byte_size BIGINT NOT NULL,
|
||||
sha256 TEXT NOT NULL,
|
||||
content_base64 TEXT NOT NULL,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
deleted_at TEXT
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS proxy_files_public_id_unique
|
||||
ON proxy_files (public_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_files_owner_lookup_idx
|
||||
ON proxy_files (owner_type, owner_id, deleted_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- PROXY DEBUG TRACES
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS proxy_debug_traces (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
downstream_path TEXT NOT NULL,
|
||||
client_kind TEXT,
|
||||
session_id TEXT,
|
||||
trace_hint TEXT,
|
||||
requested_model TEXT,
|
||||
downstream_api_key_id BIGINT,
|
||||
request_headers_json TEXT,
|
||||
request_body_json TEXT,
|
||||
sticky_session_key TEXT,
|
||||
sticky_hit_channel_id BIGINT,
|
||||
selected_channel_id BIGINT,
|
||||
selected_route_id BIGINT,
|
||||
selected_account_id BIGINT,
|
||||
selected_site_id BIGINT,
|
||||
selected_site_platform TEXT,
|
||||
endpoint_candidates_json TEXT,
|
||||
endpoint_runtime_state_json TEXT,
|
||||
decision_summary_json TEXT,
|
||||
final_status TEXT,
|
||||
final_http_status BIGINT,
|
||||
final_upstream_path TEXT,
|
||||
final_response_headers_json TEXT,
|
||||
final_response_body_json TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_debug_traces_created_at_idx
|
||||
ON proxy_debug_traces (created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_debug_traces_session_created_at_idx
|
||||
ON proxy_debug_traces (session_id, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_debug_traces_model_created_at_idx
|
||||
ON proxy_debug_traces (requested_model, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_debug_traces_final_status_created_at_idx
|
||||
ON proxy_debug_traces (final_status, created_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- PROXY DEBUG ATTEMPTS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS proxy_debug_attempts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
trace_id BIGINT NOT NULL,
|
||||
attempt_index BIGINT NOT NULL,
|
||||
endpoint TEXT NOT NULL,
|
||||
request_path TEXT NOT NULL,
|
||||
target_url TEXT NOT NULL,
|
||||
runtime_executor TEXT,
|
||||
request_headers_json TEXT,
|
||||
request_body_json TEXT,
|
||||
response_status BIGINT,
|
||||
response_headers_json TEXT,
|
||||
response_body_json TEXT,
|
||||
raw_error_text TEXT,
|
||||
recover_applied BOOLEAN DEFAULT FALSE,
|
||||
downgrade_decision BOOLEAN DEFAULT FALSE,
|
||||
downgrade_reason TEXT,
|
||||
memory_write_json TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (trace_id) REFERENCES proxy_debug_traces(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS proxy_debug_attempts_trace_attempt_unique
|
||||
ON proxy_debug_attempts (trace_id, attempt_index);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS proxy_debug_attempts_trace_created_at_idx
|
||||
ON proxy_debug_attempts (trace_id, created_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- EVENTS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
type TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
message TEXT,
|
||||
level TEXT NOT NULL DEFAULT 'info',
|
||||
"read" BOOLEAN DEFAULT FALSE,
|
||||
related_id BIGINT,
|
||||
related_type TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS events_read_created_at_idx
|
||||
ON events ("read", created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS events_type_created_at_idx
|
||||
ON events (type, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS events_created_at_idx
|
||||
ON events (created_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- SITE ANNOUNCEMENTS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS site_announcements (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
platform TEXT NOT NULL,
|
||||
source_key TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
level TEXT NOT NULL DEFAULT 'info',
|
||||
source_url TEXT,
|
||||
starts_at TEXT,
|
||||
ends_at TEXT,
|
||||
upstream_created_at TEXT,
|
||||
upstream_updated_at TEXT,
|
||||
first_seen_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
last_seen_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
read_at TEXT,
|
||||
dismissed_at TEXT,
|
||||
raw_payload TEXT,
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS site_announcements_site_source_key_unique
|
||||
ON site_announcements (site_id, source_key);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_announcements_site_id_first_seen_at_idx
|
||||
ON site_announcements (site_id, first_seen_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_announcements_read_at_idx
|
||||
ON site_announcements (read_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- OAUTH ROUTE UNITS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS oauth_route_units (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
provider TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
strategy TEXT NOT NULL DEFAULT 'round_robin',
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS oauth_route_units_site_provider_idx
|
||||
ON oauth_route_units (site_id, provider);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS oauth_route_units_enabled_idx
|
||||
ON oauth_route_units (enabled);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- OAUTH ROUTE UNIT MEMBERS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS oauth_route_unit_members (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
unit_id BIGINT NOT NULL,
|
||||
account_id BIGINT NOT NULL,
|
||||
sort_order BIGINT DEFAULT 0,
|
||||
success_count BIGINT DEFAULT 0,
|
||||
fail_count BIGINT DEFAULT 0,
|
||||
total_latency_ms BIGINT DEFAULT 0,
|
||||
total_cost DOUBLE PRECISION DEFAULT 0,
|
||||
last_used_at TEXT,
|
||||
last_selected_at TEXT,
|
||||
last_fail_at TEXT,
|
||||
consecutive_fail_count BIGINT NOT NULL DEFAULT 0,
|
||||
cooldown_level BIGINT NOT NULL DEFAULT 0,
|
||||
cooldown_until TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (unit_id) REFERENCES oauth_route_units(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS oauth_route_unit_members_unit_account_unique
|
||||
ON oauth_route_unit_members (unit_id, account_id);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS oauth_route_unit_members_account_unique
|
||||
ON oauth_route_unit_members (account_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS oauth_route_unit_members_unit_sort_idx
|
||||
ON oauth_route_unit_members (unit_id, sort_order);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS oauth_route_unit_members_unit_cooldown_idx
|
||||
ON oauth_route_unit_members (unit_id, cooldown_until);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- SETTINGS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY NOT NULL,
|
||||
value TEXT
|
||||
);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- ADMIN SNAPSHOTS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS admin_snapshots (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
namespace TEXT NOT NULL,
|
||||
snapshot_key TEXT NOT NULL,
|
||||
payload TEXT NOT NULL,
|
||||
generated_at TEXT NOT NULL,
|
||||
expires_at TEXT NOT NULL,
|
||||
stale_until TEXT NOT NULL,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS admin_snapshots_namespace_key_unique
|
||||
ON admin_snapshots (namespace, snapshot_key);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS admin_snapshots_expires_at_idx
|
||||
ON admin_snapshots (expires_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS admin_snapshots_stale_until_idx
|
||||
ON admin_snapshots (stale_until);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- ANALYTICS PROJECTION CHECKPOINTS
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS analytics_projection_checkpoints (
|
||||
projector_key TEXT PRIMARY KEY NOT NULL,
|
||||
time_zone TEXT NOT NULL DEFAULT 'Local',
|
||||
last_proxy_log_id BIGINT NOT NULL DEFAULT 0,
|
||||
watermark_created_at TEXT,
|
||||
recompute_from_id BIGINT,
|
||||
recompute_requested_at TEXT,
|
||||
recompute_reason TEXT,
|
||||
recompute_started_at TEXT,
|
||||
recompute_completed_at TEXT,
|
||||
last_projected_at TEXT,
|
||||
last_successful_at TEXT,
|
||||
last_error TEXT,
|
||||
lease_owner TEXT,
|
||||
lease_token TEXT,
|
||||
lease_expires_at TEXT,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS analytics_projection_checkpoints_recompute_from_id_idx
|
||||
ON analytics_projection_checkpoints (recompute_from_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS analytics_projection_checkpoints_lease_expires_at_idx
|
||||
ON analytics_projection_checkpoints (lease_expires_at);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- MODEL DAY USAGE
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS model_day_usage (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
local_day TEXT NOT NULL,
|
||||
site_id BIGINT NOT NULL,
|
||||
model TEXT NOT NULL,
|
||||
total_calls BIGINT NOT NULL DEFAULT 0,
|
||||
success_calls BIGINT NOT NULL DEFAULT 0,
|
||||
failed_calls BIGINT NOT NULL DEFAULT 0,
|
||||
total_tokens BIGINT NOT NULL DEFAULT 0,
|
||||
total_spend DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
total_latency_ms BIGINT NOT NULL DEFAULT 0,
|
||||
latency_count BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE,
|
||||
CONSTRAINT model_day_usage_non_negative CHECK (
|
||||
total_calls >= 0
|
||||
AND success_calls >= 0
|
||||
AND failed_calls >= 0
|
||||
AND total_tokens >= 0
|
||||
AND total_spend >= 0
|
||||
AND total_latency_ms >= 0
|
||||
AND latency_count >= 0
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS model_day_usage_day_site_model_unique
|
||||
ON model_day_usage (local_day, site_id, model);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS model_day_usage_day_idx
|
||||
ON model_day_usage (local_day);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS model_day_usage_site_id_idx
|
||||
ON model_day_usage (site_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS model_day_usage_model_idx
|
||||
ON model_day_usage (model);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- SITE DAY USAGE
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS site_day_usage (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
local_day TEXT NOT NULL,
|
||||
site_id BIGINT NOT NULL,
|
||||
total_calls BIGINT NOT NULL DEFAULT 0,
|
||||
success_calls BIGINT NOT NULL DEFAULT 0,
|
||||
failed_calls BIGINT NOT NULL DEFAULT 0,
|
||||
total_tokens BIGINT NOT NULL DEFAULT 0,
|
||||
total_summary_spend DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
total_site_spend DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
total_latency_ms BIGINT NOT NULL DEFAULT 0,
|
||||
latency_count BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE,
|
||||
CONSTRAINT site_day_usage_non_negative CHECK (
|
||||
total_calls >= 0
|
||||
AND success_calls >= 0
|
||||
AND failed_calls >= 0
|
||||
AND total_tokens >= 0
|
||||
AND total_summary_spend >= 0
|
||||
AND total_site_spend >= 0
|
||||
AND total_latency_ms >= 0
|
||||
AND latency_count >= 0
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS site_day_usage_day_site_unique
|
||||
ON site_day_usage (local_day, site_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_day_usage_day_idx
|
||||
ON site_day_usage (local_day);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_day_usage_site_id_idx
|
||||
ON site_day_usage (site_id);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- SITE HOUR USAGE
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS site_hour_usage (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
bucket_start_utc TEXT NOT NULL,
|
||||
site_id BIGINT NOT NULL,
|
||||
total_calls BIGINT NOT NULL DEFAULT 0,
|
||||
success_calls BIGINT NOT NULL DEFAULT 0,
|
||||
failed_calls BIGINT NOT NULL DEFAULT 0,
|
||||
total_tokens BIGINT NOT NULL DEFAULT 0,
|
||||
total_summary_spend DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
total_site_spend DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
total_latency_ms BIGINT NOT NULL DEFAULT 0,
|
||||
latency_count BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT),
|
||||
FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE,
|
||||
CONSTRAINT site_hour_usage_non_negative CHECK (
|
||||
total_calls >= 0
|
||||
AND success_calls >= 0
|
||||
AND failed_calls >= 0
|
||||
AND total_tokens >= 0
|
||||
AND total_summary_spend >= 0
|
||||
AND total_site_spend >= 0
|
||||
AND total_latency_ms >= 0
|
||||
AND latency_count >= 0
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS site_hour_usage_hour_site_unique
|
||||
ON site_hour_usage (bucket_start_utc, site_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_hour_usage_hour_idx
|
||||
ON site_hour_usage (bucket_start_utc);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS site_hour_usage_site_id_idx
|
||||
ON site_hour_usage (site_id);
|
||||
Reference in New Issue
Block a user