feat: import Chinese-localized Buzz source snapshot
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled

Signed-off-by: cls_宁波本机 <908705107@qq.com>
This commit is contained in:
2026-08-13 18:34:25 +08:00
parent 61c3fa1df9
commit 9dfa06ffee
3785 changed files with 1085458 additions and 2 deletions
@@ -0,0 +1,290 @@
-- scripts/cutover/1321_backfill_default_community.sql
--
-- One-off cutover: single-community (pre-1321) Postgres -> multi-tenant (1321)
-- schema. Assigns EVERY existing row to one default community derived from the
-- deployment host.
--
-- ─────────────────────────────────────────────────────────────────────────────
-- STRATEGY: rename pre-1321 objects aside into a `legacy` schema, rebuild the
-- correct 1321 schema in `public` from 0001 VERBATIM, copy data
-- forward, drop `legacy`.
--
-- Why not an in-place ALTER ("just add community_id and re-key")? The 1321
-- schema differs from pre-1321 by far MORE than a prepended community_id, and
-- hand-transcribing the difference is how silent isolation defects get in (local
-- validation caught an in-place draft that left 5 tables single-tenant-keyed and
-- dropped ~13 FKs). The real delta:
-- * channels gains ttl_seconds, ttl_deadline
-- * events gains not_before, delivered_at, search_tsv (GENERATED) + GIN
-- * EVERY scoped table is re-keyed to a community-leading PK/UNIQUE
-- * EVERY hot-path index is replaced with a community-leading one
-- * channels gains a community_id immutability trigger
-- * 6 brand-new tables (communities, scheduled_workflow_fires, relay_members,
-- archived_identities, audit_log, _operator_global_tables)
--
-- So we do NOT describe the target by hand. We let 1321's own
-- migrations/0001_initial_schema.sql define it, verbatim, into a clean `public`.
-- The result is structurally identical to a fresh 1321 DB BY CONSTRUCTION.
--
-- The one obstacle to running the from-scratch 0001 against an existing DB is
-- name collisions: 0001 issues bare `CREATE TYPE <enum>` (Postgres has no
-- CREATE TYPE IF NOT EXISTS) and `CREATE TABLE <name>`. We clear `public` of
-- those names first by renaming the pre-1321 tables AND their enum types into a
-- `legacy` schema. `CREATE EXTENSION IF NOT EXISTS pgcrypto` in 0001 is a no-op
-- (idempotent), and pgcrypto stays in public untouched.
--
-- Phases (all in ONE transaction; all-or-nothing):
-- A CREATE SCHEMA legacy; move every pre-1321 table + enum type into it
-- B \i the repo's 0001 -> full correct 1321 schema in clean public
-- C create the one default community from :host
-- D INSERT ... SELECT default_community, <explicit cols> from each legacy.*
-- (enum cols cast ::text::public.<enum>; search_tsv regenerates; new
-- columns default NULL)
-- E DROP SCHEMA legacy CASCADE
-- ─────────────────────────────────────────────────────────────────────────────
--
-- PRECONDITIONS
-- * DB is the pre-1321 single-community schema (no community_id columns, no
-- `communities` table). Guard below refuses to run otherwise.
-- * Run psql with this script via -f (not piped on stdin): the Phase B
-- include below uses \ir, which resolves relative to THIS file, so the
-- repo's migrations/0001 is found regardless of the operator's cwd.
-- * TAKE A pg_dump / PVC SNAPSHOT FIRST. No down-path; the snapshot IS the
-- rollback.
--
-- USAGE (run from anywhere; \ir below resolves the schema include relative to
-- this file, so cwd does not matter)
-- psql "$DATABASE_URL" \
-- -v host="'sprout-oss.stage.blox.sqprod.co'" \
-- -v ON_ERROR_STOP=1 \
-- -f scripts/cutover/1321_backfill_default_community.sql
--
-- After commit: boot the 1321 relay with BUZZ_AUTO_MIGRATE=false. The schema is
-- already correct; the relay's idempotent boot-time
-- ensure_configured_community / allowlist->relay_members backfill finds this
-- community and no-ops. (If you instead want the relay's sqlx migrator to
-- consider 0001 "applied", seed its _sqlx_migrations row — see the runbook;
-- not required for correct operation.)
\set ON_ERROR_STOP on
BEGIN;
-- ── Guard: refuse to run on anything but pristine pre-1321 ───────────────────
DO $guard$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.tables
WHERE table_schema='public' AND table_name='communities') THEN
RAISE EXCEPTION
'communities table already exists -- DB is not pristine pre-1321 '
'(already migrated). Refusing to run.';
END IF;
IF EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_schema='public' AND column_name='community_id') THEN
RAISE EXCEPTION
'a community_id column already exists -- DB is not pristine pre-1321. '
'Refusing to run.';
END IF;
IF EXISTS (SELECT 1 FROM information_schema.schemata
WHERE schema_name='legacy') THEN
RAISE EXCEPTION
'schema legacy already exists -- leftover from a failed run. '
'Drop it (DROP SCHEMA legacy CASCADE) and retry.';
END IF;
END
$guard$;
-- ── Phase A: move pre-1321 tables + enum types into `legacy` ─────────────────
CREATE SCHEMA legacy;
-- Tables (partitioned parents move with their partitions via SET SCHEMA on the
-- parent? -- no: SET SCHEMA does NOT move partition children. So we move the
-- parent AND each partition. Simpler and safe: move every base/partitioned/
-- partition relation owned in public that is one of our pre-1321 tables.)
DO $move_tables$
DECLARE r RECORD;
BEGIN
FOR r IN
SELECT c.relname
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public'
AND c.relkind IN ('r','p') -- ordinary + partitioned parents
ORDER BY c.relkind DESC -- move partition children before parents? not needed
LOOP
EXECUTE format('ALTER TABLE public.%I SET SCHEMA legacy', r.relname);
END LOOP;
END
$move_tables$;
-- Enum types (must leave public so 0001 can CREATE TYPE with the same names).
DO $move_types$
DECLARE r RECORD;
BEGIN
FOR r IN
SELECT t.typname
FROM pg_type t JOIN pg_namespace n ON n.oid = t.typnamespace
WHERE n.nspname = 'public' AND t.typtype = 'e'
LOOP
EXECUTE format('ALTER TYPE public.%I SET SCHEMA legacy', r.typname);
END LOOP;
END
$move_types$;
-- ── Phase B: the authoritative 1321 schema, verbatim, into clean public ──────
-- \ir = include relative to THIS script's directory (scripts/cutover/), so the
-- repo's migrations/0001 resolves regardless of the operator's cwd.
\ir ../../migrations/0001_initial_schema.sql
-- ── Phase C: the one default community, normalized like the relay does ───────
INSERT INTO public.communities (host)
VALUES (rtrim(lower(:host), '.'));
SELECT id AS default_community
FROM public.communities
WHERE lower(host) = rtrim(lower(:host), '.') \gset
-- ── Phase D: copy data forward ───────────────────────────────────────────────
-- Source legacy.*, target public.*. Enum columns cross a type boundary
-- (legacy.<enum> -> public.<enum>); same labels, so ::text::public.<enum> is
-- lossless. search_tsv omitted (GENERATED); new-in-1321 columns omitted (NULL).
INSERT INTO public.channels
(community_id, id, name, channel_type, visibility, description, canvas,
created_by, created_at, updated_at, archived_at, deleted_at, nip29_group_id,
topic_required, max_members, topic, topic_set_by, topic_set_at, purpose,
purpose_set_by, purpose_set_at, participant_hash)
SELECT :'default_community', id, name,
channel_type::text::public.channel_type,
visibility::text::public.channel_visibility,
description, canvas, created_by, created_at, updated_at, archived_at,
deleted_at, nip29_group_id, topic_required, max_members, topic, topic_set_by,
topic_set_at, purpose, purpose_set_by, purpose_set_at, participant_hash
FROM legacy.channels;
INSERT INTO public.channel_members
(community_id, channel_id, pubkey, role, joined_at, invited_by, removed_at,
removed_by, hidden_at)
SELECT :'default_community', channel_id, pubkey,
role::text::public.member_role,
joined_at, invited_by, removed_at, removed_by, hidden_at
FROM legacy.channel_members;
INSERT INTO public.users
(community_id, pubkey, nip05_handle, display_name, avatar_url, about,
agent_type, capabilities, okta_user_id, created_at, updated_at,
deactivated_at, metadata_event_id, agent_owner_pubkey, channel_add_policy)
SELECT :'default_community', pubkey, nip05_handle, display_name, avatar_url, about,
agent_type, capabilities, okta_user_id, created_at, updated_at,
deactivated_at, metadata_event_id, agent_owner_pubkey,
channel_add_policy::text::public.channel_add_policy
FROM legacy.users;
INSERT INTO public.events
(community_id, id, pubkey, created_at, kind, tags, content, sig,
received_at, channel_id, deleted_at, d_tag)
SELECT :'default_community', id, pubkey, created_at, kind, tags, content, sig,
received_at, channel_id, deleted_at, d_tag
FROM legacy.events;
INSERT INTO public.event_mentions
(community_id, pubkey_hex, event_id, event_created_at, channel_id, event_kind)
SELECT :'default_community', pubkey_hex, event_id, event_created_at, channel_id, event_kind
FROM legacy.event_mentions;
INSERT INTO public.subscriptions
(community_id, id, owner_pubkey, filter_kinds, filter_authors,
filter_channel_ids, filter_since, filter_until, delivery_method,
delivery_url, status, pause_reason, delivered_count, error_count,
created_at, updated_at)
SELECT :'default_community', id, owner_pubkey, filter_kinds, filter_authors,
filter_channel_ids, filter_since, filter_until,
delivery_method::text::public.delivery_method, delivery_url,
status::text::public.subscription_status,
pause_reason::text::public.pause_reason,
delivered_count, error_count, created_at, updated_at
FROM legacy.subscriptions;
-- delivery_log.id is GENERATED ALWAYS AS IDENTITY; preserve original ids.
INSERT INTO public.delivery_log
(community_id, id, subscription_id, event_id, method, delivered_at, success,
http_status, error_message, attempt_number)
OVERRIDING SYSTEM VALUE
SELECT :'default_community', id, subscription_id, event_id,
method::text::public.delivery_method, delivered_at, success,
http_status, error_message, attempt_number
FROM legacy.delivery_log;
INSERT INTO public.workflows
(community_id, id, name, owner_pubkey, channel_id, definition,
definition_hash, status, enabled, created_at, updated_at)
SELECT :'default_community', id, name, owner_pubkey, channel_id, definition,
definition_hash, status::text::public.workflow_status, enabled,
created_at, updated_at
FROM legacy.workflows;
INSERT INTO public.workflow_runs
(community_id, id, workflow_id, status, trigger_event_id, current_step,
execution_trace, trigger_context, started_at, completed_at, error_message,
created_at)
SELECT :'default_community', id, workflow_id,
status::text::public.run_status, trigger_event_id, current_step,
execution_trace, trigger_context, started_at, completed_at, error_message,
created_at
FROM legacy.workflow_runs;
INSERT INTO public.workflow_approvals
(community_id, token, workflow_id, run_id, step_id, step_index, approver_spec,
status, approver_pubkey, note, granted_at, denied_at, expires_at, created_at)
SELECT :'default_community', token, workflow_id, run_id, step_id, step_index,
approver_spec, status::text::public.approval_status, approver_pubkey,
note, granted_at, denied_at, expires_at, created_at
FROM legacy.workflow_approvals;
INSERT INTO public.api_tokens
(community_id, id, token_hash, owner_pubkey, name, scopes, channel_ids,
created_at, expires_at, last_used_at, revoked_at, revoked_by,
created_by_self_mint)
SELECT :'default_community', id, token_hash, owner_pubkey, name, scopes, channel_ids,
created_at, expires_at, last_used_at, revoked_at, revoked_by,
created_by_self_mint
FROM legacy.api_tokens;
-- rate_limit_violations: OPERATOR-GLOBAL; community_id nullable attribution.
-- id is IDENTITY; preserve.
INSERT INTO public.rate_limit_violations
(id, community_id, pubkey, violation_at, limit_type, limit_value,
actual_value, action_taken)
OVERRIDING SYSTEM VALUE
SELECT id, :'default_community', pubkey, violation_at, limit_type, limit_value,
actual_value, action_taken
FROM legacy.rate_limit_violations;
INSERT INTO public.thread_metadata
(community_id, event_created_at, event_id, channel_id, parent_event_id,
parent_event_created_at, root_event_id, root_event_created_at, depth,
reply_count, descendant_count, last_reply_at, broadcast)
SELECT :'default_community', event_created_at, event_id, channel_id, parent_event_id,
parent_event_created_at, root_event_id, root_event_created_at, depth,
reply_count, descendant_count, last_reply_at, broadcast
FROM legacy.thread_metadata;
INSERT INTO public.reactions
(community_id, event_created_at, event_id, pubkey, emoji, created_at,
removed_at, reaction_event_id)
SELECT :'default_community', event_created_at, event_id, pubkey, emoji, created_at,
removed_at, reaction_event_id
FROM legacy.reactions;
INSERT INTO public.pubkey_allowlist
(community_id, pubkey, added_by, added_at, note)
SELECT :'default_community', pubkey, added_by, added_at, note
FROM legacy.pubkey_allowlist;
-- ── Phase E: retire the pre-1321 objects ─────────────────────────────────────
DROP SCHEMA legacy CASCADE;
COMMIT;
-- ── Post-commit sanity (run manually; not part of the txn) ───────────────────
-- SELECT count(*) FROM communities; -- expect 1
-- SELECT count(*) FROM events WHERE community_id IS NULL; -- expect 0
-- SELECT to_regnamespace('legacy'); -- expect NULL (gone)