refactor(tenant): drop legacy plugin_installations, migrate monitoring to desktop_clients

Hard cutover from the browser-extension plugin flow to desktop clients:
remove plugin_installations/plugin_sessions tables and related service,
handler, router, and generated model code; migrate monitoring quotas
and collector types to desktop_clients (UUID primary_client_id);
recreate platform_access_snapshots keyed by client_id; update dev-seed
and callback types accordingly; mark legacy design docs as historical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 13:52:35 +08:00
parent f5a1cd81b2
commit 5ff2e2e74c
25 changed files with 535 additions and 1384 deletions
@@ -1,6 +1,4 @@
DROP TABLE IF EXISTS publish_records;
DROP TABLE IF EXISTS publish_batches;
DROP TABLE IF EXISTS plugin_sessions;
DROP TABLE IF EXISTS plugin_installations;
DROP TABLE IF EXISTS platform_accounts;
DROP TABLE IF EXISTS media_platforms;
@@ -53,50 +53,6 @@ CREATE UNIQUE INDEX uk_platform_accounts_identity_active
WHERE deleted_at IS NULL;
CREATE INDEX idx_platform_accounts_tenant_platform ON platform_accounts(tenant_id, platform_id, status, created_at DESC);
CREATE TABLE plugin_installations (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
user_id BIGINT NOT NULL REFERENCES users(id),
installation_key VARCHAR(128) NOT NULL,
installation_name VARCHAR(255) NOT NULL,
browser_name VARCHAR(64),
client_version VARCHAR(32),
installation_token_hash VARCHAR(128) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active',
last_seen_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX uk_plugin_installations_key_active
ON plugin_installations(tenant_id, installation_key)
WHERE deleted_at IS NULL;
CREATE INDEX idx_plugin_installations_tenant_status
ON plugin_installations(tenant_id, status, created_at DESC);
CREATE TABLE plugin_sessions (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
user_id BIGINT NOT NULL REFERENCES users(id),
plugin_installation_id BIGINT REFERENCES plugin_installations(id),
action_type VARCHAR(20) NOT NULL,
resource_type VARCHAR(30),
resource_id BIGINT,
platform_account_id BIGINT REFERENCES platform_accounts(id),
platform_id VARCHAR(64) NOT NULL REFERENCES media_platforms(platform_id),
session_token VARCHAR(128) NOT NULL,
client_version VARCHAR(32),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
expire_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX uk_plugin_sessions_token ON plugin_sessions(session_token);
CREATE INDEX idx_plugin_sessions_status_expire ON plugin_sessions(status, expire_at);
CREATE INDEX idx_plugin_sessions_tenant_action ON plugin_sessions(tenant_id, action_type, created_at DESC);
CREATE TABLE publish_batches (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
@@ -1,4 +1 @@
-- No-op rollback.
-- This migration repairs databases that already applied an older variant of
-- 20260402140000 without plugin_installations. Dropping these objects on
-- rollback would break databases created from the current baseline migration.
-- Legacy no-op rollback.
@@ -1,39 +1 @@
CREATE TABLE IF NOT EXISTS plugin_installations (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
user_id BIGINT NOT NULL REFERENCES users(id),
installation_key VARCHAR(128) NOT NULL,
installation_name VARCHAR(255) NOT NULL,
browser_name VARCHAR(64),
client_version VARCHAR(32),
installation_token_hash VARCHAR(128) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active',
last_seen_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX IF NOT EXISTS uk_plugin_installations_key_active
ON plugin_installations(tenant_id, installation_key)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_plugin_installations_tenant_status
ON plugin_installations(tenant_id, status, created_at DESC);
ALTER TABLE plugin_sessions
ADD COLUMN IF NOT EXISTS plugin_installation_id BIGINT;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'plugin_sessions_plugin_installation_id_fkey'
) THEN
ALTER TABLE plugin_sessions
ADD CONSTRAINT plugin_sessions_plugin_installation_id_fkey
FOREIGN KEY (plugin_installation_id)
REFERENCES plugin_installations(id);
END IF;
END $$;
-- Legacy no-op.
@@ -1,11 +1,11 @@
CREATE TABLE tenant_monitoring_quotas (
tenant_id BIGINT PRIMARY KEY REFERENCES tenants(id),
max_brands INT NOT NULL DEFAULT 1,
collection_mode VARCHAR(20) NOT NULL DEFAULT 'plugin',
collection_mode VARCHAR(20) NOT NULL DEFAULT 'desktop',
question_selection_mode VARCHAR(20) NOT NULL DEFAULT 'best_effort_all',
collect_frequency VARCHAR(20) NOT NULL DEFAULT 'daily',
enabled_platforms JSONB NOT NULL DEFAULT '["deepseek","qwen","doubao"]'::jsonb,
primary_installation_id BIGINT REFERENCES plugin_installations(id),
primary_client_id UUID,
task_daily_hard_cap INT NOT NULL DEFAULT 36,
plan_tier VARCHAR(20) NOT NULL DEFAULT 'free',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
@@ -0,0 +1,53 @@
CREATE TABLE IF NOT EXISTS plugin_installations (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
user_id BIGINT NOT NULL REFERENCES users(id),
installation_key VARCHAR(128) NOT NULL,
installation_name VARCHAR(255) NOT NULL,
browser_name VARCHAR(64),
client_version VARCHAR(32),
installation_token_hash VARCHAR(128) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active',
last_seen_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX IF NOT EXISTS uk_plugin_installations_key_active
ON plugin_installations(tenant_id, installation_key)
WHERE deleted_at IS NULL;
CREATE TABLE IF NOT EXISTS plugin_sessions (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
user_id BIGINT NOT NULL REFERENCES users(id),
plugin_installation_id BIGINT REFERENCES plugin_installations(id),
action_type VARCHAR(20) NOT NULL,
resource_type VARCHAR(30),
resource_id BIGINT,
platform_account_id BIGINT REFERENCES platform_accounts(id),
platform_id VARCHAR(64) NOT NULL REFERENCES media_platforms(platform_id),
session_token VARCHAR(128) NOT NULL,
client_version VARCHAR(32),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
expire_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ALTER TABLE tenant_monitoring_quotas
DROP CONSTRAINT IF EXISTS tenant_monitoring_quotas_primary_client_id_fkey;
ALTER TABLE tenant_monitoring_quotas
ADD COLUMN IF NOT EXISTS primary_installation_id BIGINT REFERENCES plugin_installations(id);
UPDATE tenant_monitoring_quotas
SET collection_mode = 'plugin'
WHERE collection_mode = 'desktop';
ALTER TABLE tenant_monitoring_quotas
ALTER COLUMN collection_mode SET DEFAULT 'plugin';
ALTER TABLE tenant_monitoring_quotas
DROP COLUMN IF EXISTS primary_client_id;
@@ -0,0 +1,32 @@
ALTER TABLE tenant_monitoring_quotas
DROP CONSTRAINT IF EXISTS tenant_monitoring_quotas_primary_installation_id_fkey;
ALTER TABLE tenant_monitoring_quotas
ADD COLUMN IF NOT EXISTS primary_client_id UUID;
UPDATE tenant_monitoring_quotas
SET collection_mode = 'desktop'
WHERE collection_mode = 'plugin';
ALTER TABLE tenant_monitoring_quotas
ALTER COLUMN collection_mode SET DEFAULT 'desktop';
ALTER TABLE tenant_monitoring_quotas
DROP COLUMN IF EXISTS primary_installation_id;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'tenant_monitoring_quotas_primary_client_id_fkey'
) THEN
ALTER TABLE tenant_monitoring_quotas
ADD CONSTRAINT tenant_monitoring_quotas_primary_client_id_fkey
FOREIGN KEY (primary_client_id)
REFERENCES desktop_clients(id);
END IF;
END $$;
DROP TABLE IF EXISTS plugin_sessions;
DROP TABLE IF EXISTS plugin_installations;