openship 0.1.0 → 0.1.1

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 (531) hide show
  1. package/.env.example +19 -3
  2. package/CONTRIBUTING.md +69 -19
  3. package/LICENSE +661 -0
  4. package/README.md +304 -108
  5. package/apps/api/Dockerfile +6 -8
  6. package/apps/api/package.json +25 -21
  7. package/apps/api/src/app.ts +46 -3
  8. package/apps/api/src/config/env.ts +81 -7
  9. package/apps/api/src/lib/auth-mode.ts +42 -0
  10. package/apps/api/src/lib/auth.ts +105 -0
  11. package/apps/api/src/lib/cache.ts +128 -0
  12. package/apps/api/src/lib/cloud-auth-proxy.ts +310 -0
  13. package/apps/api/src/lib/cloud-client.ts +103 -0
  14. package/apps/api/src/lib/compose-parser.ts +138 -0
  15. package/apps/api/src/lib/controller-helpers.ts +90 -0
  16. package/apps/api/src/lib/email-templates.ts +94 -0
  17. package/apps/api/src/lib/encryption.ts +102 -0
  18. package/apps/api/src/lib/local-user.ts +65 -0
  19. package/apps/api/src/lib/mail.ts +48 -0
  20. package/apps/api/src/lib/notifications.ts +195 -0
  21. package/apps/api/src/lib/openship-cloud.ts +101 -0
  22. package/apps/api/src/lib/resources.ts +96 -0
  23. package/apps/api/src/lib/screenshots.ts +189 -0
  24. package/apps/api/src/lib/ssh-manager.ts +369 -0
  25. package/apps/api/src/lib/ssl-scheduler.ts +109 -0
  26. package/apps/api/src/lib/stack-detector.ts +708 -0
  27. package/apps/api/src/lib/system-debug.ts +10 -0
  28. package/apps/api/src/middleware/auth.ts +49 -13
  29. package/apps/api/src/middleware/error-handler.ts +23 -3
  30. package/apps/api/src/middleware/index.ts +2 -0
  31. package/apps/api/src/middleware/internal-auth.ts +40 -0
  32. package/apps/api/src/middleware/local-only.ts +20 -0
  33. package/apps/api/src/middleware/rate-limiter.ts +9 -0
  34. package/apps/api/src/modules/analytics/analytics.controller.ts +111 -0
  35. package/apps/api/src/modules/analytics/analytics.routes.ts +29 -0
  36. package/apps/api/src/modules/analytics/analytics.schema.ts +37 -0
  37. package/apps/api/src/modules/analytics/analytics.service.ts +264 -0
  38. package/apps/api/src/modules/auth/auth.routes.ts +233 -6
  39. package/apps/api/src/modules/billing/billing.routes.ts +8 -0
  40. package/apps/api/src/modules/cloud/cloud.controller.ts +225 -0
  41. package/apps/api/src/modules/cloud/cloud.routes.ts +40 -0
  42. package/apps/api/src/modules/deployments/build.service.ts +791 -0
  43. package/apps/api/src/modules/deployments/deployment-lifecycle.ts +138 -0
  44. package/apps/api/src/modules/deployments/deployment.controller.ts +324 -16
  45. package/apps/api/src/modules/deployments/deployment.routes.ts +33 -7
  46. package/apps/api/src/modules/deployments/deployment.schema.ts +37 -5
  47. package/apps/api/src/modules/deployments/deployment.service.ts +178 -11
  48. package/apps/api/src/modules/deployments/preflight.ts +280 -0
  49. package/apps/api/src/modules/deployments/prepare.service.ts +252 -0
  50. package/apps/api/src/modules/deployments/session-manager.ts +283 -0
  51. package/apps/api/src/modules/deployments/ssl.service.ts +73 -0
  52. package/apps/api/src/modules/domains/domain.controller.ts +59 -7
  53. package/apps/api/src/modules/domains/domain.routes.ts +20 -5
  54. package/apps/api/src/modules/domains/domain.schema.ts +35 -0
  55. package/apps/api/src/modules/domains/domain.service.ts +375 -9
  56. package/apps/api/src/modules/github/github.auth.ts +397 -0
  57. package/apps/api/src/modules/github/github.controller.ts +387 -0
  58. package/apps/api/src/modules/github/github.local-auth.ts +251 -0
  59. package/apps/api/src/modules/github/github.routes.ts +50 -0
  60. package/apps/api/src/modules/github/github.schema.ts +52 -0
  61. package/apps/api/src/modules/github/github.service.ts +592 -0
  62. package/apps/api/src/modules/github/github.types.ts +158 -0
  63. package/apps/api/src/modules/github/github.webhook.ts +186 -0
  64. package/apps/api/src/modules/github/index.ts +16 -0
  65. package/apps/api/src/modules/health/health.routes.ts +35 -0
  66. package/apps/api/src/modules/projects/project.controller.ts +481 -12
  67. package/apps/api/src/modules/projects/project.routes.ts +63 -6
  68. package/apps/api/src/modules/projects/project.schema.ts +104 -6
  69. package/apps/api/src/modules/projects/project.service.ts +502 -11
  70. package/apps/api/src/modules/settings/settings.controller.ts +55 -0
  71. package/apps/api/src/modules/settings/settings.routes.ts +25 -0
  72. package/apps/api/src/modules/settings/settings.service.ts +49 -0
  73. package/apps/api/src/modules/system/filesystem.controller.ts +66 -0
  74. package/apps/api/src/modules/system/index.ts +1 -0
  75. package/apps/api/src/modules/system/server-check.controller.ts +475 -0
  76. package/apps/api/src/modules/system/setup-session.ts +255 -0
  77. package/apps/api/src/modules/system/setup.controller.ts +128 -0
  78. package/apps/api/src/modules/system/system.routes.ts +42 -0
  79. package/apps/api/src/modules/webhooks/webhook.controller.ts +57 -15
  80. package/apps/api/src/modules/webhooks/webhook.routes.ts +11 -8
  81. package/apps/api/src/modules/webhooks/webhook.service.ts +46 -5
  82. package/apps/api/src/modules/webhooks/webhook.types.ts +35 -0
  83. package/apps/api/tsconfig.json +3 -1
  84. package/apps/cli/package.json +8 -10
  85. package/apps/cli/tsconfig.json +2 -1
  86. package/apps/dashboard/Dockerfile +4 -6
  87. package/apps/dashboard/components.json +20 -0
  88. package/apps/dashboard/next-env.d.ts +6 -0
  89. package/apps/dashboard/next.config.mjs +11 -1
  90. package/apps/dashboard/package.json +25 -13
  91. package/apps/dashboard/postcss.config.mjs +5 -0
  92. package/apps/dashboard/public/android-chrome-192x192.png +0 -0
  93. package/apps/dashboard/public/android-chrome-512x512.png +0 -0
  94. package/apps/dashboard/public/apple-touch-icon.png +0 -0
  95. package/apps/dashboard/public/favicon-16x16.png +0 -0
  96. package/apps/dashboard/public/favicon-32x32.png +0 -0
  97. package/apps/dashboard/public/favicon.ico +0 -0
  98. package/apps/dashboard/public/site.webmanifest +19 -0
  99. package/apps/dashboard/src/app/(auth)/forgot-password/page.tsx +114 -0
  100. package/apps/dashboard/src/app/(auth)/layout.tsx +18 -3
  101. package/apps/dashboard/src/app/(auth)/login/page.tsx +215 -6
  102. package/apps/dashboard/src/app/(auth)/providers.tsx +34 -0
  103. package/apps/dashboard/src/app/(auth)/register/page.tsx +171 -5
  104. package/apps/dashboard/src/app/(auth)/reset-password/page.tsx +172 -0
  105. package/apps/dashboard/src/app/(auth)/verify-email/page.tsx +190 -0
  106. package/apps/dashboard/src/app/(dashboard)/(deployment)/build/[id]/page.tsx +162 -0
  107. package/apps/dashboard/src/app/(dashboard)/(deployment)/deploy/[slug]/components/BuildSummary.tsx +166 -0
  108. package/apps/dashboard/src/app/(dashboard)/(deployment)/deploy/[slug]/components/DnsConfiguration.tsx +119 -0
  109. package/apps/dashboard/src/app/(dashboard)/(deployment)/deploy/[slug]/components/DomainSettings.tsx +291 -0
  110. package/apps/dashboard/src/app/(dashboard)/(deployment)/deploy/[slug]/components/Header.tsx +71 -0
  111. package/apps/dashboard/src/app/(dashboard)/(deployment)/deploy/[slug]/components/Sidebar.tsx +270 -0
  112. package/apps/dashboard/src/app/(dashboard)/(deployment)/deploy/[slug]/components/SkeletonLoader.tsx +87 -0
  113. package/apps/dashboard/src/app/(dashboard)/(deployment)/deploy/[slug]/page.tsx +169 -0
  114. package/apps/dashboard/src/app/(dashboard)/(deployment)/layout.tsx +7 -0
  115. package/apps/dashboard/src/app/(dashboard)/billing/page.tsx +1 -8
  116. package/apps/dashboard/src/app/(dashboard)/deployments/components/CommitDetailsModal.tsx +240 -0
  117. package/apps/dashboard/src/app/(dashboard)/deployments/components/DeploymentCard.tsx +121 -0
  118. package/apps/dashboard/src/app/(dashboard)/deployments/components/DeploymentHeader.tsx +89 -0
  119. package/apps/dashboard/src/app/(dashboard)/deployments/components/DeploymentMenu.tsx +182 -0
  120. package/apps/dashboard/src/app/(dashboard)/deployments/components/DeploymentsContent.tsx +324 -0
  121. package/apps/dashboard/src/app/(dashboard)/deployments/components/DeploymentsFilters.tsx +99 -0
  122. package/apps/dashboard/src/app/(dashboard)/deployments/components/DeploymentsList.tsx +31 -0
  123. package/apps/dashboard/src/app/(dashboard)/deployments/components/EmptyState.tsx +101 -0
  124. package/apps/dashboard/src/app/(dashboard)/deployments/components/LoadingSkeleton.tsx +57 -0
  125. package/apps/dashboard/src/app/(dashboard)/deployments/components/ProjectFilter.tsx +109 -0
  126. package/apps/dashboard/src/app/(dashboard)/deployments/components/index.ts +9 -0
  127. package/apps/dashboard/src/app/(dashboard)/deployments/page.tsx +9 -3
  128. package/apps/dashboard/src/app/(dashboard)/deployments/types.ts +44 -0
  129. package/apps/dashboard/src/app/(dashboard)/deployments/utils.ts +176 -0
  130. package/apps/dashboard/src/app/(dashboard)/domains/page.tsx +1 -6
  131. package/apps/dashboard/src/app/(dashboard)/integrations/page.tsx +321 -0
  132. package/apps/dashboard/src/app/(dashboard)/layout.tsx +22 -32
  133. package/apps/dashboard/src/app/(dashboard)/library/components/ConnectPrompt.tsx +187 -0
  134. package/apps/dashboard/src/app/(dashboard)/library/components/LibrarySidebar.tsx +147 -0
  135. package/apps/dashboard/src/app/(dashboard)/library/components/LoadingSkeleton.tsx +39 -0
  136. package/apps/dashboard/src/app/(dashboard)/library/components/LocalProjects.tsx +548 -0
  137. package/apps/dashboard/src/app/(dashboard)/library/components/RepositoryList.tsx +292 -0
  138. package/apps/dashboard/src/app/(dashboard)/library/components/TemplateGrid.tsx +36 -0
  139. package/apps/dashboard/src/app/(dashboard)/library/components/UrlImport.tsx +74 -0
  140. package/apps/dashboard/src/app/(dashboard)/library/page.tsx +121 -0
  141. package/apps/dashboard/src/app/(dashboard)/library/types.ts +26 -0
  142. package/apps/dashboard/src/app/(dashboard)/monitoring/page.tsx +1 -6
  143. package/apps/dashboard/src/app/(dashboard)/page.tsx +485 -0
  144. package/apps/dashboard/src/app/(dashboard)/projects/[id]/[[...slug]]/layout.tsx +22 -0
  145. package/apps/dashboard/src/app/(dashboard)/projects/[id]/[[...slug]]/page.tsx +286 -0
  146. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/AdvancedSettings.tsx +235 -0
  147. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/BuildSettings.tsx +174 -0
  148. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/DeleteConfirmationDialog.tsx +45 -0
  149. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/DeletionModal.tsx +95 -0
  150. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/Deployments.tsx +11 -0
  151. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/DomainSettings.tsx +523 -0
  152. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/EnvironmentSettings.tsx +246 -0
  153. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/GeneralSettings.tsx +93 -0
  154. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/GitInfo.tsx +261 -0
  155. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/GitSettings.tsx +391 -0
  156. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/LogsSettings.tsx +133 -0
  157. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/ProjectNotFound.tsx +95 -0
  158. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/ProjectsBottomNavigation.tsx +62 -0
  159. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/ResourceSettings.tsx +333 -0
  160. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/SettingSection.tsx +66 -0
  161. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/SettingsNavigation.tsx +27 -0
  162. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/SleepModeSettings.tsx +134 -0
  163. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/ProductionUrl.tsx +25 -0
  164. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/ProjectIdentity.tsx +159 -0
  165. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/ProjectInfo.tsx +58 -0
  166. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/SkeletonLoader.tsx +170 -0
  167. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/StatsCards.tsx +33 -0
  168. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/TopPaths.tsx +56 -0
  169. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/TrafficChart.tsx +157 -0
  170. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/general/index.ts +14 -0
  171. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/logs/LogsActions.tsx +52 -0
  172. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/logs/ServerLogs.tsx +249 -0
  173. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/logs/TerminalLogs.tsx +682 -0
  174. package/apps/dashboard/src/app/(dashboard)/projects/[id]/components/logs/logs.css +127 -0
  175. package/apps/dashboard/src/app/(dashboard)/projects/components/DeploymentCard.tsx +73 -0
  176. package/apps/dashboard/src/app/(dashboard)/projects/components/ProjectCard.tsx +330 -0
  177. package/apps/dashboard/src/app/(dashboard)/projects/page.tsx +326 -4
  178. package/apps/dashboard/src/app/(dashboard)/providers.tsx +23 -0
  179. package/apps/dashboard/src/app/(dashboard)/servers/[serverId]/_components/components-tab.tsx +329 -0
  180. package/apps/dashboard/src/app/(dashboard)/servers/[serverId]/_components/overview-tab.tsx +229 -0
  181. package/apps/dashboard/src/app/(dashboard)/servers/[serverId]/_components/terminal-tab.tsx +38 -0
  182. package/apps/dashboard/src/app/(dashboard)/servers/[serverId]/page.tsx +437 -0
  183. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/checking-state.tsx +15 -0
  184. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/choose-mode.tsx +54 -0
  185. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/component-row.tsx +65 -0
  186. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/error-banner.tsx +13 -0
  187. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/installing-panel.tsx +227 -0
  188. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/results-panel.tsx +90 -0
  189. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/setup-header.tsx +56 -0
  190. package/apps/dashboard/src/app/(dashboard)/servers/new/_components/types.ts +13 -0
  191. package/apps/dashboard/src/app/(dashboard)/servers/new/page.tsx +666 -0
  192. package/apps/dashboard/src/app/(dashboard)/servers/page.tsx +283 -0
  193. package/apps/dashboard/src/app/(dashboard)/settings/_components/BuildPreferences.tsx +110 -0
  194. package/apps/dashboard/src/app/(dashboard)/settings/_components/CloudConnection.tsx +147 -0
  195. package/apps/dashboard/src/app/(dashboard)/settings/_components/InstanceInfo.tsx +54 -0
  196. package/apps/dashboard/src/app/(dashboard)/settings/_components/ServerConnection.tsx +407 -0
  197. package/apps/dashboard/src/app/(dashboard)/settings/_components/SettingsSection.tsx +32 -0
  198. package/apps/dashboard/src/app/(dashboard)/settings/page.tsx +62 -3
  199. package/apps/dashboard/src/app/auth/callback/close/page.tsx +23 -0
  200. package/apps/dashboard/src/app/auth/callback/install/page.tsx +40 -0
  201. package/apps/dashboard/src/app/authorize/page.tsx +137 -0
  202. package/apps/dashboard/src/app/globals.css +57 -3
  203. package/apps/dashboard/src/app/layout.tsx +43 -3
  204. package/apps/dashboard/src/components/auth-shell.tsx +66 -0
  205. package/apps/dashboard/src/components/deployments/types.ts +44 -0
  206. package/apps/dashboard/src/components/global-deployments/index.ts +17 -0
  207. package/apps/dashboard/src/components/i18n-provider.tsx +100 -0
  208. package/apps/dashboard/src/components/import-project/BuildSettings.tsx +376 -0
  209. package/apps/dashboard/src/components/import-project/BuildSkeleton.tsx +161 -0
  210. package/apps/dashboard/src/components/import-project/BuildTerminal.tsx +358 -0
  211. package/apps/dashboard/src/components/import-project/ComposeServices.tsx +455 -0
  212. package/apps/dashboard/src/components/import-project/DeploymentProcessing.tsx +510 -0
  213. package/apps/dashboard/src/components/import-project/DockerSettings.tsx +64 -0
  214. package/apps/dashboard/src/components/import-project/EnvironmentVariables.tsx +481 -0
  215. package/apps/dashboard/src/components/import-project/Frameworks.tsx +90 -0
  216. package/apps/dashboard/src/components/import-project/ProjectSettings.tsx +160 -0
  217. package/apps/dashboard/src/components/import-project/types.ts +82 -0
  218. package/apps/dashboard/src/components/integrations/GithubPermissionModal.tsx +173 -0
  219. package/apps/dashboard/src/components/language-switcher.tsx +36 -0
  220. package/apps/dashboard/src/components/logo.tsx +13 -0
  221. package/apps/dashboard/src/components/network-error-handler.tsx +27 -0
  222. package/apps/dashboard/src/components/oauth-buttons.tsx +81 -0
  223. package/apps/dashboard/src/components/overview/ActivityChart.tsx +247 -0
  224. package/apps/dashboard/src/components/overview/ApiRequestsCard.tsx +111 -0
  225. package/apps/dashboard/src/components/overview/CreditsCard.tsx +137 -0
  226. package/apps/dashboard/src/components/overview/DeploymentsChart.tsx +195 -0
  227. package/apps/dashboard/src/components/overview/EmptyState.tsx +126 -0
  228. package/apps/dashboard/src/components/overview/LoadingSkeleton.tsx +92 -0
  229. package/apps/dashboard/src/components/overview/OverviewHeader.tsx +74 -0
  230. package/apps/dashboard/src/components/overview/OverviewHeroChart.tsx +344 -0
  231. package/apps/dashboard/src/components/overview/ProjectsCard.tsx +134 -0
  232. package/apps/dashboard/src/components/overview/QuickLinks.tsx +119 -0
  233. package/apps/dashboard/src/components/overview/SandboxesCard.tsx +150 -0
  234. package/apps/dashboard/src/components/overview/StatsGrid.tsx +166 -0
  235. package/apps/dashboard/src/components/overview/TokenUsageChart.tsx +206 -0
  236. package/apps/dashboard/src/components/overview/UsageStats.tsx +79 -0
  237. package/apps/dashboard/src/components/overview/index.ts +30 -0
  238. package/apps/dashboard/src/components/overview/types.ts +116 -0
  239. package/apps/dashboard/src/components/page-header.tsx +25 -0
  240. package/apps/dashboard/src/components/project-settings/ServerSideSwitch.jsx +60 -0
  241. package/apps/dashboard/src/components/shared/AlertBox.tsx +39 -0
  242. package/apps/dashboard/src/components/shared/CTASection.tsx +45 -0
  243. package/apps/dashboard/src/components/shared/ErrorState.tsx +176 -0
  244. package/apps/dashboard/src/components/shared/FeatureCard.tsx +20 -0
  245. package/apps/dashboard/src/components/shared/MDXComponents.tsx +24 -0
  246. package/apps/dashboard/src/components/shared/MachineSettingsModal.tsx +286 -0
  247. package/apps/dashboard/src/components/shared/Modal.tsx +39 -0
  248. package/apps/dashboard/src/components/shared/OTPInput.tsx +122 -0
  249. package/apps/dashboard/src/components/shared/PageHero.tsx +21 -0
  250. package/apps/dashboard/src/components/shared/PlatformFeatureCard.tsx +27 -0
  251. package/apps/dashboard/src/components/shared/StatCard.tsx +14 -0
  252. package/apps/dashboard/src/components/shared/ValueCard.tsx +20 -0
  253. package/apps/dashboard/src/components/shared/index.ts +8 -0
  254. package/apps/dashboard/src/components/sidebar.tsx +282 -0
  255. package/apps/dashboard/src/components/theme-provider.tsx +96 -0
  256. package/apps/dashboard/src/components/toast.tsx +106 -0
  257. package/apps/dashboard/src/components/ui/CustomCursor.tsx +107 -0
  258. package/apps/dashboard/src/components/ui/CustomSelect.tsx +126 -0
  259. package/apps/dashboard/src/components/ui/DropdownMenu.tsx +174 -0
  260. package/apps/dashboard/src/components/ui/FileIcon.jsx +59 -0
  261. package/apps/dashboard/src/components/ui/IconPickerModal.tsx +273 -0
  262. package/apps/dashboard/src/components/ui/InfoBanner.jsx +39 -0
  263. package/apps/dashboard/src/components/ui/Logo.tsx +14 -0
  264. package/apps/dashboard/src/components/ui/Modal.tsx +117 -0
  265. package/apps/dashboard/src/components/ui/PageContainer.tsx +12 -0
  266. package/apps/dashboard/src/components/ui/SectionContainer.jsx +7 -0
  267. package/apps/dashboard/src/components/ui/SlidingToggle.tsx +128 -0
  268. package/apps/dashboard/src/components/ui/Toast.tsx +29 -0
  269. package/apps/dashboard/src/components/ui/button.tsx +58 -0
  270. package/apps/dashboard/src/components/ui/card.tsx +60 -0
  271. package/apps/dashboard/src/components/ui/input.tsx +20 -0
  272. package/apps/dashboard/src/components/ui/label.tsx +19 -0
  273. package/apps/dashboard/src/components/ui/select.tsx +10 -0
  274. package/apps/dashboard/src/components/ui/textarea.tsx +9 -0
  275. package/apps/dashboard/src/constants/lang-colors.ts +25 -0
  276. package/apps/dashboard/src/constants/mock.ts +38 -0
  277. package/apps/dashboard/src/context/AuthContext.tsx +91 -0
  278. package/apps/dashboard/src/context/CloudContext.tsx +275 -0
  279. package/apps/dashboard/src/context/DeploymentContext.tsx +71 -0
  280. package/apps/dashboard/src/context/GitHubContext.tsx +288 -0
  281. package/apps/dashboard/src/context/ModalContext.tsx +300 -0
  282. package/apps/dashboard/src/context/NetworkContext.tsx +32 -0
  283. package/apps/dashboard/src/context/ProjectSettingsContext.tsx +727 -0
  284. package/apps/dashboard/src/context/ToastContext.tsx +129 -0
  285. package/apps/dashboard/src/context/deployment/index.ts +3 -0
  286. package/apps/dashboard/src/context/deployment/types.ts +183 -0
  287. package/apps/dashboard/src/context/deployment/useDeploymentBuild.ts +547 -0
  288. package/apps/dashboard/src/context/deployment/useDeploymentConfig.ts +176 -0
  289. package/apps/dashboard/src/hooks/useBuildConnection.ts +334 -0
  290. package/apps/dashboard/src/hooks/useMonitorStream.ts +146 -0
  291. package/apps/dashboard/src/hooks/useProjectData.ts +110 -0
  292. package/apps/dashboard/src/hooks/useSSEConnection.ts +465 -0
  293. package/apps/dashboard/src/hooks/useSSEStream.ts +309 -0
  294. package/apps/dashboard/src/hooks/useSessions.ts +104 -0
  295. package/apps/dashboard/src/hooks/useSetupStream.ts +223 -0
  296. package/apps/dashboard/src/i18n/dictionaries/ar.json +218 -0
  297. package/apps/dashboard/src/i18n/dictionaries/en.json +218 -0
  298. package/apps/dashboard/src/i18n/index.ts +16 -0
  299. package/apps/dashboard/src/lib/api/ai.ts +22 -0
  300. package/apps/dashboard/src/lib/api/auth.ts +13 -0
  301. package/apps/dashboard/src/lib/api/client.ts +199 -0
  302. package/apps/dashboard/src/lib/api/cloud.ts +15 -0
  303. package/apps/dashboard/src/lib/api/deploy.ts +58 -0
  304. package/apps/dashboard/src/lib/api/domains.ts +13 -0
  305. package/apps/dashboard/src/lib/api/endpoints.ts +129 -0
  306. package/apps/dashboard/src/lib/api/github.ts +31 -0
  307. package/apps/dashboard/src/lib/api/icons.ts +12 -0
  308. package/apps/dashboard/src/lib/api/index.ts +41 -0
  309. package/apps/dashboard/src/lib/api/projects.ts +153 -0
  310. package/apps/dashboard/src/lib/api/sandbox.ts +14 -0
  311. package/apps/dashboard/src/lib/api/settings.ts +21 -0
  312. package/apps/dashboard/src/lib/api/system.ts +159 -0
  313. package/apps/dashboard/src/lib/auth-client.ts +54 -0
  314. package/apps/dashboard/src/lib/country.ts +25 -0
  315. package/apps/dashboard/src/lib/server/api.ts +140 -0
  316. package/apps/dashboard/src/lib/server/session.ts +103 -0
  317. package/apps/dashboard/src/lib/sseClient.ts +241 -0
  318. package/apps/dashboard/src/lib/sseMessageProcessors.ts +323 -0
  319. package/apps/dashboard/src/lib/utils.ts +6 -0
  320. package/apps/dashboard/src/proxy.ts +46 -0
  321. package/apps/dashboard/src/styles/Toast.module.css +92 -0
  322. package/apps/dashboard/src/styles/fonts.css +93 -0
  323. package/apps/dashboard/src/styles/theme.css +336 -0
  324. package/apps/dashboard/src/types/research.ts +42 -0
  325. package/apps/dashboard/src/utils/SSEParser.js +150 -0
  326. package/apps/dashboard/src/utils/authWindow.ts +143 -0
  327. package/apps/dashboard/src/utils/codeTheme.ts +127 -0
  328. package/apps/dashboard/src/utils/colors.json +2810 -0
  329. package/apps/dashboard/src/utils/date.ts +121 -0
  330. package/apps/dashboard/src/utils/deployment.ts +128 -0
  331. package/apps/dashboard/src/utils/deploymentPhaseDetector.ts +232 -0
  332. package/apps/dashboard/src/utils/extToLang.js +103 -0
  333. package/apps/dashboard/src/utils/github.ts +62 -0
  334. package/apps/dashboard/src/utils/icons.js +46 -0
  335. package/apps/dashboard/src/utils/repoSlug.ts +110 -0
  336. package/apps/dashboard/src/utils/research-api.ts +163 -0
  337. package/apps/dashboard/src/utils/suggestions.ts +84 -0
  338. package/apps/dashboard/src/utils/theme.js +1 -0
  339. package/apps/dashboard/src/utils/ui-helpers.tsx +30 -0
  340. package/apps/dashboard/src/utils/upload.js +12 -0
  341. package/apps/dashboard/tsconfig.json +27 -5
  342. package/apps/desktop/forge.config.json +36 -0
  343. package/apps/desktop/package.json +26 -0
  344. package/apps/desktop/scripts/dev.mjs +58 -0
  345. package/apps/desktop/src/main/index.ts +675 -0
  346. package/apps/desktop/src/main/types/desktop.d.ts +33 -0
  347. package/apps/desktop/src/preload/index.ts +88 -0
  348. package/apps/desktop/src/renderer/index.html +462 -0
  349. package/apps/desktop/src/renderer/onboarding.js +511 -0
  350. package/apps/desktop/src/renderer/styles.css +1139 -0
  351. package/apps/desktop/tsconfig.json +17 -0
  352. package/apps/web/.source/browser.ts +13 -0
  353. package/apps/web/.source/dynamic.ts +8 -0
  354. package/apps/web/.source/server.ts +22 -0
  355. package/apps/web/.source/source.config.mjs +20 -0
  356. package/apps/web/Dockerfile +4 -6
  357. package/apps/web/content/blog/how-ai-builds-work.mdx +55 -0
  358. package/apps/web/content/blog/introducing-openship.mdx +46 -0
  359. package/apps/web/content/blog/self-hosting-cost-breakdown.mdx +59 -0
  360. package/apps/web/content/docs/_meta.json +14 -0
  361. package/apps/web/content/docs/api.mdx +98 -0
  362. package/apps/web/content/docs/cli.mdx +80 -0
  363. package/apps/web/content/docs/first-deployment.mdx +56 -0
  364. package/apps/web/content/docs/index.mdx +27 -0
  365. package/apps/web/content/docs/installation.mdx +65 -0
  366. package/apps/web/content/docs/quickstart.mdx +45 -0
  367. package/apps/web/next-env.d.ts +6 -0
  368. package/apps/web/next.config.mjs +16 -2
  369. package/apps/web/package.json +19 -12
  370. package/apps/web/postcss.config.mjs +5 -0
  371. package/apps/web/public/android-chrome-192x192.png +0 -0
  372. package/apps/web/public/android-chrome-512x512.png +0 -0
  373. package/apps/web/public/apple-touch-icon.png +0 -0
  374. package/apps/web/public/favicon-16x16.png +0 -0
  375. package/apps/web/public/favicon-32x32.png +0 -0
  376. package/apps/web/public/favicon.ico +0 -0
  377. package/apps/web/public/site.webmanifest +1 -0
  378. package/apps/web/source.config.ts +17 -0
  379. package/apps/web/src/app/(marketing)/layout.tsx +7 -22
  380. package/apps/web/src/app/blog/[...slug]/page.tsx +247 -0
  381. package/apps/web/src/app/blog/blog.css +575 -0
  382. package/apps/web/src/app/blog/layout.tsx +50 -0
  383. package/apps/web/src/app/blog/page.tsx +235 -0
  384. package/apps/web/src/app/docs/[[...slug]]/page.tsx +67 -0
  385. package/apps/web/src/app/docs/layout.tsx +62 -0
  386. package/apps/web/src/app/docs/toc-theme.css +71 -0
  387. package/apps/web/src/app/download/page.tsx +396 -0
  388. package/apps/web/src/app/globals.css +20 -3
  389. package/apps/web/src/app/layout.tsx +57 -3
  390. package/apps/web/src/app/page.tsx +47 -6
  391. package/apps/web/src/app/robots.ts +15 -0
  392. package/apps/web/src/app/sitemap.ts +35 -0
  393. package/apps/web/src/components/blog/share-sidebar.tsx +81 -0
  394. package/apps/web/src/components/blog/writer-sidebar.tsx +81 -0
  395. package/apps/web/src/components/landing/ai-native.tsx +169 -0
  396. package/apps/web/src/components/landing/backups.tsx +139 -0
  397. package/apps/web/src/components/landing/cdn.tsx +109 -0
  398. package/apps/web/src/components/landing/coming-soon.tsx +180 -0
  399. package/apps/web/src/components/landing/dark-section.tsx +166 -0
  400. package/apps/web/src/components/landing/dashboard.tsx +407 -0
  401. package/apps/web/src/components/landing/developer-experience.tsx +224 -0
  402. package/apps/web/src/components/landing/dns-domains.tsx +146 -0
  403. package/apps/web/src/components/landing/download-button.tsx +151 -0
  404. package/apps/web/src/components/landing/features/ai-visual.tsx +48 -0
  405. package/apps/web/src/components/landing/features/cloud-visual.tsx +44 -0
  406. package/apps/web/src/components/landing/features/git-visual.tsx +61 -0
  407. package/apps/web/src/components/landing/features/index.tsx +642 -0
  408. package/apps/web/src/components/landing/features/infra-visual.tsx +69 -0
  409. package/apps/web/src/components/landing/features/rollback-visual.tsx +42 -0
  410. package/apps/web/src/components/landing/features/ssl-visual.tsx +41 -0
  411. package/apps/web/src/components/landing/features/stacks-visual.tsx +54 -0
  412. package/apps/web/src/components/landing/final-cta.tsx +116 -0
  413. package/apps/web/src/components/landing/footer.tsx +145 -0
  414. package/apps/web/src/components/landing/hero.tsx +110 -0
  415. package/apps/web/src/components/landing/how-it-works.tsx +210 -0
  416. package/apps/web/src/components/landing/index.ts +21 -0
  417. package/apps/web/src/components/landing/mail-server.tsx +122 -0
  418. package/apps/web/src/components/landing/navbar.tsx +199 -0
  419. package/apps/web/src/components/landing/no-lockin.tsx +185 -0
  420. package/apps/web/src/components/landing/open-source-cta.tsx +151 -0
  421. package/apps/web/src/components/landing/portability.tsx +190 -0
  422. package/apps/web/src/components/landing/pricing.tsx +263 -0
  423. package/apps/web/src/components/landing/scaling.tsx +209 -0
  424. package/apps/web/src/components/landing/why-openship.tsx +291 -0
  425. package/apps/web/src/hooks/use-gsap-scroll.ts +158 -0
  426. package/apps/web/src/hooks/use-platform.ts +86 -0
  427. package/apps/web/src/lib/source.ts +12 -0
  428. package/apps/web/src/styles/fonts.css +84 -0
  429. package/apps/web/src/styles/landing.css +951 -0
  430. package/apps/web/src/styles/theme.css +227 -0
  431. package/apps/web/tsconfig.json +8 -3
  432. package/bun.lock +3260 -0
  433. package/docker-compose.yml +3 -0
  434. package/docs/screenshots/.gitkeep +0 -0
  435. package/docs/screenshots/screen.png +0 -0
  436. package/openship.code-workspace +7 -0
  437. package/package.json +23 -9
  438. package/packages/adapters/docs/ARCHITECTURE.md +160 -0
  439. package/packages/adapters/docs/BUILD-PIPELINE.md +171 -0
  440. package/packages/adapters/docs/CLOUD.md +239 -0
  441. package/packages/adapters/docs/EXECUTOR.md +174 -0
  442. package/packages/adapters/docs/LOG-STREAMING.md +175 -0
  443. package/packages/adapters/docs/SYSTEM.md +191 -0
  444. package/packages/adapters/package.json +14 -9
  445. package/packages/adapters/src/index.ts +144 -4
  446. package/packages/adapters/src/infra/cloud.ts +42 -0
  447. package/packages/adapters/src/infra/index.ts +9 -0
  448. package/packages/adapters/src/infra/noop.ts +27 -0
  449. package/packages/adapters/src/infra/traefik.ts +297 -0
  450. package/packages/adapters/src/infra/types.ts +36 -0
  451. package/packages/adapters/src/platform.ts +255 -0
  452. package/packages/adapters/src/runtime/bare.ts +567 -0
  453. package/packages/adapters/src/runtime/build-pipeline.ts +237 -0
  454. package/packages/adapters/src/runtime/cloud.ts +791 -0
  455. package/packages/adapters/src/runtime/deploy-pipeline.ts +146 -0
  456. package/packages/adapters/src/runtime/docker.ts +443 -0
  457. package/packages/adapters/src/runtime/index.ts +69 -0
  458. package/packages/adapters/src/runtime/local-build.ts +112 -0
  459. package/packages/adapters/src/runtime/transfer.ts +82 -0
  460. package/packages/adapters/src/runtime/types.ts +138 -0
  461. package/packages/adapters/src/system/catalog.ts +188 -0
  462. package/packages/adapters/src/system/checks.ts +228 -0
  463. package/packages/adapters/src/system/components.ts +45 -0
  464. package/packages/adapters/src/system/debug.ts +18 -0
  465. package/packages/adapters/src/system/environment.ts +168 -0
  466. package/packages/adapters/src/system/errors.ts +34 -0
  467. package/packages/adapters/src/system/executor.ts +600 -0
  468. package/packages/adapters/src/system/index.ts +58 -0
  469. package/packages/adapters/src/system/installer.ts +310 -0
  470. package/packages/adapters/src/system/setup.ts +442 -0
  471. package/packages/adapters/src/system/state.ts +96 -0
  472. package/packages/adapters/src/system/types.ts +116 -0
  473. package/packages/adapters/src/toolchain/catalog.ts +439 -0
  474. package/packages/adapters/src/toolchain/checks.ts +181 -0
  475. package/packages/adapters/src/toolchain/index.ts +38 -0
  476. package/packages/adapters/src/toolchain/installer.ts +189 -0
  477. package/packages/adapters/src/toolchain/types.ts +84 -0
  478. package/packages/adapters/src/types.ts +277 -0
  479. package/packages/adapters/tsconfig.json +3 -1
  480. package/packages/core/package.json +9 -9
  481. package/packages/core/src/constants.ts +44 -1
  482. package/packages/core/src/index.ts +2 -0
  483. package/packages/core/src/stacks.ts +778 -0
  484. package/packages/core/src/system.ts +111 -0
  485. package/packages/core/src/types.ts +22 -1
  486. package/packages/core/src/utils.ts +8 -0
  487. package/packages/core/tsconfig.json +3 -1
  488. package/packages/db/drizzle/0000_init.sql +231 -0
  489. package/packages/db/drizzle/0001_add_server_name.sql +1 -0
  490. package/packages/db/drizzle/meta/_journal.json +20 -0
  491. package/packages/db/drizzle.config.ts +10 -0
  492. package/packages/db/package.json +26 -14
  493. package/packages/db/src/client.ts +119 -0
  494. package/packages/db/src/index.ts +57 -8
  495. package/packages/db/src/repos/account.repo.ts +92 -0
  496. package/packages/db/src/repos/deployment.repo.ts +168 -0
  497. package/packages/db/src/repos/domain.repo.ts +102 -0
  498. package/packages/db/src/repos/git-installation.repo.ts +95 -0
  499. package/packages/db/src/repos/index.ts +43 -0
  500. package/packages/db/src/repos/instance-settings.repo.ts +43 -0
  501. package/packages/db/src/repos/project.repo.ts +159 -0
  502. package/packages/db/src/repos/session.repo.ts +82 -0
  503. package/packages/db/src/repos/settings.repo.ts +51 -0
  504. package/packages/db/src/repos/user.repo.ts +94 -0
  505. package/packages/db/src/schema/auth.ts +56 -0
  506. package/packages/db/src/schema/deployment.ts +87 -0
  507. package/packages/db/src/schema/domain.ts +46 -0
  508. package/packages/db/src/schema/github.ts +24 -0
  509. package/packages/db/src/schema/index.ts +6 -0
  510. package/packages/db/src/schema/project.ts +117 -0
  511. package/packages/db/src/schema/settings.ts +103 -0
  512. package/packages/db/tsconfig.json +3 -1
  513. package/packages/ui/package.json +17 -17
  514. package/packages/ui/src/components/status-dot.tsx +1 -1
  515. package/packages/ui/tsconfig.json +7 -2
  516. package/pnpm-workspace.yaml +0 -1
  517. package/{tooling/tsconfig/base.json → tsconfig.base.json} +0 -1
  518. package/turbo.json +4 -0
  519. package/apps/api/src/modules/auth/auth.controller.ts +0 -26
  520. package/apps/api/src/modules/auth/auth.schema.ts +0 -12
  521. package/apps/api/src/modules/auth/auth.service.ts +0 -23
  522. package/apps/dashboard/src/app/page.tsx +0 -6
  523. package/apps/web/src/app/(marketing)/docs/page.tsx +0 -10
  524. package/packages/adapters/src/base-adapter.ts +0 -49
  525. package/packages/adapters/src/docker-adapter.ts +0 -43
  526. package/packages/adapters/src/oblien-adapter.ts +0 -46
  527. package/packages/adapters/src/registry.ts +0 -22
  528. package/packages/db/prisma/schema.prisma +0 -160
  529. package/tooling/tsconfig/nextjs.json +0 -10
  530. package/tooling/tsconfig/node.json +0 -10
  531. package/tooling/tsconfig/package.json +0 -7
@@ -0,0 +1,89 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { Rocket, CheckCircle2, XCircle, Loader2 } from "lucide-react";
5
+
6
+ interface DeploymentHeaderProps {
7
+ stats: {
8
+ total: number;
9
+ success: number;
10
+ failed: number;
11
+ building: number;
12
+ pending?: number;
13
+ canceled?: number;
14
+ };
15
+ projectCount?: number;
16
+ }
17
+
18
+ export const DeploymentHeader: React.FC<DeploymentHeaderProps> = ({ stats, projectCount }) => {
19
+ const failureCount = (stats.failed || 0) + (stats.canceled || 0);
20
+ const successRate = stats.total > 0 ? Math.round((stats.success / stats.total) * 100) : 0;
21
+ const activeCount = (stats.building || 0) + (stats.pending || 0);
22
+
23
+ return (
24
+ <div className="bg-card rounded-2xl border border-border/50 p-5">
25
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
26
+ {/* Total */}
27
+ <div className="flex items-center gap-3">
28
+ <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
29
+ <Rocket className="size-4 text-primary" />
30
+ </div>
31
+ <div>
32
+ <span className="text-sm text-muted-foreground">Total</span>
33
+ <div className="flex items-baseline gap-1.5">
34
+ <span className="text-lg font-semibold text-foreground">{stats.total}</span>
35
+ {projectCount !== undefined && (
36
+ <span className="text-[10px] text-muted-foreground">
37
+ {projectCount} project{projectCount !== 1 ? "s" : ""}
38
+ </span>
39
+ )}
40
+ </div>
41
+ </div>
42
+ </div>
43
+
44
+ {/* Success */}
45
+ <div className="flex items-center gap-3">
46
+ <div className="w-8 h-8 rounded-lg bg-emerald-500/10 flex items-center justify-center shrink-0">
47
+ <CheckCircle2 className="size-4 text-emerald-500" />
48
+ </div>
49
+ <div>
50
+ <span className="text-sm text-muted-foreground">Success</span>
51
+ <div className="flex items-baseline gap-1.5">
52
+ <span className="text-lg font-semibold text-foreground">{stats.success}</span>
53
+ <span className="text-[10px] font-medium px-1.5 py-0.5 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400">
54
+ {successRate}%
55
+ </span>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ {/* Failed */}
61
+ <div className="flex items-center gap-3">
62
+ <div className="w-8 h-8 rounded-lg bg-red-500/10 flex items-center justify-center shrink-0">
63
+ <XCircle className="size-4 text-red-500" />
64
+ </div>
65
+ <div>
66
+ <span className="text-sm text-muted-foreground">Failed</span>
67
+ <p className="text-lg font-semibold text-foreground">{failureCount}</p>
68
+ </div>
69
+ </div>
70
+
71
+ {/* In Progress */}
72
+ <div className="flex items-center gap-3">
73
+ <div className="w-8 h-8 rounded-lg bg-amber-500/10 flex items-center justify-center shrink-0">
74
+ <Loader2 className={`size-4 text-amber-500${activeCount > 0 ? " animate-spin" : ""}`} />
75
+ </div>
76
+ <div>
77
+ <span className="text-sm text-muted-foreground">In Progress</span>
78
+ <div className="flex items-baseline gap-1.5">
79
+ <span className="text-lg font-semibold text-foreground">{activeCount}</span>
80
+ {activeCount > 0 && (
81
+ <span className="text-[10px] text-muted-foreground">building now</span>
82
+ )}
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ );
89
+ };
@@ -0,0 +1,182 @@
1
+ "use client";
2
+
3
+ import React, { useState, useRef, useEffect } from "react";
4
+ import { MoreVertical, ExternalLink, Copy, RotateCcw, XCircle, Trash2 } from "lucide-react";
5
+ import { generateIcon } from "@/utils/icons";
6
+ import { deployApi } from "@/lib/api";
7
+
8
+ interface Deployment {
9
+ id: string;
10
+ status: string;
11
+ domain: string;
12
+ owner?: string;
13
+ repo?: string;
14
+ commit: {
15
+ hash: string;
16
+ };
17
+ }
18
+
19
+ interface DeploymentMenuProps {
20
+ deployment: Deployment;
21
+ triggerClassName?: string;
22
+ onStatusChange?: () => void;
23
+ }
24
+
25
+ export const DeploymentMenu: React.FC<DeploymentMenuProps> = ({
26
+ deployment,
27
+ triggerClassName,
28
+ onStatusChange,
29
+ }) => {
30
+ const [isOpen, setIsOpen] = useState(false);
31
+ const menuRef = useRef<HTMLDivElement>(null);
32
+
33
+ useEffect(() => {
34
+ const handleClickOutside = (event: MouseEvent) => {
35
+ if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
36
+ setIsOpen(false);
37
+ }
38
+ };
39
+
40
+ if (isOpen) {
41
+ document.addEventListener("mousedown", handleClickOutside);
42
+ }
43
+
44
+ return () => {
45
+ document.removeEventListener("mousedown", handleClickOutside);
46
+ };
47
+ }, [isOpen]);
48
+
49
+ const isActive = ["pending", "queued", "building", "deploying"].includes(deployment.status);
50
+
51
+ const handleCancel = async (e: React.MouseEvent) => {
52
+ e.stopPropagation();
53
+ setIsOpen(false);
54
+ try {
55
+ await deployApi.cancel(deployment.id);
56
+ onStatusChange?.();
57
+ } catch {
58
+ /* silent */
59
+ }
60
+ };
61
+
62
+ const handleDelete = async (e: React.MouseEvent) => {
63
+ e.stopPropagation();
64
+ setIsOpen(false);
65
+ try {
66
+ await deployApi.deleteDeployment(deployment.id);
67
+ onStatusChange?.();
68
+ } catch {
69
+ /* silent */
70
+ }
71
+ };
72
+
73
+ return (
74
+ <div className="relative" ref={menuRef}>
75
+ <button
76
+ onClick={(e) => {
77
+ e.stopPropagation();
78
+ setIsOpen(!isOpen);
79
+ }}
80
+ className={triggerClassName || "w-8 h-8 rounded-lg flex items-center justify-center text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"}
81
+ >
82
+ <MoreVertical className="w-4 h-4" />
83
+ </button>
84
+
85
+ {isOpen && (
86
+ <div className="absolute right-0 top-10 w-56 bg-popover rounded-xl shadow-lg border border-border/50 py-2 z-50 animate-in fade-in slide-in-from-top-2 duration-200">
87
+ {deployment.domain && (
88
+ <button
89
+ onClick={() => {
90
+ window.open(`https://${deployment.domain}`, "_blank");
91
+ setIsOpen(false);
92
+ }}
93
+ className="w-full px-4 py-2.5 text-left text-sm text-foreground/70 hover:bg-muted transition-colors flex items-center gap-3"
94
+ >
95
+ <ExternalLink className="w-4 h-4" />
96
+ Open Deployment
97
+ </button>
98
+ )}
99
+
100
+ {deployment.owner && deployment.repo && (
101
+ <button
102
+ onClick={() => {
103
+ window.open(`https://github.com/${deployment.owner}/${deployment.repo}`, "_blank");
104
+ setIsOpen(false);
105
+ }}
106
+ className="w-full px-4 py-2.5 text-left text-sm text-foreground/70 hover:bg-muted transition-colors flex items-center gap-3"
107
+ >
108
+ {generateIcon('https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg', 16, 'currentColor', {}, true)}
109
+ View Repository
110
+ </button>
111
+ )}
112
+
113
+ <div className="h-px bg-border/50 my-2" />
114
+
115
+ {deployment.domain && (
116
+ <button
117
+ onClick={() => {
118
+ navigator.clipboard.writeText(`https://${deployment.domain}`);
119
+ setIsOpen(false);
120
+ }}
121
+ className="w-full px-4 py-2.5 text-left text-sm text-foreground/70 hover:bg-muted transition-colors flex items-center gap-3"
122
+ >
123
+ <Copy className="w-4 h-4" />
124
+ Copy Domain URL
125
+ </button>
126
+ )}
127
+
128
+ <button
129
+ onClick={() => {
130
+ navigator.clipboard.writeText(deployment.id);
131
+ setIsOpen(false);
132
+ }}
133
+ className="w-full px-4 py-2.5 text-left text-sm text-foreground/70 hover:bg-muted transition-colors flex items-center gap-3"
134
+ >
135
+ <Copy className="w-4 h-4" />
136
+ Copy Build ID
137
+ </button>
138
+
139
+ {isActive && (
140
+ <>
141
+ <div className="h-px bg-border/50 my-2" />
142
+ <button
143
+ onClick={handleCancel}
144
+ className="w-full px-4 py-2.5 text-left text-sm text-red-500 hover:bg-red-500/10 transition-colors flex items-center gap-3"
145
+ >
146
+ <XCircle className="w-4 h-4" />
147
+ Cancel Deployment
148
+ </button>
149
+ </>
150
+ )}
151
+
152
+ {!isActive && deployment.status !== "building" && (
153
+ <>
154
+ <div className="h-px bg-border/50 my-2" />
155
+ <button
156
+ onClick={() => setIsOpen(false)}
157
+ className="w-full px-4 py-2.5 text-left text-sm text-foreground/70 hover:bg-muted transition-colors flex items-center gap-3"
158
+ >
159
+ <RotateCcw className="w-4 h-4" />
160
+ Redeploy
161
+ </button>
162
+ </>
163
+ )}
164
+
165
+ {!isActive && (
166
+ <>
167
+ <div className="h-px bg-border/50 my-2" />
168
+ <button
169
+ onClick={handleDelete}
170
+ className="w-full px-4 py-2.5 text-left text-sm text-red-500 hover:bg-red-500/10 transition-colors flex items-center gap-3"
171
+ >
172
+ <Trash2 className="w-4 h-4" />
173
+ Delete Deployment
174
+ </button>
175
+ </>
176
+ )}
177
+ </div>
178
+ )}
179
+ </div>
180
+ );
181
+ };
182
+
@@ -0,0 +1,324 @@
1
+ "use client";
2
+
3
+ import React, { useState, useEffect, useMemo, useCallback } from "react";
4
+ import Link from "next/link";
5
+ import {
6
+ Rocket,
7
+ Activity,
8
+ CheckCircle2,
9
+ XCircle,
10
+ Loader2,
11
+ Clock,
12
+ Zap,
13
+ ArrowRight,
14
+ } from "lucide-react";
15
+ import { deployApi, projectsApi } from "@/lib/api";
16
+ import { DeploymentsFilters } from "./DeploymentsFilters";
17
+ import { DeploymentsList } from "./DeploymentsList";
18
+ import { LoadingSkeleton } from "./LoadingSkeleton";
19
+ import type { Deployment, Project } from "../types";
20
+ import {
21
+ calculateDeploymentStats,
22
+ filterDeployments,
23
+ sortDeploymentsByDate,
24
+ mapRowToDeployment,
25
+ formatDistanceToNow,
26
+ } from "../utils";
27
+
28
+ interface DeploymentsContentProps {
29
+ /** When set, scope to this project and hide the project selector */
30
+ projectId?: string;
31
+ projectName?: string;
32
+ }
33
+
34
+ export const DeploymentsContent: React.FC<DeploymentsContentProps> = ({
35
+ projectId,
36
+ projectName,
37
+ }) => {
38
+ const isProject = !!projectId;
39
+
40
+ const [deployments, setDeployments] = useState<Deployment[]>([]);
41
+ const [projects, setProjects] = useState<Project[]>([]);
42
+ const [isLoading, setIsLoading] = useState(true);
43
+ const [filter, setFilter] = useState<
44
+ "all" | "success" | "failed" | "building" | "pending" | "canceled"
45
+ >("all");
46
+ const [searchQuery, setSearchQuery] = useState("");
47
+ const [selectedProjectId, setSelectedProjectId] = useState<string | "all">(
48
+ "all"
49
+ );
50
+
51
+ const fetchDeployments = useCallback(async () => {
52
+ setIsLoading(true);
53
+ try {
54
+ if (isProject && projectId) {
55
+ const res = await projectsApi.getDeployments(projectId);
56
+ const rows: any[] = res.data ?? res.deployments ?? [];
57
+ const mapped = rows.map((r: any) =>
58
+ mapRowToDeployment({
59
+ ...r,
60
+ projectId,
61
+ projectName: projectName ?? r.projectName,
62
+ })
63
+ );
64
+ setDeployments(sortDeploymentsByDate(mapped));
65
+ setProjects([]);
66
+ } else {
67
+ const res = await deployApi.getAll({ perPage: 100 });
68
+ const rows: any[] = res.data ?? [];
69
+ const mapped = rows.map(mapRowToDeployment);
70
+ setDeployments(sortDeploymentsByDate(mapped));
71
+
72
+ const projectMap = new Map<string, Project>();
73
+ for (const d of mapped) {
74
+ if (d.projectId && d.projectName) {
75
+ projectMap.set(d.projectId, {
76
+ id: d.projectId,
77
+ name: d.projectName,
78
+ });
79
+ }
80
+ }
81
+ setProjects([...projectMap.values()]);
82
+ }
83
+ } catch {
84
+ /* silent */
85
+ } finally {
86
+ setIsLoading(false);
87
+ }
88
+ }, [isProject, projectId, projectName]);
89
+
90
+ useEffect(() => {
91
+ fetchDeployments();
92
+ }, [fetchDeployments]);
93
+
94
+ const filteredDeployments = useMemo(
95
+ () =>
96
+ filterDeployments(deployments, {
97
+ status: filter,
98
+ searchQuery,
99
+ projectId: selectedProjectId,
100
+ }),
101
+ [deployments, filter, searchQuery, selectedProjectId]
102
+ );
103
+
104
+ const stats = useMemo(
105
+ () => calculateDeploymentStats(deployments),
106
+ [deployments]
107
+ );
108
+
109
+ const activeCount = (stats.building || 0) + (stats.pending || 0);
110
+ const recentDeployments = deployments.slice(0, 4);
111
+
112
+ return (
113
+ <div>
114
+ {/* Header */}
115
+ <div className="mb-6">
116
+ <div className="flex items-center gap-3 mb-1">
117
+ <div className="w-9 h-9 bg-primary/10 rounded-xl flex items-center justify-center">
118
+ <Rocket className="size-[18px] text-primary" />
119
+ </div>
120
+ <div>
121
+ <h1 className="text-xl font-semibold text-foreground" style={{ letterSpacing: "-0.2px" }}>
122
+ Deployments
123
+ </h1>
124
+ <p className="text-xs text-muted-foreground">
125
+ {isLoading
126
+ ? "Loading..."
127
+ : isProject
128
+ ? `${deployments.length} deployment${deployments.length !== 1 ? "s" : ""}`
129
+ : `${deployments.length} total across ${projects.length} project${projects.length !== 1 ? "s" : ""}`}
130
+ </p>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ {/* Main Grid */}
136
+ <div className="grid grid-cols-1 lg:grid-cols-[1fr_340px] gap-6">
137
+ {/* LEFT COLUMN */}
138
+ <div className="space-y-4 min-w-0">
139
+ {isLoading ? (
140
+ <LoadingSkeleton />
141
+ ) : (
142
+ <>
143
+ <DeploymentsFilters
144
+ isProject={isProject}
145
+ filter={filter}
146
+ searchQuery={searchQuery}
147
+ selectedProjectId={selectedProjectId}
148
+ projects={projects}
149
+ onFilterChange={setFilter}
150
+ onSearchChange={setSearchQuery}
151
+ onProjectChange={setSelectedProjectId}
152
+ />
153
+
154
+ <DeploymentsList
155
+ deployments={filteredDeployments}
156
+ hasFilters={
157
+ filter !== "all" ||
158
+ searchQuery !== "" ||
159
+ selectedProjectId !== "all"
160
+ }
161
+ onStatusChange={fetchDeployments}
162
+ />
163
+ </>
164
+ )}
165
+ </div>
166
+
167
+ {/* RIGHT COLUMN (Sticky) */}
168
+ <div className="space-y-4 lg:sticky lg:top-6 lg:self-start">
169
+ {/* Activity Overview */}
170
+ <div className="bg-card rounded-2xl border border-border/50 p-5">
171
+ <div className="flex items-center gap-2 mb-4">
172
+ <Activity className="size-4 text-muted-foreground" />
173
+ <h3 className="font-semibold text-foreground text-sm">Overview</h3>
174
+ </div>
175
+
176
+ <div className="space-y-3">
177
+ <div className="flex items-center justify-between">
178
+ <div className="flex items-center gap-2">
179
+ <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center">
180
+ <Rocket className="size-4 text-primary" />
181
+ </div>
182
+ <span className="text-sm text-muted-foreground">Total</span>
183
+ </div>
184
+ <span className="text-lg font-semibold text-foreground">
185
+ {isLoading ? "–" : stats.total}
186
+ </span>
187
+ </div>
188
+
189
+ <div className="flex items-center justify-between">
190
+ <div className="flex items-center gap-2">
191
+ <div className="w-8 h-8 rounded-lg bg-emerald-500/10 flex items-center justify-center">
192
+ <CheckCircle2 className="size-4 text-emerald-500" />
193
+ </div>
194
+ <span className="text-sm text-muted-foreground">Successful</span>
195
+ </div>
196
+ <span className="text-lg font-semibold text-foreground">
197
+ {isLoading ? "–" : stats.success}
198
+ </span>
199
+ </div>
200
+
201
+ <div className="flex items-center justify-between">
202
+ <div className="flex items-center gap-2">
203
+ <div className="w-8 h-8 rounded-lg bg-red-500/10 flex items-center justify-center">
204
+ <XCircle className="size-4 text-red-500" />
205
+ </div>
206
+ <span className="text-sm text-muted-foreground">Failed</span>
207
+ </div>
208
+ <span className="text-lg font-semibold text-foreground">
209
+ {isLoading ? "–" : (stats.failed || 0) + (stats.canceled || 0)}
210
+ </span>
211
+ </div>
212
+
213
+ {activeCount > 0 && (
214
+ <div className="flex items-center justify-between">
215
+ <div className="flex items-center gap-2">
216
+ <div className="w-8 h-8 rounded-lg bg-amber-500/10 flex items-center justify-center">
217
+ <Loader2 className="size-4 text-amber-500 animate-spin" />
218
+ </div>
219
+ <span className="text-sm text-muted-foreground">In Progress</span>
220
+ </div>
221
+ <span className="text-lg font-semibold text-foreground">{activeCount}</span>
222
+ </div>
223
+ )}
224
+ </div>
225
+ </div>
226
+
227
+ {/* Quick Tip */}
228
+ {deployments.length > 0 ? (
229
+ <div className="bg-gradient-to-br from-primary/5 via-primary/3 to-transparent rounded-2xl border border-primary/10 p-5">
230
+ <div className="flex items-center gap-2 mb-3">
231
+ <Zap className="size-4 text-primary" />
232
+ <h3 className="font-semibold text-foreground text-sm">Auto-Deploy</h3>
233
+ </div>
234
+ <p className="text-sm text-muted-foreground leading-relaxed">
235
+ Enable auto-deploy to trigger deployments automatically on every push to your main branch.
236
+ </p>
237
+ {!isProject && (
238
+ <Link
239
+ href="/projects"
240
+ className="inline-flex items-center gap-1 text-sm font-medium text-primary hover:text-primary/80 mt-3 transition-colors"
241
+ >
242
+ Go to project settings
243
+ <ArrowRight className="size-3.5" />
244
+ </Link>
245
+ )}
246
+ </div>
247
+ ) : (
248
+ <div className="bg-gradient-to-br from-primary/5 via-primary/3 to-transparent rounded-2xl border border-primary/10 p-5">
249
+ <div className="flex items-center gap-2 mb-3">
250
+ <Zap className="size-4 text-primary" />
251
+ <h3 className="font-semibold text-foreground text-sm">Get Started</h3>
252
+ </div>
253
+ <p className="text-sm text-muted-foreground leading-relaxed">
254
+ Import a Git repository or use a template to create your first deployment.
255
+ </p>
256
+ <Link
257
+ href="/library"
258
+ className="inline-flex items-center gap-1 text-sm font-medium text-primary hover:text-primary/80 mt-3 transition-colors"
259
+ >
260
+ Deploy a project
261
+ <ArrowRight className="size-3.5" />
262
+ </Link>
263
+ </div>
264
+ )}
265
+
266
+ {/* Recent Deployments */}
267
+ <div className="bg-card rounded-2xl border border-border/50 p-5">
268
+ <div className="flex items-center gap-2 mb-4">
269
+ <Clock className="size-4 text-muted-foreground" />
270
+ <h3 className="font-semibold text-foreground text-sm">Recent</h3>
271
+ </div>
272
+
273
+ {isLoading ? (
274
+ <div className="space-y-3">
275
+ {[...Array(3)].map((_, i) => (
276
+ <div key={i} className="flex items-center gap-3 animate-pulse">
277
+ <div className="w-2 h-2 rounded-full bg-muted" />
278
+ <div className="flex-1 h-4 bg-muted rounded" />
279
+ </div>
280
+ ))}
281
+ </div>
282
+ ) : recentDeployments.length === 0 ? (
283
+ <div className="text-center py-4">
284
+ <div className="w-10 h-10 rounded-xl bg-muted/50 flex items-center justify-center mx-auto mb-3">
285
+ <Clock className="size-4 text-muted-foreground/50" />
286
+ </div>
287
+ <p className="text-xs text-muted-foreground/70">
288
+ Your deployments will appear here
289
+ </p>
290
+ </div>
291
+ ) : (
292
+ <div className="space-y-2.5">
293
+ {recentDeployments.map((d) => (
294
+ <Link
295
+ key={d.id}
296
+ href={`/build/${d.id}`}
297
+ className="flex items-center gap-3 group"
298
+ >
299
+ <span
300
+ className={`w-2 h-2 rounded-full shrink-0 ${
301
+ d.status === "success" ? "bg-emerald-500" :
302
+ d.status === "failed" ? "bg-red-500" :
303
+ d.status === "building" ? "bg-amber-500" :
304
+ "bg-muted-foreground/40"
305
+ }`}
306
+ />
307
+ <div className="flex-1 min-w-0">
308
+ <p className="text-sm text-foreground truncate group-hover:text-primary transition-colors">
309
+ {d.projectName || "Unknown"}
310
+ </p>
311
+ <p className="text-xs text-muted-foreground">
312
+ {formatDistanceToNow(new Date(d.createdAt))}
313
+ </p>
314
+ </div>
315
+ </Link>
316
+ ))}
317
+ </div>
318
+ )}
319
+ </div>
320
+ </div>
321
+ </div>
322
+ </div>
323
+ );
324
+ };
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ import React, { useState, useEffect, useRef } from "react";
4
+ import { Search } from "lucide-react";
5
+ import { ProjectFilter } from "./ProjectFilter";
6
+ import type { Project } from "../types";
7
+
8
+ interface DeploymentsFiltersProps {
9
+ filter: "all" | "success" | "failed" | "building" | "pending" | "canceled";
10
+ searchQuery: string;
11
+ selectedProjectId: string | "all";
12
+ projects: Project[];
13
+ onFilterChange: (filter: "all" | "success" | "failed" | "building" | "pending" | "canceled") => void;
14
+ onSearchChange: (query: string) => void;
15
+ onProjectChange: (projectId: string | "all") => void;
16
+ isProject: boolean;
17
+ }
18
+
19
+ const FILTERS = [
20
+ { value: "all", label: "All" },
21
+ { value: "success", label: "Success" },
22
+ { value: "failed", label: "Failed" },
23
+ { value: "building", label: "Building" },
24
+ { value: "canceled", label: "Canceled" },
25
+ ] as const;
26
+
27
+ export const DeploymentsFilters: React.FC<DeploymentsFiltersProps> = React.memo(({
28
+ filter,
29
+ searchQuery,
30
+ selectedProjectId,
31
+ projects,
32
+ onFilterChange,
33
+ onSearchChange,
34
+ onProjectChange,
35
+ isProject,
36
+ }) => {
37
+ const [localSearchQuery, setLocalSearchQuery] = useState(searchQuery);
38
+ const debounceTimeout = useRef<NodeJS.Timeout | null>(null);
39
+
40
+ useEffect(() => {
41
+ setLocalSearchQuery(searchQuery);
42
+ }, [searchQuery]);
43
+
44
+ const handleSearchChange = (value: string) => {
45
+ setLocalSearchQuery(value);
46
+ if (debounceTimeout.current) clearTimeout(debounceTimeout.current);
47
+ debounceTimeout.current = setTimeout(() => onSearchChange(value), 300);
48
+ };
49
+
50
+ useEffect(() => {
51
+ return () => {
52
+ if (debounceTimeout.current) clearTimeout(debounceTimeout.current);
53
+ };
54
+ }, []);
55
+
56
+ return (
57
+ <div className="bg-card rounded-2xl border border-border/50 px-4 py-3 space-y-3">
58
+ {/* Row 1: Search + Project filter */}
59
+ <div className="flex items-center gap-3">
60
+ <div className="relative flex-1">
61
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground" />
62
+ <input
63
+ type="text"
64
+ placeholder="Search deployments..."
65
+ value={localSearchQuery}
66
+ onChange={(e) => handleSearchChange(e.target.value)}
67
+ className="w-full pl-9 pr-3 py-2 bg-background border border-border rounded-lg text-sm text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring transition-colors"
68
+ />
69
+ </div>
70
+ {!isProject && (
71
+ <ProjectFilter
72
+ projects={projects}
73
+ selectedProjectId={selectedProjectId}
74
+ onProjectChange={onProjectChange}
75
+ />
76
+ )}
77
+ </div>
78
+
79
+ {/* Row 2: Status filters */}
80
+ <div className="flex items-center gap-2 flex-wrap">
81
+ {FILTERS.map((f) => (
82
+ <button
83
+ key={f.value}
84
+ onClick={() => onFilterChange(f.value)}
85
+ className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-colors ${
86
+ filter === f.value
87
+ ? "bg-foreground text-background"
88
+ : "text-muted-foreground hover:text-foreground hover:bg-muted/60"
89
+ }`}
90
+ >
91
+ {f.label}
92
+ </button>
93
+ ))}
94
+ </div>
95
+ </div>
96
+ );
97
+ });
98
+
99
+ DeploymentsFilters.displayName = "DeploymentsFilters";