feat(device-core): add idempotent project management

This commit is contained in:
Codex
2026-08-10 17:27:07 +03:00
parent 336602c7ca
commit 70bafdd028
10 changed files with 2260 additions and 2 deletions
@@ -0,0 +1,68 @@
begin;
do $$
begin
if not exists (
select 1
from pg_constraint
where conname = 'device_project_grants_owner_user_only'
and conrelid = 'device_project_grants'::regclass
) then
alter table device_project_grants
add constraint device_project_grants_owner_user_only
check (project_role <> 'owner' or principal_kind = 'user');
end if;
end
$$;
alter table device_audit_events
add column if not exists project_id uuid references device_projects(id);
create index if not exists device_audit_events_project_time_idx
on device_audit_events (project_id, occurred_at desc);
create table if not exists device_management_command_receipts (
id uuid primary key,
actor_ref text not null
check (length(btrim(actor_ref)) between 3 and 256),
command_kind text not null
check (command_kind in (
'owner_scope.ensure',
'project.ensure',
'collection.ensure',
'project_grant.upsert'
)),
idempotency_key text not null
check (length(idempotency_key) between 8 and 256),
request_digest text not null
check (request_digest ~ '^sha256:[a-f0-9]{64}$'),
lifecycle_state text not null default 'pending'
check (lifecycle_state in ('pending', 'completed')),
response_status integer
check (response_status between 200 and 599),
response_body jsonb,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
completed_at timestamptz,
unique (actor_ref, command_kind, idempotency_key),
check (
(
lifecycle_state = 'pending'
and response_status is null
and response_body is null
and completed_at is null
)
or
(
lifecycle_state = 'completed'
and response_status is not null
and response_body is not null
and completed_at is not null
)
)
);
create index if not exists device_management_receipts_created_idx
on device_management_command_receipts (created_at desc);
commit;