Files
accounted/supabase/migrations/20260513200000_api_v1_async_operations.sql
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

82 lines
3.3 KiB
SQL

-- Migration: api_v1_async_operations
--
-- Substrate for the v1 async-operation lifecycle. Distinct from
-- `pending_operations` (which is the "stage and wait for human approval"
-- substrate used by the MCP write tools) — this table tracks long-running
-- jobs initiated by v1 callers that the API needs to report progress + final
-- status against, without blocking the request cycle.
--
-- Response contract (per the Phase 4 plan):
-- POST returns 202 with { operation_id, status: 'queued', poll_url, webhook_event }
-- GET /v1/operations/{id} returns { operation_id, type, status, progress, result, error, started_at, completed_at }
--
-- Used by:
-- - POST /fiscal-periods/{id}/close
-- - POST /fiscal-periods/{id}/year-end
-- - POST /fiscal-periods/{id}/currency-revaluation
-- - POST /imports/sie (future PR)
-- - POST /imports/bank (future PR)
-- - POST /salary-runs/{id}/generate-agi (future PR)
--
-- Phase 4 PR-2 ships this with synchronous execution inside the POST handler
-- (status flips queued → running → succeeded/failed in one request cycle).
-- A future PR can introduce a Vercel cron worker that picks up `queued` rows
-- and processes them out-of-band; the row format remains stable.
CREATE TABLE IF NOT EXISTS public.operations (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
-- Tenancy
company_id uuid NOT NULL REFERENCES public.companies(id) ON DELETE CASCADE,
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
-- Operation identity
operation_type text NOT NULL,
-- Free-form tag for which v1 surface initiated this op (e.g.
-- 'fiscal_periods.close', 'fiscal_periods.year_end', 'imports.sie').
-- The set of accepted values is open by design — adding a new async
-- endpoint should not require an enum migration.
-- Lifecycle
status text NOT NULL DEFAULT 'queued'
CHECK (status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')),
started_at timestamptz,
completed_at timestamptz,
-- Payload
params jsonb NOT NULL DEFAULT '{}'::jsonb,
progress jsonb NOT NULL DEFAULT '{}'::jsonb,
result jsonb,
error jsonb,
-- Audit
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE public.operations ENABLE ROW LEVEL SECURITY;
-- Members of the company can read their company's operations. Only the
-- service role writes (via the v1 wrapper); no anon/authenticated INSERT/
-- UPDATE/DELETE policy is exposed.
CREATE POLICY "operations_select"
ON public.operations FOR SELECT
USING (company_id IN (SELECT public.user_company_ids()));
CREATE TRIGGER operations_updated_at
BEFORE UPDATE ON public.operations
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
-- Indexes:
-- - Polling: GET /v1/operations/{id} is point-lookup on PK.
-- - Listing by company + recency for future GET /v1/operations endpoint.
-- - Worker queries (future cron): pick up oldest `queued` ops per company.
CREATE INDEX idx_operations_company_created
ON public.operations (company_id, created_at DESC);
CREATE INDEX idx_operations_status_queued
ON public.operations (created_at)
WHERE status = 'queued';
NOTIFY pgrst, 'reload schema';