devpush 0.0.0-stage → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/README.md +6 -2
  2. package/agent-release.json +37 -0
  3. package/dist/package/LICENSE.md +21 -0
  4. package/dist/package/migrate.mjs +182 -0
  5. package/dist/package/plugins.mjs +125 -0
  6. package/dist/package/vite.mjs +439 -0
  7. package/package.json +170 -4
  8. package/public/apple-touch-icon.png +0 -0
  9. package/public/favicon.svg +1 -0
  10. package/public/logo-email.png +0 -0
  11. package/public/robots.txt +3 -0
  12. package/public/social.png +0 -0
  13. package/src/cloudflare-runtime.d.ts +78 -0
  14. package/src/components/about-dialog.tsx +70 -0
  15. package/src/components/copy-button.tsx +74 -0
  16. package/src/components/logo-mark.tsx +20 -0
  17. package/src/components/pending-collection.tsx +103 -0
  18. package/src/components/resource-avatar.tsx +65 -0
  19. package/src/components/route-error-state.tsx +95 -0
  20. package/src/components/section-navigation.tsx +39 -0
  21. package/src/components/settings-layout.tsx +111 -0
  22. package/src/components/storage-capabilities.tsx +11 -0
  23. package/src/components/table-pager.tsx +101 -0
  24. package/src/components/ui/alert-dialog.tsx +180 -0
  25. package/src/components/ui/alert.tsx +78 -0
  26. package/src/components/ui/avatar.tsx +58 -0
  27. package/src/components/ui/badge.tsx +49 -0
  28. package/src/components/ui/button-group.tsx +38 -0
  29. package/src/components/ui/button.tsx +67 -0
  30. package/src/components/ui/card.tsx +82 -0
  31. package/src/components/ui/chart.tsx +307 -0
  32. package/src/components/ui/checkbox.tsx +30 -0
  33. package/src/components/ui/combobox.tsx +256 -0
  34. package/src/components/ui/dialog.tsx +163 -0
  35. package/src/components/ui/dropdown-menu.tsx +110 -0
  36. package/src/components/ui/empty.tsx +65 -0
  37. package/src/components/ui/field.tsx +192 -0
  38. package/src/components/ui/input-group.tsx +118 -0
  39. package/src/components/ui/input-otp.tsx +70 -0
  40. package/src/components/ui/input.tsx +19 -0
  41. package/src/components/ui/label.tsx +24 -0
  42. package/src/components/ui/pagination.tsx +34 -0
  43. package/src/components/ui/popover.tsx +86 -0
  44. package/src/components/ui/select.tsx +145 -0
  45. package/src/components/ui/skeleton.tsx +19 -0
  46. package/src/components/ui/sonner.tsx +47 -0
  47. package/src/components/ui/switch.tsx +31 -0
  48. package/src/components/ui/table.tsx +81 -0
  49. package/src/components/ui/textarea.tsx +18 -0
  50. package/src/components/ui/tooltip.tsx +55 -0
  51. package/src/features/admin/admin-settings.tsx +1048 -0
  52. package/src/features/auth/current-user-context-query.ts +11 -0
  53. package/src/features/auth/login-form.tsx +442 -0
  54. package/src/features/auth/sign-out-menu-item.tsx +42 -0
  55. package/src/features/deployments/deployment-actions.tsx +336 -0
  56. package/src/features/deployments/deployment-alias-badge.tsx +35 -0
  57. package/src/features/deployments/deployment-detail.tsx +596 -0
  58. package/src/features/deployments/deployment-filters.ts +66 -0
  59. package/src/features/deployments/deployment-list.tsx +465 -0
  60. package/src/features/deployments/deployment-visit-links.tsx +104 -0
  61. package/src/features/deployments/deployments-page.tsx +376 -0
  62. package/src/features/logs/log-scroll-area.tsx +69 -0
  63. package/src/features/notifications/email-preferences.tsx +232 -0
  64. package/src/features/onboarding/profile-onboarding.tsx +162 -0
  65. package/src/features/plugins/contributions.tsx +118 -0
  66. package/src/features/projects/backup-job-logs.tsx +143 -0
  67. package/src/features/projects/backup-policy-delete-dialog.tsx +82 -0
  68. package/src/features/projects/backup-restore-actions.tsx +573 -0
  69. package/src/features/projects/backup-status-icon.tsx +33 -0
  70. package/src/features/projects/backup-target-list.tsx +222 -0
  71. package/src/features/projects/backup-timestamp.tsx +32 -0
  72. package/src/features/projects/backup-volume-picker.tsx +158 -0
  73. package/src/features/projects/compose-service-input.tsx +64 -0
  74. package/src/features/projects/deploy-readiness.ts +28 -0
  75. package/src/features/projects/environment-dot.tsx +91 -0
  76. package/src/features/projects/log-keyword-filter.tsx +48 -0
  77. package/src/features/projects/manual-deploy-dialog.tsx +563 -0
  78. package/src/features/projects/pending-section.ts +26 -0
  79. package/src/features/projects/project-backup-details-pending.tsx +305 -0
  80. package/src/features/projects/project-backup-details.tsx +936 -0
  81. package/src/features/projects/project-backup-policy.tsx +644 -0
  82. package/src/features/projects/project-backup-utils.ts +190 -0
  83. package/src/features/projects/project-backups-pending.tsx +136 -0
  84. package/src/features/projects/project-backups.tsx +876 -0
  85. package/src/features/projects/project-environment-settings.tsx +1128 -0
  86. package/src/features/projects/project-heading.tsx +68 -0
  87. package/src/features/projects/project-layout.tsx +188 -0
  88. package/src/features/projects/project-logs.tsx +461 -0
  89. package/src/features/projects/project-overview.tsx +280 -0
  90. package/src/features/projects/project-route-pending.tsx +1064 -0
  91. package/src/features/projects/project-routing-settings.tsx +663 -0
  92. package/src/features/projects/project-settings-section.tsx +64 -0
  93. package/src/features/projects/project-settings-utils.ts +101 -0
  94. package/src/features/projects/project-settings.tsx +1472 -0
  95. package/src/features/projects/repository-picker-dialog.tsx +404 -0
  96. package/src/features/repositories/new-project-dialog-launcher.tsx +44 -0
  97. package/src/features/repositories/new-project-dialog.tsx +1204 -0
  98. package/src/features/repositories/new-project-pending.tsx +28 -0
  99. package/src/features/repositories/repository-list-pending.tsx +59 -0
  100. package/src/features/servers/server-availability-alert.tsx +44 -0
  101. package/src/features/servers/server-availability.ts +29 -0
  102. package/src/features/servers/server-avatar.tsx +52 -0
  103. package/src/features/servers/server-detail-pending.tsx +315 -0
  104. package/src/features/servers/server-detail.tsx +855 -0
  105. package/src/features/servers/server-metric-chart-pending.tsx +38 -0
  106. package/src/features/servers/server-metric-chart.tsx +182 -0
  107. package/src/features/servers/server-metric-format.ts +50 -0
  108. package/src/features/servers/server-metrics-pending.tsx +75 -0
  109. package/src/features/servers/server-metrics.tsx +136 -0
  110. package/src/features/servers/team-servers.tsx +432 -0
  111. package/src/features/settings/settings-route-pending.tsx +406 -0
  112. package/src/features/teams/backup-storage-settings.tsx +483 -0
  113. package/src/features/teams/navigation.ts +51 -0
  114. package/src/features/teams/new-team-dialog.tsx +132 -0
  115. package/src/features/teams/pending-section.ts +19 -0
  116. package/src/features/teams/team-header.tsx +1286 -0
  117. package/src/features/teams/team-heading.tsx +57 -0
  118. package/src/features/teams/team-home.tsx +391 -0
  119. package/src/features/teams/team-invite.tsx +155 -0
  120. package/src/features/teams/team-route-pending.tsx +505 -0
  121. package/src/features/teams/team-settings.tsx +1555 -0
  122. package/src/features/teams/use-workspace-updates.ts +126 -0
  123. package/src/features/users/api-keys-settings.tsx +544 -0
  124. package/src/features/users/api-scope-selection.tsx +269 -0
  125. package/src/features/users/connected-apps-settings.tsx +449 -0
  126. package/src/features/users/mcp-consent.tsx +220 -0
  127. package/src/features/users/profile-picture.tsx +171 -0
  128. package/src/features/users/user-settings.tsx +1064 -0
  129. package/src/lib/account-settings-schema.ts +78 -0
  130. package/src/lib/action-feedback.ts +81 -0
  131. package/src/lib/admin-search-schema.ts +8 -0
  132. package/src/lib/agent-release.d.mts +21 -0
  133. package/src/lib/agent-release.mjs +90 -0
  134. package/src/lib/allowlist-schema.ts +79 -0
  135. package/src/lib/api-key-schema.ts +176 -0
  136. package/src/lib/auth/client.ts +20 -0
  137. package/src/lib/avatar-schema.ts +11 -0
  138. package/src/lib/backup-cron.ts +118 -0
  139. package/src/lib/backup-database.ts +41 -0
  140. package/src/lib/backup-feature.ts +2 -0
  141. package/src/lib/backup-paths.ts +7 -0
  142. package/src/lib/backup-policy-schema.ts +85 -0
  143. package/src/lib/backup-resource-schema.ts +12 -0
  144. package/src/lib/backup-restore-schema.ts +15 -0
  145. package/src/lib/backup-storage-schema.ts +55 -0
  146. package/src/lib/deferred-module.ts +38 -0
  147. package/src/lib/deployment-logs-query.ts +63 -0
  148. package/src/lib/display-name.ts +7 -0
  149. package/src/lib/error-message.ts +119 -0
  150. package/src/lib/form-errors.ts +10 -0
  151. package/src/lib/internal-redirect.ts +25 -0
  152. package/src/lib/manual-deploy-options-query.ts +21 -0
  153. package/src/lib/notification-preferences.ts +43 -0
  154. package/src/lib/onboarding.ts +11 -0
  155. package/src/lib/operation-logs-query.ts +51 -0
  156. package/src/lib/page-head.ts +14 -0
  157. package/src/lib/project-context-query.ts +257 -0
  158. package/src/lib/project-creation-schema.ts +64 -0
  159. package/src/lib/project-env-schema.ts +23 -0
  160. package/src/lib/project-environments-schema.ts +56 -0
  161. package/src/lib/project-routing-schema.ts +94 -0
  162. package/src/lib/project-runtime-config.ts +131 -0
  163. package/src/lib/project-settings-schema.ts +75 -0
  164. package/src/lib/public-api-schema.ts +128 -0
  165. package/src/lib/public-error.ts +337 -0
  166. package/src/lib/query.ts +13 -0
  167. package/src/lib/repository-selection-query.ts +11 -0
  168. package/src/lib/resource-cache.ts +30 -0
  169. package/src/lib/runtime-id.ts +19 -0
  170. package/src/lib/search.ts +14 -0
  171. package/src/lib/server-context-query.ts +43 -0
  172. package/src/lib/server-name-schema.ts +11 -0
  173. package/src/lib/settings-conflict.ts +45 -0
  174. package/src/lib/settings-version.ts +7 -0
  175. package/src/lib/team-context-query.ts +126 -0
  176. package/src/lib/team-context-schema.ts +8 -0
  177. package/src/lib/team-forms-schema.ts +45 -0
  178. package/src/lib/team-role.ts +13 -0
  179. package/src/lib/team-settings-schema.ts +48 -0
  180. package/src/lib/urls.ts +3 -0
  181. package/src/lib/use-avatar-input.ts +61 -0
  182. package/src/lib/use-refresh-error.ts +18 -0
  183. package/src/lib/use-settings-version.ts +23 -0
  184. package/src/lib/utils.ts +6 -0
  185. package/src/lib/validation.ts +5 -0
  186. package/src/lib/workspace-updates.ts +53 -0
  187. package/src/package/config.ts +10 -0
  188. package/src/package/env.mjs +54 -0
  189. package/src/package/migrate-api.d.ts +5 -0
  190. package/src/package/migrate.ts +56 -0
  191. package/src/package/plugins.ts +141 -0
  192. package/src/package/sqlite.mjs +2 -0
  193. package/src/package/vite-api.d.ts +5 -0
  194. package/src/package/vite.ts +324 -0
  195. package/src/package/watch-guard.cjs +26 -0
  196. package/src/plugins/client.ts +57 -0
  197. package/src/plugins/contract.ts +188 -0
  198. package/src/plugins/default.server.ts +4 -0
  199. package/src/plugins/default.ui.ts +4 -0
  200. package/src/plugins/operations.ts +45 -0
  201. package/src/plugins/server.ts +197 -0
  202. package/src/plugins/version.ts +1 -0
  203. package/src/routeTree.gen.ts +1275 -0
  204. package/src/router.tsx +46 -0
  205. package/src/routes/$teamSlug.index.tsx +53 -0
  206. package/src/routes/$teamSlug.new-project.tsx +13 -0
  207. package/src/routes/$teamSlug.projects.$projectName.backups.$backupId.tsx +40 -0
  208. package/src/routes/$teamSlug.projects.$projectName.backups.tsx +82 -0
  209. package/src/routes/$teamSlug.projects.$projectName.deployments.$deploymentId.tsx +52 -0
  210. package/src/routes/$teamSlug.projects.$projectName.deployments.tsx +86 -0
  211. package/src/routes/$teamSlug.projects.$projectName.logs.tsx +55 -0
  212. package/src/routes/$teamSlug.projects.$projectName.settings.tsx +51 -0
  213. package/src/routes/$teamSlug.projects.$projectName.tsx +106 -0
  214. package/src/routes/$teamSlug.projects.tsx +40 -0
  215. package/src/routes/$teamSlug.servers.$serverId.tsx +31 -0
  216. package/src/routes/$teamSlug.servers.index.tsx +59 -0
  217. package/src/routes/$teamSlug.servers.tsx +7 -0
  218. package/src/routes/$teamSlug.settings.tsx +56 -0
  219. package/src/routes/$teamSlug.tsx +86 -0
  220. package/src/routes/[.]well-known.oauth-authorization-server.api.auth.ts +13 -0
  221. package/src/routes/[.]well-known.oauth-protected-resource.mcp.ts +6 -0
  222. package/src/routes/__root.tsx +155 -0
  223. package/src/routes/admin.tsx +55 -0
  224. package/src/routes/api/agents/certificates.ts +21 -0
  225. package/src/routes/api/agents/credentials/rotate.ts +22 -0
  226. package/src/routes/api/agents/heartbeat.ts +42 -0
  227. package/src/routes/api/agents/job-logs.ts +22 -0
  228. package/src/routes/api/agents/jobs/complete.ts +22 -0
  229. package/src/routes/api/agents/jobs/next.ts +29 -0
  230. package/src/routes/api/agents/jobs/progress.ts +32 -0
  231. package/src/routes/api/agents/jobs/status.ts +22 -0
  232. package/src/routes/api/agents/register.ts +45 -0
  233. package/src/routes/api/agents/runtime-logs.ts +22 -0
  234. package/src/routes/api/agents/switch.ts +25 -0
  235. package/src/routes/api/auth/$.ts +38 -0
  236. package/src/routes/api/cron/backups.ts +33 -0
  237. package/src/routes/api/cron/maintenance.ts +30 -0
  238. package/src/routes/api/github/install.callback.ts +51 -0
  239. package/src/routes/api/github/install.ts +41 -0
  240. package/src/routes/api/github/webhook.ts +11 -0
  241. package/src/routes/api/health.ts +19 -0
  242. package/src/routes/api/install/agent.$token.ts +50 -0
  243. package/src/routes/api/install/agent.binary.ts +83 -0
  244. package/src/routes/api/install/agent.checksum.ts +45 -0
  245. package/src/routes/api/install/agent.ts +26 -0
  246. package/src/routes/api/plugins/$.ts +11 -0
  247. package/src/routes/api/v1/$.ts +15 -0
  248. package/src/routes/api/workspaces/updates.ts +8 -0
  249. package/src/routes/auth.login.tsx +50 -0
  250. package/src/routes/deployments.$deploymentId.unavailable.tsx +21 -0
  251. package/src/routes/index.tsx +23 -0
  252. package/src/routes/invites.$token.tsx +35 -0
  253. package/src/routes/mcp.ts +13 -0
  254. package/src/routes/oauth.consent.tsx +54 -0
  255. package/src/routes/onboarding.tsx +37 -0
  256. package/src/routes/onboarding.workspace.tsx +44 -0
  257. package/src/routes/settings.tsx +30 -0
  258. package/src/routes/uploads/avatars/$fileName.ts +44 -0
  259. package/src/server/admin/access.ts +67 -0
  260. package/src/server/admin/functions.ts +122 -0
  261. package/src/server/admin/service.ts +260 -0
  262. package/src/server/agents/backup-state.ts +210 -0
  263. package/src/server/agents/certificates.ts +96 -0
  264. package/src/server/agents/claims.ts +116 -0
  265. package/src/server/agents/cleanup.ts +52 -0
  266. package/src/server/agents/compatibility.ts +116 -0
  267. package/src/server/agents/completion.ts +83 -0
  268. package/src/server/agents/container-cleanup-state.ts +51 -0
  269. package/src/server/agents/deployment-state.ts +111 -0
  270. package/src/server/agents/job-payloads.ts +159 -0
  271. package/src/server/agents/jobs.ts +1293 -0
  272. package/src/server/agents/protocol.ts +400 -0
  273. package/src/server/agents/reconciliation.ts +43 -0
  274. package/src/server/agents/runtime-logs.ts +337 -0
  275. package/src/server/agents/runtime-state.ts +125 -0
  276. package/src/server/api/admission.ts +12 -0
  277. package/src/server/api/authorization.ts +115 -0
  278. package/src/server/api/credentials.ts +569 -0
  279. package/src/server/api/cursors.ts +72 -0
  280. package/src/server/api/errors.ts +24 -0
  281. package/src/server/api/functions.ts +60 -0
  282. package/src/server/api/handler.ts +274 -0
  283. package/src/server/api/mutation-operations.ts +913 -0
  284. package/src/server/api/operation-context.ts +139 -0
  285. package/src/server/api/operation-contract.ts +1099 -0
  286. package/src/server/api/operations.ts +64 -0
  287. package/src/server/api/read-operations.ts +891 -0
  288. package/src/server/api/retention.ts +33 -0
  289. package/src/server/auth/access.ts +109 -0
  290. package/src/server/auth/auth-methods.ts +28 -0
  291. package/src/server/auth/better-auth.ts +518 -0
  292. package/src/server/auth/context.ts +28 -0
  293. package/src/server/auth/email-change.ts +47 -0
  294. package/src/server/auth/email-delivery.ts +51 -0
  295. package/src/server/auth/provider-identity.ts +19 -0
  296. package/src/server/auth/provider-tokens.ts +27 -0
  297. package/src/server/auth/session.server.ts +19 -0
  298. package/src/server/backups/database-commands.ts +40 -0
  299. package/src/server/backups/destination.ts +12 -0
  300. package/src/server/backups/feature.ts +6 -0
  301. package/src/server/backups/policy-validation.ts +51 -0
  302. package/src/server/backups/queue-state.ts +137 -0
  303. package/src/server/backups/service.ts +918 -0
  304. package/src/server/backups/storage-functions.ts +42 -0
  305. package/src/server/backups/storage-state.ts +67 -0
  306. package/src/server/backups/storage.ts +237 -0
  307. package/src/server/config.ts +274 -0
  308. package/src/server/db/atomic.ts +16 -0
  309. package/src/server/db/d1-connection.ts +83 -0
  310. package/src/server/db/errors.ts +17 -0
  311. package/src/server/db/index.cloudflare.ts +15 -0
  312. package/src/server/db/index.ts +35 -0
  313. package/src/server/db/libsql-connection.ts +72 -0
  314. package/src/server/db/log-schema.ts +102 -0
  315. package/src/server/db/migrate-libsql.ts +29 -0
  316. package/src/server/db/migrate-sqlite.ts +47 -0
  317. package/src/server/db/migration-history.ts +12 -0
  318. package/src/server/db/migrations-logs/0000_initial.sql +48 -0
  319. package/src/server/db/migrations-logs/0001_runtime_log_order.sql +2 -0
  320. package/src/server/db/migrations-logs/meta/0000_snapshot.json +327 -0
  321. package/src/server/db/migrations-logs/meta/0001_snapshot.json +329 -0
  322. package/src/server/db/migrations-logs/meta/_journal.json +20 -0
  323. package/src/server/db/migrations-sqlite/0000_initial.sql +894 -0
  324. package/src/server/db/migrations-sqlite/0001_agent_indexes.sql +5 -0
  325. package/src/server/db/migrations-sqlite/meta/0000_snapshot.json +5816 -0
  326. package/src/server/db/migrations-sqlite/meta/0001_snapshot.json +5841 -0
  327. package/src/server/db/migrations-sqlite/meta/_journal.json +20 -0
  328. package/src/server/db/oauth-schema.ts +216 -0
  329. package/src/server/db/remote-config.ts +47 -0
  330. package/src/server/db/schema.ts +1218 -0
  331. package/src/server/db/sqlite-connection.ts +94 -0
  332. package/src/server/db/sqlite-files.ts +27 -0
  333. package/src/server/db/sqlite-params.ts +19 -0
  334. package/src/server/db/worker-connection.ts +47 -0
  335. package/src/server/deleted-resources.ts +133 -0
  336. package/src/server/deletion-recovery.ts +272 -0
  337. package/src/server/email/layout.tsx +85 -0
  338. package/src/server/email/notification-template.tsx +62 -0
  339. package/src/server/email/otp-template.tsx +48 -0
  340. package/src/server/email/service.ts +251 -0
  341. package/src/server/email/team-invite-template.tsx +59 -0
  342. package/src/server/email/theme.ts +10 -0
  343. package/src/server/github/http.ts +14 -0
  344. package/src/server/github/install-state.ts +48 -0
  345. package/src/server/github/service.ts +831 -0
  346. package/src/server/github/webhook-handler.ts +198 -0
  347. package/src/server/github/webhooks.ts +205 -0
  348. package/src/server/http/auth.ts +11 -0
  349. package/src/server/http/error-diagnostics.ts +66 -0
  350. package/src/server/http/plugin-request.ts +15 -0
  351. package/src/server/http/proxy-request.ts +53 -0
  352. package/src/server/http/public-errors.ts +48 -0
  353. package/src/server/http/rate-limit.ts +59 -0
  354. package/src/server/http/request-body.ts +54 -0
  355. package/src/server/http/request-ip.ts +54 -0
  356. package/src/server/http/security-headers.ts +36 -0
  357. package/src/server/install/agent-binary.ts +230 -0
  358. package/src/server/install/agent-script.ts +565 -0
  359. package/src/server/install/release-binary.ts +65 -0
  360. package/src/server/logs/budget.ts +72 -0
  361. package/src/server/logs/cleanup.ts +64 -0
  362. package/src/server/logs/deployments.ts +98 -0
  363. package/src/server/logs/operations.ts +145 -0
  364. package/src/server/logs/retention.ts +39 -0
  365. package/src/server/maintenance.ts +61 -0
  366. package/src/server/mcp/connections.ts +358 -0
  367. package/src/server/mcp/functions.ts +63 -0
  368. package/src/server/mcp/handler.ts +295 -0
  369. package/src/server/mcp/oauth-discovery.cloudflare.ts +6 -0
  370. package/src/server/mcp/oauth-discovery.ts +8 -0
  371. package/src/server/mcp/oauth-provider.ts +220 -0
  372. package/src/server/mcp/urls.ts +20 -0
  373. package/src/server/notifications/functions.ts +51 -0
  374. package/src/server/notifications/observe.ts +303 -0
  375. package/src/server/notifications/outbox.ts +335 -0
  376. package/src/server/notifications/preferences.ts +131 -0
  377. package/src/server/notifications/query.ts +16 -0
  378. package/src/server/plugins/scope.ts +17 -0
  379. package/src/server/plugins/server-context.ts +157 -0
  380. package/src/server/plugins/services.server.ts +10 -0
  381. package/src/server/plugins/workspace-access.ts +74 -0
  382. package/src/server/projects/backup-policy-state.ts +68 -0
  383. package/src/server/projects/backups.ts +219 -0
  384. package/src/server/projects/compose-metadata.ts +174 -0
  385. package/src/server/projects/configuration-state.ts +90 -0
  386. package/src/server/projects/context.ts +909 -0
  387. package/src/server/projects/creation-state.ts +46 -0
  388. package/src/server/projects/deletion-state.ts +69 -0
  389. package/src/server/projects/deployment-config.ts +139 -0
  390. package/src/server/projects/deployment-logs.ts +97 -0
  391. package/src/server/projects/deployments.ts +1123 -0
  392. package/src/server/projects/domain-state.ts +71 -0
  393. package/src/server/projects/environment-branches.ts +75 -0
  394. package/src/server/projects/environment-state.ts +158 -0
  395. package/src/server/projects/environments.ts +424 -0
  396. package/src/server/projects/functions.ts +748 -0
  397. package/src/server/projects/generated-hostname.ts +77 -0
  398. package/src/server/projects/hostnames.ts +244 -0
  399. package/src/server/projects/mappers.ts +457 -0
  400. package/src/server/projects/names.ts +41 -0
  401. package/src/server/projects/project-env-vars.ts +28 -0
  402. package/src/server/projects/queue-state.ts +118 -0
  403. package/src/server/projects/repository.ts +368 -0
  404. package/src/server/projects/route-state.ts +37 -0
  405. package/src/server/projects/runtime-logs.ts +97 -0
  406. package/src/server/projects/settings-context.ts +58 -0
  407. package/src/server/projects/settings.ts +738 -0
  408. package/src/server/projects/types.ts +355 -0
  409. package/src/server/realtime/agent-events.ts +32 -0
  410. package/src/server/realtime/node-broadcaster.ts +71 -0
  411. package/src/server/realtime/node-handler.ts +55 -0
  412. package/src/server/realtime/service.ts +125 -0
  413. package/src/server/realtime/workspace-object.cloudflare.ts +62 -0
  414. package/src/server/realtime/workspace-object.ts +3 -0
  415. package/src/server/resources/color.ts +13 -0
  416. package/src/server/runtime/background.ts +21 -0
  417. package/src/server/runtime/node-asset-cache-plugin.ts +12 -0
  418. package/src/server/runtime/node-scheduler-plugin.ts +17 -0
  419. package/src/server/runtime/node-scheduler.ts +51 -0
  420. package/src/server/runtime/platform-env.cloudflare.ts +17 -0
  421. package/src/server/runtime/platform-env.ts +19 -0
  422. package/src/server/runtime/scheduled-task-error.ts +9 -0
  423. package/src/server/scheduled-tasks.ts +28 -0
  424. package/src/server/secrets/crypto.ts +56 -0
  425. package/src/server/servers/functions.ts +233 -0
  426. package/src/server/servers/lifecycle.ts +122 -0
  427. package/src/server/servers/metric-history-schema.ts +24 -0
  428. package/src/server/servers/metric-history.ts +55 -0
  429. package/src/server/servers/monitoring.ts +592 -0
  430. package/src/server/servers/service.ts +1211 -0
  431. package/src/server/servers/status.ts +32 -0
  432. package/src/server/servers/switch.ts +96 -0
  433. package/src/server/settings-version.ts +43 -0
  434. package/src/server/storage/capabilities.ts +6 -0
  435. package/src/server/storage/local-object-store.cloudflare.ts +7 -0
  436. package/src/server/storage/local-object-store.ts +50 -0
  437. package/src/server/storage/object-store.ts +160 -0
  438. package/src/server/teams/creation.ts +51 -0
  439. package/src/server/teams/delete-policy.ts +13 -0
  440. package/src/server/teams/deletion-state.ts +80 -0
  441. package/src/server/teams/functions.ts +324 -0
  442. package/src/server/teams/invitations.ts +38 -0
  443. package/src/server/teams/membership.ts +86 -0
  444. package/src/server/teams/queries.ts +261 -0
  445. package/src/server/teams/service.ts +1127 -0
  446. package/src/server/teams/types.ts +101 -0
  447. package/src/server/uploads/avatar.ts +133 -0
  448. package/src/server/users/deletion-state.ts +93 -0
  449. package/src/server/users/functions.ts +160 -0
  450. package/src/server/users/onboarding.ts +17 -0
  451. package/src/server/users/service.ts +644 -0
  452. package/src/server.ts +55 -0
  453. package/src/styles/shadcn.css +653 -0
  454. package/src/styles/theme.css +76 -0
  455. package/src/styles.css +177 -0
  456. package/wrangler.template.jsonc +37 -0
@@ -0,0 +1,1218 @@
1
+ import { getTableName, sql } from 'drizzle-orm'
2
+ import {
3
+ check,
4
+ index,
5
+ integer,
6
+ real,
7
+ sqliteTable,
8
+ text,
9
+ uniqueIndex,
10
+ } from 'drizzle-orm/sqlite-core'
11
+ import type { SQLiteColumn } from 'drizzle-orm/sqlite-core'
12
+
13
+ export * from './oauth-schema'
14
+
15
+ function enumColumn<const T extends readonly [string, ...string[]]>(values: T) {
16
+ return (name: string) => text(name, { enum: values })
17
+ }
18
+
19
+ function enumChecks(table: Record<string, SQLiteColumn>) {
20
+ return Object.values(table).flatMap((column) =>
21
+ column.enumValues?.length
22
+ ? [
23
+ check(
24
+ `${getTableName(column.table)}_${column.name}_enum`,
25
+ sql`${column} in (${sql.join(
26
+ column.enumValues.map((value) => sql`${value}`),
27
+ sql`, `,
28
+ )})`.inlineParams(),
29
+ ),
30
+ ]
31
+ : [],
32
+ )
33
+ }
34
+
35
+ export const teamMemberRole = enumColumn(['owner', 'admin', 'member'])
36
+
37
+ export const teamInviteStatus = enumColumn([
38
+ 'pending',
39
+ 'accepted',
40
+ 'revoked',
41
+ 'expired',
42
+ ])
43
+
44
+ export const serverStatus = enumColumn([
45
+ 'pending',
46
+ 'connected',
47
+ 'disconnected',
48
+ 'disabled',
49
+ 'deleted',
50
+ ])
51
+
52
+ export const githubInstallationStatus = enumColumn([
53
+ 'active',
54
+ 'deleted',
55
+ 'suspended',
56
+ ])
57
+
58
+ export const deploymentStatus = enumColumn([
59
+ 'queued',
60
+ 'running',
61
+ 'succeeded',
62
+ 'failed',
63
+ 'canceled',
64
+ ])
65
+
66
+ export const deploymentTrigger = enumColumn([
67
+ 'user',
68
+ 'webhook',
69
+ 'api',
70
+ 'rollback',
71
+ ])
72
+
73
+ export const environmentStatus = enumColumn(['active', 'deleted'])
74
+
75
+ export const aliasType = enumColumn(['deployment', 'environment', 'branch'])
76
+
77
+ export const domainStatus = enumColumn([
78
+ 'pending',
79
+ 'active',
80
+ 'disabled',
81
+ 'failed',
82
+ ])
83
+
84
+ export const resourceStatus = enumColumn(['active', 'deleted', 'banned'])
85
+
86
+ export const allowlistType = enumColumn(['email', 'domain', 'pattern'])
87
+
88
+ export const agentJobType = enumColumn([
89
+ 'deploy',
90
+ 'deprovision',
91
+ 'purge',
92
+ 'backup',
93
+ 'restore',
94
+ 'backup_cleanup',
95
+ ])
96
+
97
+ export const agentJobStatus = enumColumn([
98
+ 'queued',
99
+ 'running',
100
+ 'succeeded',
101
+ 'failed',
102
+ 'canceled',
103
+ 'abandoned',
104
+ ])
105
+
106
+ export const userCleanupJobStatus = enumColumn([
107
+ 'queued',
108
+ 'running',
109
+ 'succeeded',
110
+ 'failed',
111
+ ])
112
+
113
+ export const backupDestinationType = enumColumn(['s3'])
114
+
115
+ export const backupStatus = enumColumn([
116
+ 'queued',
117
+ 'running',
118
+ 'succeeded',
119
+ 'failed',
120
+ 'canceled',
121
+ ])
122
+
123
+ export const backupArtifactType = enumColumn(['manifest', 'volume', 'database'])
124
+
125
+ export const backupArtifactStorage = enumColumn(['local', 'remote'])
126
+
127
+ export const teams = sqliteTable(
128
+ 'teams',
129
+ {
130
+ id: text('id').primaryKey(),
131
+ settingsVersion: integer('settings_version').notNull().default(1),
132
+ slug: text('slug').notNull(),
133
+ name: text('name').notNull(),
134
+ status: resourceStatus('status').notNull().default('active'),
135
+ avatarPath: text('avatar_path'),
136
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
137
+ .notNull()
138
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
139
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
140
+ .notNull()
141
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
142
+ },
143
+ (table) => [
144
+ ...enumChecks(table),
145
+ uniqueIndex('teams_slug_idx').on(table.slug),
146
+ ],
147
+ )
148
+
149
+ export const user = sqliteTable(
150
+ 'user',
151
+ {
152
+ id: text('id').primaryKey(),
153
+ settingsVersion: integer('settings_version').notNull().default(1),
154
+ profileConfirmedAt: integer('profile_confirmed_at', {
155
+ mode: 'timestamp_ms',
156
+ }),
157
+ name: text('name').notNull(),
158
+ username: text('username').notNull(),
159
+ email: text('email').notNull().unique(),
160
+ emailVerified: integer('email_verified', { mode: 'boolean' })
161
+ .notNull()
162
+ .default(false),
163
+ status: resourceStatus('status').notNull().default('active'),
164
+ image: text('image'),
165
+ defaultTeamId: text('default_team_id').references(() => teams.id, {
166
+ onDelete: 'set null',
167
+ }),
168
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
169
+ .notNull()
170
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
171
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
172
+ .notNull()
173
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`)
174
+ .$onUpdate(() => new Date()),
175
+ },
176
+ (table) => [
177
+ ...enumChecks(table),
178
+ uniqueIndex('user_username_idx')
179
+ .on(sql`lower(${table.username})`)
180
+ .where(sql`status = 'active'`),
181
+ ],
182
+ )
183
+
184
+ export const session = sqliteTable(
185
+ 'session',
186
+ {
187
+ id: text('id').primaryKey(),
188
+ expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),
189
+ token: text('token').notNull().unique(),
190
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
191
+ .notNull()
192
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
193
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
194
+ .notNull()
195
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`)
196
+ .$onUpdate(() => new Date()),
197
+ ipAddress: text('ip_address'),
198
+ userAgent: text('user_agent'),
199
+ userId: text('user_id')
200
+ .notNull()
201
+ .references(() => user.id, { onDelete: 'cascade' }),
202
+ },
203
+ (table) => [
204
+ ...enumChecks(table),
205
+ index('session_user_id_idx').on(table.userId),
206
+ ],
207
+ )
208
+
209
+ export const account = sqliteTable(
210
+ 'account',
211
+ {
212
+ id: text('id').primaryKey(),
213
+ accountId: text('account_id').notNull(),
214
+ providerId: text('provider_id').notNull(),
215
+ userId: text('user_id')
216
+ .notNull()
217
+ .references(() => user.id, { onDelete: 'cascade' }),
218
+ accessToken: text('access_token'),
219
+ refreshToken: text('refresh_token'),
220
+ idToken: text('id_token'),
221
+ accessTokenExpiresAt: integer('access_token_expires_at', {
222
+ mode: 'timestamp_ms',
223
+ }),
224
+ refreshTokenExpiresAt: integer('refresh_token_expires_at', {
225
+ mode: 'timestamp_ms',
226
+ }),
227
+ scope: text('scope'),
228
+ password: text('password'),
229
+ providerAccountName: text('provider_account_name'),
230
+ providerAccountEmail: text('provider_account_email'),
231
+ providerAccountUsername: text('provider_account_username'),
232
+ providerAccountImage: text('provider_account_image'),
233
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
234
+ .notNull()
235
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
236
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
237
+ .notNull()
238
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`)
239
+ .$onUpdate(() => new Date()),
240
+ },
241
+ (table) => [
242
+ ...enumChecks(table),
243
+ index('account_user_id_idx').on(table.userId),
244
+ index('account_provider_identity_idx').on(
245
+ table.providerId,
246
+ table.accountId,
247
+ ),
248
+ ],
249
+ )
250
+
251
+ export const verification = sqliteTable(
252
+ 'verification',
253
+ {
254
+ id: text('id').primaryKey(),
255
+ identifier: text('identifier').notNull(),
256
+ value: text('value').notNull(),
257
+ expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),
258
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
259
+ .notNull()
260
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
261
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
262
+ .notNull()
263
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`)
264
+ .$onUpdate(() => new Date()),
265
+ },
266
+ (table) => [
267
+ ...enumChecks(table),
268
+ index('verification_identifier_idx').on(table.identifier),
269
+ ],
270
+ )
271
+
272
+ export const rateLimits = sqliteTable('rate_limits', {
273
+ key: text('key').primaryKey(),
274
+ count: integer('count').notNull(),
275
+ resetAt: integer('reset_at', { mode: 'timestamp_ms' }).notNull(),
276
+ })
277
+
278
+ export const allowlist = sqliteTable(
279
+ 'allowlist',
280
+ {
281
+ id: text('id').primaryKey(),
282
+ type: allowlistType('type').notNull(),
283
+ value: text('value').notNull(),
284
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
285
+ .notNull()
286
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
287
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
288
+ .notNull()
289
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`)
290
+ .$onUpdate(() => new Date()),
291
+ },
292
+ (table) => [
293
+ ...enumChecks(table),
294
+ index('allowlist_type_idx').on(table.type),
295
+ uniqueIndex('allowlist_type_value_idx').on(
296
+ table.type,
297
+ sql`lower(${table.value})`,
298
+ ),
299
+ ],
300
+ )
301
+
302
+ export const teamMembers = sqliteTable(
303
+ 'team_members',
304
+ {
305
+ id: text('id').primaryKey(),
306
+ teamId: text('team_id')
307
+ .notNull()
308
+ .references(() => teams.id, { onDelete: 'cascade' }),
309
+ userId: text('user_id')
310
+ .notNull()
311
+ .references(() => user.id, { onDelete: 'cascade' }),
312
+ role: teamMemberRole('role').notNull().default('member'),
313
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
314
+ .notNull()
315
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
316
+ },
317
+ (table) => [
318
+ ...enumChecks(table),
319
+ uniqueIndex('team_members_team_user_idx').on(table.teamId, table.userId),
320
+ index('team_members_user_idx').on(table.userId),
321
+ index('team_members_team_idx').on(table.teamId),
322
+ ],
323
+ )
324
+
325
+ export const apiKeys = sqliteTable(
326
+ 'api_keys',
327
+ {
328
+ id: text('id').primaryKey(),
329
+ userId: text('user_id')
330
+ .notNull()
331
+ .references(() => user.id, { onDelete: 'cascade' }),
332
+ teamId: text('team_id')
333
+ .notNull()
334
+ .references(() => teams.id, { onDelete: 'cascade' }),
335
+ membershipId: text('membership_id')
336
+ .notNull()
337
+ .references(() => teamMembers.id, { onDelete: 'cascade' }),
338
+ name: text('name').notNull(),
339
+ prefix: text('prefix').notNull(),
340
+ keyHash: text('key_hash').notNull(),
341
+ roleCeiling: teamMemberRole('role_ceiling').notNull(),
342
+ preset: text('preset', { enum: ['read', 'deploy', 'manage'] }).notNull(),
343
+ projectIds: text('project_ids', {
344
+ mode: 'json',
345
+ }).$type<Array<string> | null>(),
346
+ scopes: text('scopes', { mode: 'json' }).notNull().$type<Array<string>>(),
347
+ expiresAt: integer('expires_at', { mode: 'timestamp_ms' }),
348
+ revokedAt: integer('revoked_at', { mode: 'timestamp_ms' }),
349
+ lastUsedAt: integer('last_used_at', { mode: 'timestamp_ms' }),
350
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
351
+ .notNull()
352
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
353
+ },
354
+ (table) => [
355
+ ...enumChecks(table),
356
+ uniqueIndex('api_keys_hash_idx').on(table.keyHash),
357
+ index('api_keys_user_idx').on(table.userId),
358
+ index('api_keys_membership_idx').on(table.membershipId),
359
+ ],
360
+ )
361
+
362
+ export const apiGrants = sqliteTable(
363
+ 'api_grants',
364
+ {
365
+ id: text('id').primaryKey(),
366
+ userId: text('user_id')
367
+ .notNull()
368
+ .references(() => user.id, { onDelete: 'cascade' }),
369
+ teamId: text('team_id')
370
+ .notNull()
371
+ .references(() => teams.id, { onDelete: 'cascade' }),
372
+ membershipId: text('membership_id')
373
+ .notNull()
374
+ .references(() => teamMembers.id, { onDelete: 'cascade' }),
375
+ roleCeiling: teamMemberRole('role_ceiling').notNull(),
376
+ projectIds: text('project_ids', {
377
+ mode: 'json',
378
+ }).$type<Array<string> | null>(),
379
+ scopes: text('scopes', { mode: 'json' }).notNull().$type<Array<string>>(),
380
+ providerGrantId: text('provider_grant_id'),
381
+ expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),
382
+ revokedAt: integer('revoked_at', { mode: 'timestamp_ms' }),
383
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
384
+ .notNull()
385
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
386
+ },
387
+ (table) => [
388
+ ...enumChecks(table),
389
+ index('api_grants_user_idx').on(table.userId),
390
+ index('api_grants_membership_idx').on(table.membershipId),
391
+ ],
392
+ )
393
+
394
+ export const apiAudit = sqliteTable(
395
+ 'api_audit',
396
+ {
397
+ id: text('id').primaryKey(),
398
+ teamId: text('team_id').notNull(),
399
+ userId: text('user_id').notNull(),
400
+ credentialId: text('credential_id').notNull(),
401
+ requestId: text('request_id').notNull(),
402
+ operation: text('operation').notNull(),
403
+ resourceId: text('resource_id').notNull(),
404
+ outcome: text('outcome').notNull(),
405
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
406
+ .notNull()
407
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
408
+ },
409
+ (table) => [
410
+ index('api_audit_team_created_idx').on(table.teamId, table.createdAt),
411
+ index('api_audit_created_idx').on(table.createdAt),
412
+ ],
413
+ )
414
+
415
+ export const apiIdempotency = sqliteTable(
416
+ 'api_idempotency',
417
+ {
418
+ id: text('id').primaryKey(),
419
+ membershipId: text('membership_id')
420
+ .notNull()
421
+ .references(() => teamMembers.id, { onDelete: 'cascade' }),
422
+ credentialId: text('credential_id').notNull(),
423
+ operation: text('operation').notNull(),
424
+ resourceId: text('resource_id').notNull(),
425
+ keyHash: text('key_hash').notNull(),
426
+ requestHash: text('request_hash').notNull(),
427
+ response: text('response', { mode: 'json' }).notNull().$type<unknown>(),
428
+ statusCode: integer('status_code').notNull(),
429
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
430
+ .notNull()
431
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
432
+ expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),
433
+ },
434
+ (table) => [
435
+ uniqueIndex('api_idempotency_actor_operation_key_idx').on(
436
+ table.membershipId,
437
+ table.operation,
438
+ table.resourceId,
439
+ table.keyHash,
440
+ ),
441
+ index('api_idempotency_expiry_idx').on(table.expiresAt),
442
+ ],
443
+ )
444
+
445
+ export const teamInvites = sqliteTable(
446
+ 'team_invites',
447
+ {
448
+ id: text('id').primaryKey(),
449
+ teamId: text('team_id')
450
+ .notNull()
451
+ .references(() => teams.id, { onDelete: 'cascade' }),
452
+ email: text('email').notNull(),
453
+ role: teamMemberRole('role').notNull().default('member'),
454
+ inviterId: text('inviter_id').references(() => user.id, {
455
+ onDelete: 'set null',
456
+ }),
457
+ tokenHash: text('token_hash').notNull(),
458
+ status: teamInviteStatus('status').notNull().default('pending'),
459
+ expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(),
460
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
461
+ .notNull()
462
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
463
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
464
+ .notNull()
465
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
466
+ },
467
+ (table) => [
468
+ ...enumChecks(table),
469
+ uniqueIndex('team_invites_token_hash_idx').on(table.tokenHash),
470
+ uniqueIndex('team_invites_pending_email_idx')
471
+ .on(table.teamId, sql`lower(${table.email})`)
472
+ .where(sql`${table.status} = 'pending'`),
473
+ index('team_invites_team_idx').on(table.teamId),
474
+ index('team_invites_email_idx').on(sql`lower(${table.email})`),
475
+ ],
476
+ )
477
+
478
+ export const githubInstallations = sqliteTable(
479
+ 'github_installations',
480
+ {
481
+ installationId: integer('installation_id').primaryKey(),
482
+ accountId: integer('account_id'),
483
+ accountLogin: text('account_login').notNull(),
484
+ accountType: text('account_type').notNull(),
485
+ repositorySelection: text('repository_selection').notNull(),
486
+ status: githubInstallationStatus('status').notNull().default('active'),
487
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
488
+ .notNull()
489
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
490
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
491
+ .notNull()
492
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
493
+ },
494
+ (table) => [
495
+ ...enumChecks(table),
496
+ index('github_installations_account_id_idx').on(table.accountId),
497
+ index('github_installations_account_idx').on(table.accountLogin),
498
+ index('github_installations_status_idx').on(table.status),
499
+ ],
500
+ )
501
+
502
+ export const servers = sqliteTable(
503
+ 'servers',
504
+ {
505
+ id: text('id').primaryKey(),
506
+ settingsVersion: integer('settings_version').notNull().default(1),
507
+ teamId: text('team_id')
508
+ .notNull()
509
+ .references(() => teams.id, { onDelete: 'cascade' }),
510
+ name: text('name').notNull(),
511
+ handle: text('handle').notNull(),
512
+ status: serverStatus('status').notNull().default('pending'),
513
+ agentVersion: text('agent_version'),
514
+ agentProtocolVersion: integer('agent_protocol_version'),
515
+ agentCapabilities: text('agent_capabilities', { mode: 'json' }).$type<
516
+ Array<string>
517
+ >(),
518
+ publicAddress: text('public_address'),
519
+ publicIpv4: text('public_ipv4'),
520
+ publicIpv6: text('public_ipv6'),
521
+ hostname: text('hostname'),
522
+ operatingSystem: text('operating_system'),
523
+ kernelVersion: text('kernel_version'),
524
+ architecture: text('architecture'),
525
+ cpuCoreCount: integer('cpu_core_count'),
526
+ hostUptimeSeconds: integer('host_uptime_seconds'),
527
+ enrollmentTokenHash: text('enrollment_token_hash'),
528
+ enrollmentTokenExpiresAt: integer('enrollment_token_expires_at', {
529
+ mode: 'timestamp_ms',
530
+ }),
531
+ agentCredentialHash: text('agent_credential_hash'),
532
+ pendingAgentCredentialHash: text('pending_agent_credential_hash'),
533
+ agentCredentialRotatedAt: integer('agent_credential_rotated_at', {
534
+ mode: 'timestamp_ms',
535
+ }),
536
+ lastSeenAt: integer('last_seen_at', { mode: 'timestamp_ms' }),
537
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
538
+ .notNull()
539
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
540
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
541
+ .notNull()
542
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
543
+ },
544
+ (table) => [
545
+ ...enumChecks(table),
546
+ uniqueIndex('servers_team_handle_idx')
547
+ .on(table.teamId, table.handle)
548
+ .where(sql`${table.status} <> 'deleted'`),
549
+ uniqueIndex('servers_team_name_idx')
550
+ .on(table.teamId, sql`lower(${table.name})`)
551
+ .where(sql`${table.status} <> 'deleted'`),
552
+ index('servers_team_idx').on(table.teamId),
553
+ index('servers_agent_credential_idx')
554
+ .on(table.agentCredentialHash)
555
+ .where(sql`${table.agentCredentialHash} is not null`),
556
+ index('servers_pending_agent_credential_idx')
557
+ .on(table.pendingAgentCredentialHash)
558
+ .where(sql`${table.pendingAgentCredentialHash} is not null`),
559
+ index('servers_enrollment_token_idx')
560
+ .on(table.enrollmentTokenHash)
561
+ .where(sql`${table.enrollmentTokenHash} is not null`),
562
+ ],
563
+ )
564
+
565
+ export const serverHeartbeats = sqliteTable(
566
+ 'server_heartbeats',
567
+ {
568
+ id: text('id').primaryKey(),
569
+ serverId: text('server_id')
570
+ .notNull()
571
+ .references(() => servers.id, { onDelete: 'cascade' }),
572
+ observedAt: integer('observed_at', { mode: 'timestamp_ms' }).notNull(),
573
+ agentVersion: text('agent_version'),
574
+ publicAddress: text('public_address'),
575
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
576
+ .notNull()
577
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
578
+ },
579
+ (table) => [
580
+ ...enumChecks(table),
581
+ index('server_heartbeats_server_observed_idx').on(
582
+ table.serverId,
583
+ table.observedAt,
584
+ ),
585
+ index('server_heartbeats_observed_idx').on(table.observedAt),
586
+ ],
587
+ )
588
+
589
+ export const serverAvailabilityWindows = sqliteTable(
590
+ 'server_availability_windows',
591
+ {
592
+ id: text('id').primaryKey(),
593
+ serverId: text('server_id')
594
+ .notNull()
595
+ .references(() => servers.id, { onDelete: 'cascade' }),
596
+ startedAt: integer('started_at', { mode: 'timestamp_ms' }).notNull(),
597
+ endedAt: integer('ended_at', { mode: 'timestamp_ms' }),
598
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
599
+ .notNull()
600
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
601
+ },
602
+ (table) => [
603
+ ...enumChecks(table),
604
+ index('server_availability_windows_server_started_idx').on(
605
+ table.serverId,
606
+ table.startedAt,
607
+ ),
608
+ ],
609
+ )
610
+
611
+ export const serverMetrics = sqliteTable(
612
+ 'server_metrics',
613
+ {
614
+ id: text('id').primaryKey(),
615
+ serverId: text('server_id')
616
+ .notNull()
617
+ .references(() => servers.id, { onDelete: 'cascade' }),
618
+ observedAt: integer('observed_at', { mode: 'timestamp_ms' }).notNull(),
619
+ cpuPercent: real('cpu_percent'),
620
+ load1: real('load_1'),
621
+ memoryUsedBytes: integer('memory_used_bytes'),
622
+ memoryTotalBytes: integer('memory_total_bytes'),
623
+ diskUsedBytes: integer('disk_used_bytes'),
624
+ diskTotalBytes: integer('disk_total_bytes'),
625
+ networkReceiveBytesPerSecond: real('network_receive_bytes_per_second'),
626
+ networkTransmitBytesPerSecond: real('network_transmit_bytes_per_second'),
627
+ diskReadBytesPerSecond: real('disk_read_bytes_per_second'),
628
+ diskWriteBytesPerSecond: real('disk_write_bytes_per_second'),
629
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
630
+ .notNull()
631
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
632
+ },
633
+ (table) => [
634
+ ...enumChecks(table),
635
+ index('server_metrics_server_observed_idx').on(
636
+ table.serverId,
637
+ table.observedAt,
638
+ ),
639
+ index('server_metrics_observed_idx').on(table.observedAt),
640
+ ],
641
+ )
642
+
643
+ export const projects = sqliteTable(
644
+ 'projects',
645
+ {
646
+ id: text('id').primaryKey(),
647
+ settingsVersion: integer('settings_version').notNull().default(1),
648
+ teamId: text('team_id')
649
+ .notNull()
650
+ .references(() => teams.id, { onDelete: 'cascade' }),
651
+ slug: text('slug').notNull(),
652
+ name: text('name').notNull(),
653
+ status: resourceStatus('status').notNull().default('active'),
654
+ githubRepoId: integer('github_repo_id'),
655
+ githubRepo: text('github_repo').notNull(),
656
+ githubInstallationId: integer('github_installation_id').references(
657
+ () => githubInstallations.installationId,
658
+ {
659
+ onDelete: 'restrict',
660
+ },
661
+ ),
662
+ defaultServerId: text('default_server_id').references(() => servers.id, {
663
+ onDelete: 'restrict',
664
+ }),
665
+ avatarPath: text('avatar_path'),
666
+ routes: text('routes', { mode: 'json' }),
667
+ envVarsEncrypted: text('env_vars_encrypted'),
668
+ runtimeConfig: text('runtime_config', { mode: 'json' })
669
+ .notNull()
670
+ .default(
671
+ sql`'{"type":"compose","path":null,"releaseCommand":null,"releaseService":null,"readinessPath":null,"deployCommand":null,"rollbackCommand":null,"timeoutSeconds":1800}'`,
672
+ ),
673
+ composeMetadata: text('compose_metadata', { mode: 'json' }),
674
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
675
+ .notNull()
676
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
677
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
678
+ .notNull()
679
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
680
+ },
681
+ (table) => [
682
+ ...enumChecks(table),
683
+ uniqueIndex('projects_team_slug_idx')
684
+ .on(table.teamId, sql`lower(${table.slug})`)
685
+ .where(sql`${table.status} <> 'deleted'`),
686
+ index('projects_team_idx').on(table.teamId),
687
+ index('projects_github_installation_idx').on(table.githubInstallationId),
688
+ index('projects_default_server_idx').on(table.defaultServerId),
689
+ ],
690
+ )
691
+
692
+ export const environments = sqliteTable(
693
+ 'environments',
694
+ {
695
+ id: text('id').primaryKey(),
696
+ projectId: text('project_id')
697
+ .notNull()
698
+ .references(() => projects.id, { onDelete: 'cascade' }),
699
+ serverId: text('server_id').references(() => servers.id, {
700
+ onDelete: 'restrict',
701
+ }),
702
+ name: text('name').notNull(),
703
+ slug: text('slug').notNull(),
704
+ branch: text('branch').notNull(),
705
+ isProduction: integer('is_production', { mode: 'boolean' })
706
+ .notNull()
707
+ .default(false),
708
+ position: integer('position').notNull().default(0),
709
+ color: text('color').notNull().default('blue'),
710
+ status: environmentStatus('status').notNull().default('active'),
711
+ runtimeId: text('runtime_id').notNull(),
712
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
713
+ .notNull()
714
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
715
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
716
+ .notNull()
717
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
718
+ },
719
+ (table) => [
720
+ ...enumChecks(table),
721
+ uniqueIndex('environments_project_slug_idx').on(
722
+ table.projectId,
723
+ table.slug,
724
+ ),
725
+ index('environments_server_idx').on(table.serverId),
726
+ ],
727
+ )
728
+
729
+ export const agentJobs = sqliteTable(
730
+ 'agent_jobs',
731
+ {
732
+ id: text('id').primaryKey(),
733
+ type: agentJobType('type').notNull(),
734
+ status: agentJobStatus('status').notNull().default('queued'),
735
+ serverId: text('server_id')
736
+ .notNull()
737
+ .references(() => servers.id, { onDelete: 'restrict' }),
738
+ teamId: text('team_id').references(() => teams.id, {
739
+ onDelete: 'set null',
740
+ }),
741
+ projectId: text('project_id').references(() => projects.id, {
742
+ onDelete: 'set null',
743
+ }),
744
+ payload: text('payload', { mode: 'json' }).notNull(),
745
+ attempt: integer('attempt').notNull().default(0),
746
+ maxAttempts: integer('max_attempts').notNull().default(5),
747
+ errorMessage: text('error_message'),
748
+ claimedAt: integer('claimed_at', { mode: 'timestamp_ms' }),
749
+ availableAt: integer('available_at', { mode: 'timestamp_ms' })
750
+ .notNull()
751
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
752
+ finishedAt: integer('finished_at', { mode: 'timestamp_ms' }),
753
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
754
+ .notNull()
755
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
756
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
757
+ .notNull()
758
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
759
+ },
760
+ (table) => [
761
+ ...enumChecks(table),
762
+ index('agent_jobs_server_status_idx').on(table.serverId, table.status),
763
+ index('agent_jobs_project_idx').on(table.projectId),
764
+ index('agent_jobs_team_idx').on(table.teamId),
765
+ ],
766
+ )
767
+
768
+ export const agentRuntimeObservations = sqliteTable(
769
+ 'agent_runtime_observations',
770
+ {
771
+ id: text('id').primaryKey(),
772
+ serverId: text('server_id')
773
+ .notNull()
774
+ .references(() => servers.id, { onDelete: 'cascade' }),
775
+ runtimeId: text('runtime_id').notNull(),
776
+ services: text('services', { mode: 'json' }),
777
+ firstSeenAt: integer('first_seen_at', { mode: 'timestamp_ms' }).notNull(),
778
+ lastSeenAt: integer('last_seen_at', { mode: 'timestamp_ms' }).notNull(),
779
+ cleanupQueuedAt: integer('cleanup_queued_at', { mode: 'timestamp_ms' }),
780
+ purgeQueuedAt: integer('purge_queued_at', { mode: 'timestamp_ms' }),
781
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
782
+ .notNull()
783
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
784
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
785
+ .notNull()
786
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
787
+ },
788
+ (table) => [
789
+ ...enumChecks(table),
790
+ uniqueIndex('agent_runtime_observations_server_runtime_idx').on(
791
+ table.serverId,
792
+ table.runtimeId,
793
+ ),
794
+ index('agent_runtime_observations_server_idx').on(table.serverId),
795
+ index('agent_runtime_observations_cleanup_idx').on(table.cleanupQueuedAt),
796
+ index('agent_runtime_observations_purge_idx').on(table.purgeQueuedAt),
797
+ ],
798
+ )
799
+
800
+ export const userCleanupJobs = sqliteTable(
801
+ 'user_cleanup_jobs',
802
+ {
803
+ id: text('id').primaryKey(),
804
+ userId: text('user_id')
805
+ .notNull()
806
+ .references(() => user.id, { onDelete: 'cascade' }),
807
+ status: userCleanupJobStatus('status').notNull().default('queued'),
808
+ payload: text('payload', { mode: 'json' }).notNull(),
809
+ attempt: integer('attempt').notNull().default(0),
810
+ maxAttempts: integer('max_attempts').notNull().default(5),
811
+ errorMessage: text('error_message'),
812
+ claimedAt: integer('claimed_at', { mode: 'timestamp_ms' }),
813
+ availableAt: integer('available_at', { mode: 'timestamp_ms' })
814
+ .notNull()
815
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
816
+ finishedAt: integer('finished_at', { mode: 'timestamp_ms' }),
817
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
818
+ .notNull()
819
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
820
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
821
+ .notNull()
822
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
823
+ },
824
+ (table) => [
825
+ ...enumChecks(table),
826
+ index('user_cleanup_jobs_status_available_idx').on(
827
+ table.status,
828
+ table.availableAt,
829
+ ),
830
+ index('user_cleanup_jobs_user_idx').on(table.userId),
831
+ ],
832
+ )
833
+
834
+ export const backupStorage = sqliteTable(
835
+ 'backup_storage',
836
+ {
837
+ id: text('id').primaryKey(),
838
+ settingsVersion: integer('settings_version').notNull().default(1),
839
+ teamId: text('team_id')
840
+ .notNull()
841
+ .references(() => teams.id, { onDelete: 'cascade' }),
842
+ name: text('name').notNull(),
843
+ bucket: text('bucket').notNull(),
844
+ region: text('region').notNull(),
845
+ endpoint: text('endpoint'),
846
+ accessKeyId: text('access_key_id').notNull(),
847
+ secretAccessKeyCiphertext: text('secret_access_key_ciphertext').notNull(),
848
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
849
+ .notNull()
850
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
851
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
852
+ .notNull()
853
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
854
+ },
855
+ (table) => [index('backup_storage_team_idx').on(table.teamId)],
856
+ )
857
+
858
+ export const backupConfigs = sqliteTable(
859
+ 'backup_configs',
860
+ {
861
+ id: text('id').primaryKey(),
862
+ settingsVersion: integer('settings_version').notNull().default(1),
863
+ name: text('name').notNull(),
864
+ projectId: text('project_id')
865
+ .notNull()
866
+ .references(() => projects.id, { onDelete: 'cascade' }),
867
+ environmentId: text('environment_id')
868
+ .notNull()
869
+ .references(() => environments.id, { onDelete: 'cascade' }),
870
+ enabled: integer('enabled', { mode: 'boolean' }).notNull().default(true),
871
+ destinationType: backupDestinationType('destination_type')
872
+ .notNull()
873
+ .default('s3'),
874
+ schedule: text('schedule', { mode: 'json' }).notNull(),
875
+ destination: text('destination', { mode: 'json' }).notNull(),
876
+ retention: text('retention', { mode: 'json' }).notNull(),
877
+ volumes: text('volumes', { mode: 'json' })
878
+ .notNull()
879
+ .default({ mode: 'allNamed', names: [] }),
880
+ databaseDumps: text('database_dumps', { mode: 'json' })
881
+ .notNull()
882
+ .default([]),
883
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
884
+ .notNull()
885
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
886
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
887
+ .notNull()
888
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
889
+ },
890
+ (table) => [
891
+ ...enumChecks(table),
892
+ index('backup_configs_environment_idx').on(table.environmentId),
893
+ index('backup_configs_project_idx').on(table.projectId),
894
+ ],
895
+ )
896
+
897
+ export const deployments = sqliteTable(
898
+ 'deployments',
899
+ {
900
+ id: text('id').primaryKey(),
901
+ environmentId: text('environment_id')
902
+ .notNull()
903
+ .references(() => environments.id, { onDelete: 'cascade' }),
904
+ serverId: text('server_id')
905
+ .notNull()
906
+ .references(() => servers.id, { onDelete: 'restrict' }),
907
+ status: deploymentStatus('status').notNull().default('queued'),
908
+ phase: text('phase'),
909
+ trigger: deploymentTrigger('trigger').notNull().default('user'),
910
+ githubDeliveryId: text('github_delivery_id'),
911
+ repoId: integer('repo_id').notNull(),
912
+ repoFullName: text('repo_full_name').notNull(),
913
+ gitSha: text('git_sha').notNull(),
914
+ gitBranch: text('git_branch').notNull(),
915
+ commitMeta: text('commit_meta', { mode: 'json' }).notNull().default({}),
916
+ config: text('config', { mode: 'json' }).notNull(),
917
+ envVars: text('env_vars').notNull(),
918
+ runtimeId: text('runtime_id').notNull(),
919
+ errorMessage: text('error_message'),
920
+ startedAt: integer('started_at', { mode: 'timestamp_ms' }),
921
+ finishedAt: integer('finished_at', { mode: 'timestamp_ms' }),
922
+ createdByUserId: text('created_by_user_id').references(() => user.id, {
923
+ onDelete: 'set null',
924
+ }),
925
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
926
+ .notNull()
927
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
928
+ },
929
+ (table) => [
930
+ ...enumChecks(table),
931
+ index('deployments_environment_idx').on(table.environmentId),
932
+ index('deployments_server_status_idx').on(table.serverId, table.status),
933
+ index('deployments_status_idx').on(table.status),
934
+ uniqueIndex('deployments_running_runtime_idx')
935
+ .on(table.runtimeId)
936
+ .where(sql`${table.status} = 'running'`),
937
+ uniqueIndex('deployments_github_delivery_environment_idx')
938
+ .on(table.githubDeliveryId, table.environmentId)
939
+ .where(sql`${table.githubDeliveryId} is not null`),
940
+ ],
941
+ )
942
+
943
+ export const backupRuns = sqliteTable(
944
+ 'backup_runs',
945
+ {
946
+ id: text('id').primaryKey(),
947
+ projectId: text('project_id')
948
+ .notNull()
949
+ .references(() => projects.id, { onDelete: 'cascade' }),
950
+ environmentId: text('environment_id')
951
+ .notNull()
952
+ .references(() => environments.id, { onDelete: 'cascade' }),
953
+ deploymentId: text('deployment_id').references(() => deployments.id, {
954
+ onDelete: 'set null',
955
+ }),
956
+ serverId: text('server_id').references(() => servers.id, {
957
+ onDelete: 'restrict',
958
+ }),
959
+ runtimeId: text('runtime_id').notNull(),
960
+ configId: text('config_id'),
961
+ scheduleKey: text('schedule_key'),
962
+ status: backupStatus('status').notNull().default('queued'),
963
+ manifest: text('manifest', { mode: 'json' }),
964
+ destination: text('destination', { mode: 'json' }),
965
+ errorMessage: text('error_message'),
966
+ startedAt: integer('started_at', { mode: 'timestamp_ms' }),
967
+ finishedAt: integer('finished_at', { mode: 'timestamp_ms' }),
968
+ cleanupQueuedAt: integer('cleanup_queued_at', { mode: 'timestamp_ms' }),
969
+ cleanupFinishedAt: integer('cleanup_finished_at', { mode: 'timestamp_ms' }),
970
+ cleanupErrorMessage: text('cleanup_error_message'),
971
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
972
+ .notNull()
973
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
974
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
975
+ .notNull()
976
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
977
+ },
978
+ (table) => [
979
+ ...enumChecks(table),
980
+ index('backup_runs_environment_status_idx').on(
981
+ table.environmentId,
982
+ table.status,
983
+ ),
984
+ index('backup_runs_project_idx').on(table.projectId),
985
+ index('backup_runs_deployment_idx').on(table.deploymentId),
986
+ index('backup_runs_cleanup_idx').on(table.cleanupQueuedAt),
987
+ uniqueIndex('backup_runs_environment_schedule_idx')
988
+ .on(table.environmentId, table.scheduleKey)
989
+ .where(sql`${table.scheduleKey} is not null`),
990
+ uniqueIndex('backup_runs_active_environment_idx')
991
+ .on(table.environmentId)
992
+ .where(sql`${table.status} in ('queued', 'running')`),
993
+ ],
994
+ )
995
+
996
+ export const backupArtifacts = sqliteTable(
997
+ 'backup_artifacts',
998
+ {
999
+ id: text('id').primaryKey(),
1000
+ backupRunId: text('backup_run_id')
1001
+ .notNull()
1002
+ .references(() => backupRuns.id, { onDelete: 'cascade' }),
1003
+ type: backupArtifactType('type').notNull(),
1004
+ storage: backupArtifactStorage('storage').notNull(),
1005
+ objectKey: text('object_key').notNull(),
1006
+ checksumSha256: text('checksum_sha256').notNull(),
1007
+ sizeBytes: integer('size_bytes').notNull(),
1008
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
1009
+ .notNull()
1010
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
1011
+ },
1012
+ (table) => [
1013
+ ...enumChecks(table),
1014
+ uniqueIndex('backup_artifacts_run_object_idx').on(
1015
+ table.backupRunId,
1016
+ table.objectKey,
1017
+ ),
1018
+ index('backup_artifacts_run_idx').on(table.backupRunId),
1019
+ ],
1020
+ )
1021
+
1022
+ export const deploymentEvents = sqliteTable(
1023
+ 'deployment_events',
1024
+ {
1025
+ deploymentId: text('deployment_id')
1026
+ .notNull()
1027
+ .references(() => deployments.id, { onDelete: 'cascade' }),
1028
+ stream: text('stream').notNull(),
1029
+ seq: integer('seq').notNull(),
1030
+ content: text('content').notNull(),
1031
+ searchContent: text('search_content').notNull(),
1032
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
1033
+ .notNull()
1034
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
1035
+ },
1036
+ (table) => [
1037
+ ...enumChecks(table),
1038
+ uniqueIndex('deployment_events_seq_idx').on(
1039
+ table.deploymentId,
1040
+ table.stream,
1041
+ table.seq,
1042
+ ),
1043
+ ],
1044
+ )
1045
+
1046
+ export const aliases = sqliteTable(
1047
+ 'aliases',
1048
+ {
1049
+ id: text('id').primaryKey(),
1050
+ projectId: text('project_id')
1051
+ .notNull()
1052
+ .references(() => projects.id, { onDelete: 'cascade' }),
1053
+ environmentId: text('environment_id')
1054
+ .notNull()
1055
+ .references(() => environments.id, { onDelete: 'cascade' }),
1056
+ deploymentId: text('deployment_id').references(() => deployments.id, {
1057
+ onDelete: 'set null',
1058
+ }),
1059
+ previousDeploymentId: text('previous_deployment_id').references(
1060
+ () => deployments.id,
1061
+ { onDelete: 'set null' },
1062
+ ),
1063
+ hostname: text('hostname').notNull(),
1064
+ type: aliasType('type').notNull(),
1065
+ branch: text('branch'),
1066
+ https: integer('https', { mode: 'boolean' }).notNull().default(true),
1067
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
1068
+ .notNull()
1069
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
1070
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
1071
+ .notNull()
1072
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
1073
+ },
1074
+ (table) => [
1075
+ ...enumChecks(table),
1076
+ uniqueIndex('aliases_hostname_idx').on(table.hostname),
1077
+ index('aliases_project_idx').on(table.projectId),
1078
+ index('aliases_environment_idx').on(table.environmentId),
1079
+ index('aliases_deployment_idx').on(table.deploymentId),
1080
+ ],
1081
+ )
1082
+
1083
+ export const domains = sqliteTable(
1084
+ 'domains',
1085
+ {
1086
+ id: text('id').primaryKey(),
1087
+ projectId: text('project_id')
1088
+ .notNull()
1089
+ .references(() => projects.id, { onDelete: 'cascade' }),
1090
+ environmentId: text('environment_id')
1091
+ .notNull()
1092
+ .references(() => environments.id, { onDelete: 'cascade' }),
1093
+ hostname: text('hostname').notNull(),
1094
+ basePath: text('base_path'),
1095
+ https: integer('https', { mode: 'boolean' }).notNull().default(true),
1096
+ status: domainStatus('status').notNull().default('pending'),
1097
+ message: text('message'),
1098
+ verifiedAt: integer('verified_at', { mode: 'timestamp_ms' }),
1099
+ createdAt: integer('created_at', { mode: 'timestamp_ms' })
1100
+ .notNull()
1101
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
1102
+ updatedAt: integer('updated_at', { mode: 'timestamp_ms' })
1103
+ .notNull()
1104
+ .default(sql`(cast(unixepoch('subsec') * 1000 as integer))`),
1105
+ },
1106
+ (table) => [
1107
+ ...enumChecks(table),
1108
+ uniqueIndex('domains_hostname_idx').on(table.hostname),
1109
+ index('domains_project_idx').on(table.projectId),
1110
+ index('domains_environment_idx').on(table.environmentId),
1111
+ ],
1112
+ )
1113
+
1114
+ export const notificationPreferences = sqliteTable('notification_preferences', {
1115
+ userId: text('user_id')
1116
+ .primaryKey()
1117
+ .references(() => user.id, { onDelete: 'cascade' }),
1118
+ preferences: text('preferences', { mode: 'json' })
1119
+ .notNull()
1120
+ .$type<Record<string, boolean>>(),
1121
+ version: integer('version').notNull().default(1),
1122
+ })
1123
+
1124
+ export const notificationMutes = sqliteTable(
1125
+ 'notification_mutes',
1126
+ {
1127
+ id: text('id').primaryKey(),
1128
+ userId: text('user_id')
1129
+ .notNull()
1130
+ .references(() => user.id, { onDelete: 'cascade' }),
1131
+ teamId: text('team_id')
1132
+ .notNull()
1133
+ .references(() => teams.id, { onDelete: 'cascade' }),
1134
+ projectId: text('project_id').references(() => projects.id, {
1135
+ onDelete: 'cascade',
1136
+ }),
1137
+ },
1138
+ (table) => [
1139
+ index('notification_mutes_user_scope_idx').on(
1140
+ table.userId,
1141
+ table.teamId,
1142
+ table.projectId,
1143
+ ),
1144
+ ],
1145
+ )
1146
+
1147
+ export const notificationIncidents = sqliteTable('notification_incidents', {
1148
+ id: text('id').primaryKey(),
1149
+ teamId: text('team_id')
1150
+ .notNull()
1151
+ .references(() => teams.id, { onDelete: 'cascade' }),
1152
+ projectId: text('project_id').references(() => projects.id, {
1153
+ onDelete: 'cascade',
1154
+ }),
1155
+ category: text('category').notNull(),
1156
+ sourceId: text('source_id').notNull(),
1157
+ revision: text('revision').notNull(),
1158
+ active: integer('active', { mode: 'boolean' }).notNull(),
1159
+ actorId: text('actor_id'),
1160
+ title: text('title').notNull(),
1161
+ message: text('message').notNull(),
1162
+ path: text('path').notNull(),
1163
+ updatedAt: integer('updated_at').notNull(),
1164
+ })
1165
+
1166
+ export const notificationOutbox = sqliteTable(
1167
+ 'notification_outbox',
1168
+ {
1169
+ id: text('id').primaryKey(),
1170
+ incidentId: text('incident_id')
1171
+ .notNull()
1172
+ .references(() => notificationIncidents.id, { onDelete: 'cascade' }),
1173
+ payload: text('payload', { mode: 'json' }).notNull().$type<{
1174
+ from: string
1175
+ to: string
1176
+ subject: string
1177
+ text: string
1178
+ html: string
1179
+ }>(),
1180
+ revision: text('revision').notNull(),
1181
+ userId: text('user_id')
1182
+ .notNull()
1183
+ .references(() => user.id, { onDelete: 'cascade' }),
1184
+ actor: integer('actor', { mode: 'boolean' }).notNull().default(false),
1185
+ recovery: integer('recovery', { mode: 'boolean' }).notNull(),
1186
+ createdAt: integer('created_at').notNull(),
1187
+ firstAttemptAt: integer('first_attempt_at'),
1188
+ deliveredAt: integer('delivered_at'),
1189
+ discardedAt: integer('discarded_at'),
1190
+ failedAt: integer('failed_at'),
1191
+ attempts: integer('attempts').notNull().default(0),
1192
+ nextAttemptAt: integer('next_attempt_at').notNull().default(0),
1193
+ leaseOwner: text('lease_owner'),
1194
+ leaseUntil: integer('lease_until').notNull().default(0),
1195
+ },
1196
+ (table) => [
1197
+ uniqueIndex('notification_outbox_event_recipient_idx').on(
1198
+ table.incidentId,
1199
+ table.revision,
1200
+ table.userId,
1201
+ table.recovery,
1202
+ ),
1203
+ index('notification_outbox_due_idx').on(
1204
+ table.deliveredAt,
1205
+ table.discardedAt,
1206
+ table.failedAt,
1207
+ table.nextAttemptAt,
1208
+ ),
1209
+ ],
1210
+ )
1211
+
1212
+ export const notificationScanCursors = sqliteTable(
1213
+ 'notification_scan_cursors',
1214
+ {
1215
+ source: text('source').primaryKey(),
1216
+ afterId: text('after_id').notNull(),
1217
+ },
1218
+ )