initial commit
Some checks failed
ci / go (push) Has been cancelled
ci / admin-ui (push) Has been cancelled
ci / runtime-sdk (push) Has been cancelled
ci / helm (push) Has been cancelled
ci / openapi (push) Has been cancelled
ci / container (push) Has been cancelled
ci / release-config (push) Has been cancelled
ci / shared styling (push) Has been cancelled
ci / security (push) Has been cancelled
ci / all green (push) Has been cancelled
Some checks failed
ci / go (push) Has been cancelled
ci / admin-ui (push) Has been cancelled
ci / runtime-sdk (push) Has been cancelled
ci / helm (push) Has been cancelled
ci / openapi (push) Has been cancelled
ci / container (push) Has been cancelled
ci / release-config (push) Has been cancelled
ci / shared styling (push) Has been cancelled
ci / security (push) Has been cancelled
ci / all green (push) Has been cancelled
This commit is contained in:
commit
951dd8d26b
175 changed files with 50041 additions and 0 deletions
144
internal/store/migrations/0001_init.sql
Normal file
144
internal/store/migrations/0001_init.sql
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
-- go-bananas schema.
|
||||
--
|
||||
-- Design notes
|
||||
-- ------------
|
||||
-- * Timestamps are TEXT in RFC 3339 (see domain.TimeFormat). SQLite has no
|
||||
-- native date type, and storing a sortable string removes every date-parsing
|
||||
-- ambiguity from ORDER BY and range filters.
|
||||
-- * JSON columns are TEXT holding a JSON document. The token document is read
|
||||
-- and written as a whole value, never queried into, so a JSON column is the
|
||||
-- correct shape: it avoids a join table whose only purpose would be to
|
||||
-- reassemble a tree the application always wants whole.
|
||||
-- * Every table a consumer might read carries an explicit covering index.
|
||||
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
-- ---------------------------------------------------------------- themes ----
|
||||
|
||||
CREATE TABLE IF NOT EXISTS themes (
|
||||
id TEXT PRIMARY KEY,
|
||||
slug TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'draft'
|
||||
CHECK (status IN ('draft', 'published', 'archived')),
|
||||
default_appearance TEXT NOT NULL DEFAULT 'light'
|
||||
CHECK (default_appearance IN ('light', 'dark')),
|
||||
tokens_json TEXT NOT NULL,
|
||||
meta_json TEXT NOT NULL DEFAULT '{}',
|
||||
is_system INTEGER NOT NULL DEFAULT 0 CHECK (is_system IN (0, 1)),
|
||||
version INTEGER NOT NULL DEFAULT 1 CHECK (version >= 1),
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
created_by TEXT NOT NULL DEFAULT '',
|
||||
updated_by TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
-- The runtime API resolves by slug on every page load, so this index is the
|
||||
-- single most important one in the schema.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_themes_slug ON themes (slug);
|
||||
|
||||
-- Listing is always ordered by recency within a status filter.
|
||||
CREATE INDEX IF NOT EXISTS idx_themes_status_updated
|
||||
ON themes (status, updated_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_themes_updated ON themes (updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_themes_created ON themes (created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_themes_name ON themes (name COLLATE NOCASE);
|
||||
|
||||
-- Free-text search over slug, name and description. SQLite's LIKE with a
|
||||
-- leading wildcard cannot use an index, so this stays a scan; the table is
|
||||
-- small by construction (themes are hand-authored, not machine-generated) and
|
||||
-- the scan is served from the page cache.
|
||||
CREATE INDEX IF NOT EXISTS idx_themes_search
|
||||
ON themes (name COLLATE NOCASE, slug, description);
|
||||
|
||||
-- ---------------------------------------------------------- theme_versions --
|
||||
|
||||
CREATE TABLE IF NOT EXISTS theme_versions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
theme_id TEXT NOT NULL REFERENCES themes (id) ON DELETE CASCADE,
|
||||
version INTEGER NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL,
|
||||
default_appearance TEXT NOT NULL,
|
||||
tokens_json TEXT NOT NULL,
|
||||
message TEXT NOT NULL DEFAULT '',
|
||||
author TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
UNIQUE (theme_id, version)
|
||||
);
|
||||
|
||||
-- Rollback resolves a theme's history newest-first.
|
||||
CREATE INDEX IF NOT EXISTS idx_versions_theme
|
||||
ON theme_versions (theme_id, version DESC);
|
||||
|
||||
-- ---------------------------------------------------------------- api_keys --
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_keys (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
-- prefix is the non-secret leading segment shown in the UI so a key can be
|
||||
-- identified after its secret has been forgotten.
|
||||
prefix TEXT NOT NULL,
|
||||
-- secret_hash is a SHA-256 of the full key. The plaintext is never stored,
|
||||
-- so a database disclosure does not yield usable credentials.
|
||||
secret_hash TEXT NOT NULL,
|
||||
scopes TEXT NOT NULL,
|
||||
identity TEXT NOT NULL DEFAULT '',
|
||||
created_by TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
last_used_at TEXT,
|
||||
expires_at TEXT,
|
||||
revoked_at TEXT
|
||||
);
|
||||
|
||||
-- Lookup by secret hash happens on every machine request.
|
||||
CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys (secret_hash);
|
||||
-- The UI lists live keys newest-first and hides revoked ones by default.
|
||||
CREATE INDEX IF NOT EXISTS idx_api_keys_live ON api_keys (revoked_at, created_at DESC);
|
||||
|
||||
-- ----------------------------------------------------------------- users ----
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
subject TEXT PRIMARY KEY,
|
||||
email TEXT NOT NULL DEFAULT '',
|
||||
display_name TEXT NOT NULL DEFAULT '',
|
||||
roles TEXT NOT NULL DEFAULT '[]',
|
||||
method TEXT NOT NULL DEFAULT '',
|
||||
first_seen TEXT NOT NULL,
|
||||
last_login TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users (email);
|
||||
|
||||
-- ------------------------------------------------------------- audit_log ----
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
at TEXT NOT NULL,
|
||||
actor TEXT NOT NULL DEFAULT '',
|
||||
actor_email TEXT NOT NULL DEFAULT '',
|
||||
action TEXT NOT NULL,
|
||||
entity_type TEXT NOT NULL DEFAULT '',
|
||||
entity_id TEXT NOT NULL DEFAULT '',
|
||||
summary TEXT NOT NULL DEFAULT '',
|
||||
detail_json TEXT NOT NULL DEFAULT '{}',
|
||||
remote_ip TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_at ON audit_log (at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit_log (actor, at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_entity ON audit_log (entity_type, entity_id, at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log (action, at DESC);
|
||||
|
||||
-- -------------------------------------------------------------- settings ----
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
updated_by TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue