target-revenue/migrations/0007_forgejo_hubs.sql

85 lines
3.2 KiB
MySQL
Raw Normal View History

-- WP-0015-T05 (specs/PhaseProvenanceSpecAddendum.md §4): the Forgejo
-- hub -> service URI registry.
-- Depends on migrations/0001_registries.sql (phase_manifests).
--
-- Per WP-0012-T02's accepted decision: target-revenue is the generic
-- framework, not a specific deployment's list of repos it monetizes, so
-- this mapping is hosted Trust Service data (parallel to
-- licensor_identities, migrations/0005_licensor_credentials.sql), never
-- a file in this git repo. A Phase Manifest stays fully self-describing
-- and offline-verifiable regardless -- it already carries
-- repo_hub_uri/repo_name directly at registration time
-- (specs/PhaseLifecycleUseCases.md use case 9). This table exists purely
-- as an admin/repair convenience: if a hub's URI ever changes, there is
-- one row to correct rather than every affected Phase Manifest.
BEGIN;
CREATE TABLE IF NOT EXISTS forgejo_hubs (
hub_slug text PRIMARY KEY,
service_uri text NOT NULL,
first_seen_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
GRANT SELECT, INSERT ON forgejo_hubs TO trf_app;
-- No UPDATE/DELETE grant for trf_app -- correcting a hub's URI after the
-- fact is a governance action (correct_forgejo_hub_uri() below), same
-- pattern as set_extension_status()/revoke_credential(), not a route
-- the application's ordinary write path can take.
-- Auto-populate on first sight of a repo_hub during Phase registration --
-- registry.register_phase_manifest() never needs to know this table
-- exists; the trigger reads repo_hub/repo_hub_uri straight out of the
-- manifest JSONB it was already given, mirroring
-- ensure_licensor_identity()'s auto-create-on-first-INSERT pattern.
-- ON CONFLICT DO NOTHING: a hub already seen is left alone here -- URI
-- corrections go through correct_forgejo_hub_uri() below, not a silent
-- overwrite on the next unrelated Phase registration.
CREATE OR REPLACE FUNCTION ensure_forgejo_hub() RETURNS trigger
LANGUAGE plpgsql
AS $$
DECLARE
v_hub_slug text := NEW.manifest #>> '{phase,milestone_release,repo_hub}';
v_hub_uri text := NEW.manifest #>> '{phase,milestone_release,repo_hub_uri}';
BEGIN
IF v_hub_slug IS NOT NULL AND v_hub_uri IS NOT NULL THEN
INSERT INTO forgejo_hubs (hub_slug, service_uri)
VALUES (v_hub_slug, v_hub_uri)
ON CONFLICT (hub_slug) DO NOTHING;
END IF;
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS phase_manifests_ensure_forgejo_hub ON phase_manifests;
CREATE TRIGGER phase_manifests_ensure_forgejo_hub
BEFORE INSERT ON phase_manifests
FOR EACH ROW
EXECUTE FUNCTION ensure_forgejo_hub();
-- Correcting a hub's URI after the fact (a domain move, etc.) is a
-- recorded governance action, not an ordinary UPDATE -- trf_app has no
-- UPDATE grant on forgejo_hubs at all.
CREATE OR REPLACE FUNCTION correct_forgejo_hub_uri(
p_hub_slug text,
p_new_uri text
) RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
UPDATE forgejo_hubs
SET service_uri = p_new_uri,
updated_at = now()
WHERE hub_slug = p_hub_slug;
IF NOT FOUND THEN
RAISE EXCEPTION 'unknown forgejo hub: %', p_hub_slug;
END IF;
END;
$$;
GRANT EXECUTE ON FUNCTION correct_forgejo_hub_uri(text, text) TO trf_app;
COMMIT;