0027_migration_requests.sql 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. -- 0027_migration_requests — customer-initiated migration intake.
  2. -- The dashboard wizard at /dashboard/projects/new/migrate writes one
  3. -- row per source-platform import request. During beta, every row is
  4. -- triaged by an operator via /dashboard/admin/migrations and either
  5. -- handled by the (forthcoming) automated adapter pipeline or migrated
  6. -- by hand for free. Once the adapter for a source ships, status
  7. -- transitions through `scheduled` → `in_progress` → `completed`
  8. -- without an operator in the loop.
  9. CREATE TABLE IF NOT EXISTS "migration_requests" (
  10. "id" text PRIMARY KEY NOT NULL,
  11. -- Requester. user_id is always set; org_id is the org the migrated
  12. -- project should land in, defaulting to the user's personal org.
  13. "user_id" text NOT NULL,
  14. "org_id" text,
  15. -- Source platform. Open vocabulary so we can add adapters without a
  16. -- migration; the wizard restricts the picker to known sources.
  17. "source" text NOT NULL,
  18. "source_url" text,
  19. "source_notes" text NOT NULL DEFAULT '',
  20. -- Rough scale signals the operator uses to triage queue order.
  21. "estimated_tables" integer,
  22. "estimated_rows" bigint,
  23. "estimated_functions" integer,
  24. -- Urgency lets the customer self-report their timeline so we don't
  25. -- have to ask twice. Values: exploring | this_week | this_month | this_quarter.
  26. "urgency" text NOT NULL DEFAULT 'exploring',
  27. -- Lifecycle. new → contacted → scheduled → in_progress → completed
  28. -- (terminal) | cancelled (terminal).
  29. "status" text NOT NULL DEFAULT 'new',
  30. "contact_email" text NOT NULL,
  31. -- Operator-only fields. Customer never sees these.
  32. "assigned_to" text,
  33. "operator_notes" text NOT NULL DEFAULT '',
  34. "created_at" timestamp with time zone DEFAULT now() NOT NULL,
  35. "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
  36. CONSTRAINT "migration_requests_user_fk"
  37. FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE,
  38. CONSTRAINT "migration_requests_org_fk"
  39. FOREIGN KEY ("org_id") REFERENCES "organizations"("id") ON DELETE SET NULL,
  40. CONSTRAINT "migration_requests_assigned_fk"
  41. FOREIGN KEY ("assigned_to") REFERENCES "users"("id") ON DELETE SET NULL
  42. );
  43. -- Triage queue: operator opens the admin page sorted newest-first.
  44. CREATE INDEX IF NOT EXISTS "migration_requests_created_idx"
  45. ON "migration_requests" USING btree ("created_at" DESC);
  46. -- Hot path for the customer's own list ("my migration requests").
  47. CREATE INDEX IF NOT EXISTS "migration_requests_user_idx"
  48. ON "migration_requests" USING btree ("user_id", "created_at" DESC);
  49. -- "How many open requests do we have?" — drives the admin nav badge
  50. -- and the operator's at-a-glance load.
  51. CREATE INDEX IF NOT EXISTS "migration_requests_open_idx"
  52. ON "migration_requests" USING btree ("created_at" DESC)
  53. WHERE "status" NOT IN ('completed', 'cancelled');