72 lines
3.1 KiB
PL/PgSQL
72 lines
3.1 KiB
PL/PgSQL
-- 0011_ingest_state.sql
|
|
--
|
|
-- Makes photo ingestion a managed, Cockpit-governed service instead of a one-off
|
|
-- batch. One singleton row per Person holds both the CONTROL plane (the Cockpit
|
|
-- toggles enabled / requests a run / pauses) and the STATUS plane (the
|
|
-- content-ingestor worker reports progress here after each batch). The worker
|
|
-- talks only to platform.api (single API plane) — it never reaches iOS directly;
|
|
-- iOS reads/writes this state through platform.api's /ingestion endpoints.
|
|
--
|
|
-- Layers on users/orgs (0001). Reuses 0001 helpers: current_user_uuid() (RLS) +
|
|
-- touch_updated_at(). platform.db is Postgres ≥ 14 (INFRA.md §1).
|
|
|
|
BEGIN;
|
|
|
|
CREATE TYPE ingest_run_state AS ENUM ('idle', 'running', 'paused');
|
|
|
|
CREATE TABLE ingest_state (
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
org_id UUID NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
|
|
-- Control plane (set by the Cockpit via POST /ingestion/control)
|
|
enabled BOOLEAN NOT NULL DEFAULT FALSE, -- continuous polling on/off
|
|
run_requested BOOLEAN NOT NULL DEFAULT FALSE, -- one-shot "run now" trigger; worker clears it
|
|
|
|
-- Status plane (reported by the worker via PUT /ingestion/progress)
|
|
state ingest_run_state NOT NULL DEFAULT 'idle',
|
|
cursor TIMESTAMPTZ NULL, -- newest-first paging cursor (photos.taken_at)
|
|
total_photos INTEGER NOT NULL DEFAULT 0, -- library size with uploaded bytes
|
|
processed INTEGER NOT NULL DEFAULT 0, -- assets classified+landed so far
|
|
hot_count INTEGER NOT NULL DEFAULT 0,
|
|
stocked_count INTEGER NOT NULL DEFAULT 0,
|
|
explicit_count INTEGER NOT NULL DEFAULT 0,
|
|
failed_count INTEGER NOT NULL DEFAULT 0,
|
|
last_run_at TIMESTAMPTZ NULL,
|
|
last_error TEXT NULL,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
|
|
PRIMARY KEY (user_id),
|
|
CONSTRAINT ingest_state_counts_chk CHECK (
|
|
total_photos >= 0 AND processed >= 0 AND hot_count >= 0
|
|
AND stocked_count >= 0 AND explicit_count >= 0 AND failed_count >= 0
|
|
)
|
|
);
|
|
CREATE INDEX idx_ingest_state_org ON ingest_state(org_id) WHERE org_id IS NOT NULL;
|
|
|
|
CREATE TRIGGER trg_touch_ingest_state BEFORE UPDATE ON ingest_state
|
|
FOR EACH ROW EXECUTE FUNCTION touch_updated_at();
|
|
|
|
-- Row-level security (per 0001 convention). current_user_uuid() reads the
|
|
-- app.current_user_id GUC set by platform.api per request.
|
|
ALTER TABLE ingest_state ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY tenant_isolation ON ingest_state
|
|
USING (
|
|
user_id = current_user_uuid()
|
|
OR (org_id IS NOT NULL
|
|
AND org_id IN (SELECT org_id FROM org_members
|
|
WHERE user_id = current_user_uuid()))
|
|
)
|
|
WITH CHECK (
|
|
user_id = current_user_uuid()
|
|
OR (org_id IS NOT NULL
|
|
AND org_id IN (SELECT org_id FROM org_members
|
|
WHERE user_id = current_user_uuid()))
|
|
);
|
|
|
|
COMMENT ON TABLE ingest_state IS
|
|
'Singleton-per-Person control+status for photo ingestion. Cockpit sets enabled/run_requested; the content-ingestor worker reports progress. Makes ingestion a managed service surfaced in the iOS Cockpit.';
|
|
|
|
COMMIT;
|