Files
story/migrations/000050_device_activation_state_machine.up.sql
T

77 lines
3.1 KiB
PL/PgSQL
Raw Normal View History

2026-09-06 17:04:11 +08:00
BEGIN;
ALTER TABLE device
ADD COLUMN activation_state varchar(24),
ADD COLUMN activation_revision bigint NOT NULL DEFAULT 1,
ADD COLUMN activation_reason text,
ADD COLUMN activation_updated_at timestamptz NOT NULL DEFAULT now();
UPDATE device d
SET activation_state = CASE
WHEN d.status = 'revoked' THEN 'REVOKED'
WHEN EXISTS (SELECT 1 FROM device_binding b WHERE b.device_id=d.id AND b.unbound_at IS NULL)
AND EXISTS (SELECT 1 FROM device_credential dc WHERE dc.device_id=d.id AND dc.status='active')
AND (
COALESCE((SELECT dt.desired_config->>'voice_runtime' FROM device_twin dt WHERE dt.device_id=d.id),'legacy_voicechat') <> 'volc_hardware_agent'
OR EXISTS (
SELECT 1 FROM hardware_agent_provider_credential pc
WHERE pc.device_id=d.id AND pc.status='ACTIVE'
)
) THEN 'ACTIVE'
WHEN EXISTS (
SELECT 1 FROM device_binding b
WHERE b.device_id=d.id AND b.unbound_at IS NULL
) THEN 'BOUND'
ELSE 'CLAIMABLE'
END,
activation_reason = 'migration-50-backfill';
ALTER TABLE device
ALTER COLUMN activation_state SET NOT NULL,
ALTER COLUMN activation_state SET DEFAULT 'FACTORY',
ADD CONSTRAINT device_activation_state_check
CHECK (activation_state IN ('FACTORY','CLAIMABLE','BOUND','PROVISIONING','ACTIVE','LOST','REVOKED'));
CREATE INDEX device_tenant_activation_state_idx
ON device(tenant_id,activation_state,activation_updated_at DESC);
CREATE TABLE device_activation_transition (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
tenant_id uuid NOT NULL REFERENCES tenant(id),
device_id uuid NOT NULL REFERENCES device(id),
from_state varchar(24),
to_state varchar(24) NOT NULL,
reason text NOT NULL,
source varchar(80) NOT NULL,
actor_type varchar(40) NOT NULL,
actor_public_id uuid,
request_id varchar(160),
activation_revision bigint NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT device_activation_transition_from_check CHECK (
from_state IS NULL OR from_state IN ('FACTORY','CLAIMABLE','BOUND','PROVISIONING','ACTIVE','LOST','REVOKED')
),
CONSTRAINT device_activation_transition_to_check CHECK (
to_state IN ('FACTORY','CLAIMABLE','BOUND','PROVISIONING','ACTIVE','LOST','REVOKED')
),
UNIQUE(device_id,activation_revision)
);
CREATE INDEX device_activation_transition_tenant_device_idx
ON device_activation_transition(tenant_id,device_id,created_at DESC);
INSERT INTO device_activation_transition(
tenant_id,device_id,from_state,to_state,reason,source,actor_type,activation_revision
)
SELECT tenant_id,id,NULL,activation_state,'migration-50-backfill','migration','system',activation_revision
FROM device;
ALTER TABLE hardware_agent_provider_credential
DROP CONSTRAINT hardware_agent_provider_credential_status_check;
ALTER TABLE hardware_agent_provider_credential
ADD CONSTRAINT hardware_agent_provider_credential_status_check
CHECK (status IN ('ACTIVE','SUSPENDED','REVOKED'));
COMMIT;