Add full sortof codebase: API, drain workers, frontend, schema, specs
This commit is contained in:
127
init/01_schema.sql
Normal file
127
init/01_schema.sql
Normal file
@@ -0,0 +1,127 @@
|
||||
-- pzsort schema
|
||||
-- Run as: psql -U pzsort -d pzsort -f schema.sql
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- workshop_meta
|
||||
-- Cheap, refreshed often via Steam ISteamRemoteStorage/GetPublishedFileDetails.
|
||||
-- Keyed by Steam publishedfileid (text to avoid bigint surprises).
|
||||
-- time_updated is the cache-invalidation key for mod_parsed.
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS workshop_meta (
|
||||
workshop_id TEXT PRIMARY KEY,
|
||||
title TEXT,
|
||||
description TEXT,
|
||||
tags TEXT[] NOT NULL DEFAULT '{}',
|
||||
creator_steamid TEXT,
|
||||
time_created BIGINT, -- unix ts from Steam
|
||||
time_updated BIGINT NOT NULL, -- unix ts; cache invalidation key
|
||||
file_size BIGINT,
|
||||
preview_url TEXT,
|
||||
consumer_app_id INTEGER, -- 108600 for PZ
|
||||
visibility INTEGER, -- 0=public, 1=friends, 2=private
|
||||
banned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
last_checked_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS workshop_meta_last_checked_idx
|
||||
ON workshop_meta (last_checked_at);
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- mod_parsed
|
||||
-- Expensive: requires DepotDownloader fetch. Only refreshed when
|
||||
-- workshop_meta.time_updated changes vs parsed_at_time_updated.
|
||||
-- One workshop item can yield N rows (multi-mod packages).
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS mod_parsed (
|
||||
workshop_id TEXT NOT NULL REFERENCES workshop_meta(workshop_id) ON DELETE CASCADE,
|
||||
mod_id TEXT NOT NULL, -- mod.info `id=`
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
category TEXT NOT NULL DEFAULT 'undefined',
|
||||
requirements TEXT[] NOT NULL DEFAULT '{}',
|
||||
load_after TEXT[] NOT NULL DEFAULT '{}',
|
||||
load_before TEXT[] NOT NULL DEFAULT '{}',
|
||||
incompatible_mods TEXT[] NOT NULL DEFAULT '{}',
|
||||
load_first TEXT NOT NULL DEFAULT 'off',
|
||||
load_last TEXT NOT NULL DEFAULT 'off',
|
||||
tags TEXT[] NOT NULL DEFAULT '{}',
|
||||
maps TEXT[] NOT NULL DEFAULT '{}', -- map folder names
|
||||
raw_mod_info TEXT, -- original file for debugging
|
||||
version_min TEXT, -- e.g. 41.55
|
||||
parsed_at_time_updated BIGINT NOT NULL, -- snapshot of workshop_meta.time_updated at parse
|
||||
parsed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (workshop_id, mod_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS mod_parsed_mod_id_idx ON mod_parsed (mod_id);
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- download_jobs
|
||||
-- Work queue for the DepotDownloader worker. One job per workshop_id.
|
||||
-- Worker dequeues (status='queued') ORDER BY priority DESC, created_at ASC.
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS download_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workshop_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'queued', -- queued|downloading|parsing|done|failed
|
||||
priority INTEGER NOT NULL DEFAULT 0, -- higher first
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
error TEXT,
|
||||
requested_by TEXT, -- IP hash or user token; for rate limiting
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS download_jobs_dequeue_idx
|
||||
ON download_jobs (status, priority DESC, created_at ASC)
|
||||
WHERE status = 'queued';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS download_jobs_workshop_idx
|
||||
ON download_jobs (workshop_id);
|
||||
|
||||
-- Trigger: keep updated_at fresh
|
||||
CREATE OR REPLACE FUNCTION touch_updated_at() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS download_jobs_touch ON download_jobs;
|
||||
CREATE TRIGGER download_jobs_touch
|
||||
BEFORE UPDATE ON download_jobs
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION touch_updated_at();
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- collections
|
||||
-- Cache for ISteamRemoteStorage/GetCollectionDetails results.
|
||||
-- Collections expand to N child workshop_ids; we cache that mapping.
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS collections (
|
||||
collection_id TEXT PRIMARY KEY,
|
||||
title TEXT,
|
||||
child_workshop_ids TEXT[] NOT NULL DEFAULT '{}',
|
||||
last_fetched_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- sort_requests
|
||||
-- Optional: log of submitted sort jobs for debugging + abuse triage.
|
||||
-- Not required for sort to function. Keep TTL short via a cron.
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS sort_requests (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
input_workshop_ids TEXT[] NOT NULL,
|
||||
input_collection_id TEXT,
|
||||
cache_hits INTEGER NOT NULL DEFAULT 0,
|
||||
cache_misses INTEGER NOT NULL DEFAULT 0,
|
||||
requested_by TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS sort_requests_created_idx
|
||||
ON sort_requests (created_at);
|
||||
Reference in New Issue
Block a user