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,291 @@
1
+ import React, { useState, useEffect, useCallback } from "react";
2
+ import { Globe, Shield, Server, X, Copy, Check, ChevronDown, Info } from "lucide-react";
3
+ import { domainsApi } from "@/lib/api";
4
+
5
+ interface DnsRecord {
6
+ type: "CNAME" | "A" | "TXT";
7
+ host: string;
8
+ value: string;
9
+ }
10
+
11
+ interface DomainSettingsProps {
12
+ projectName: string;
13
+ domain: string;
14
+ setDomain: (domain: string) => void;
15
+ customDomain: string;
16
+ setCustomDomain: (domain: string) => void;
17
+ domainType: "free" | "custom";
18
+ setDomainType: (type: "free" | "custom") => void;
19
+ }
20
+
21
+ const RECORD_LABELS: Record<string, string> = {
22
+ CNAME: "Routes traffic through the edge network",
23
+ A: "Points to your server IP",
24
+ TXT: "Verifies domain ownership",
25
+ };
26
+
27
+ const DomainSettings: React.FC<DomainSettingsProps> = ({
28
+ projectName,
29
+ domain,
30
+ setDomain,
31
+ customDomain,
32
+ setCustomDomain,
33
+ domainType,
34
+ setDomainType,
35
+ }) => {
36
+ const [showDnsModal, setShowDnsModal] = useState(false);
37
+ const [dnsRecords, setDnsRecords] = useState<DnsRecord[]>([]);
38
+ const [dnsMode, setDnsMode] = useState<"cloud" | "selfhosted">("cloud");
39
+ const [loading, setLoading] = useState(false);
40
+ const [copied, setCopied] = useState<string | null>(null);
41
+
42
+ const copy = (text: string, id: string) => {
43
+ navigator.clipboard.writeText(text);
44
+ setCopied(id);
45
+ setTimeout(() => setCopied(null), 2000);
46
+ };
47
+
48
+ // Fetch preview records when custom domain changes (debounced)
49
+ const fetchRecords = useCallback(async (hostname: string) => {
50
+ if (!hostname || hostname.length < 3 || !hostname.includes(".")) return;
51
+ setLoading(true);
52
+ try {
53
+ const res = await domainsApi.previewRecords(hostname);
54
+ setDnsRecords(res.data.records);
55
+ setDnsMode(res.data.mode);
56
+ } catch {
57
+ // Silently fail — user can still deploy
58
+ } finally {
59
+ setLoading(false);
60
+ }
61
+ }, []);
62
+
63
+ useEffect(() => {
64
+ if (domainType !== "custom" || !customDomain) {
65
+ setDnsRecords([]);
66
+ return;
67
+ }
68
+ const timer = setTimeout(() => fetchRecords(customDomain), 400);
69
+ return () => clearTimeout(timer);
70
+ }, [customDomain, domainType, fetchRecords]);
71
+
72
+ const hasRecords = dnsRecords.length > 0 && dnsRecords.every((r) => r.value);
73
+ const routeRecord = dnsRecords.find((r) => r.type !== "TXT");
74
+
75
+ return (
76
+ <div className="bg-card rounded-xl border border-border/50 overflow-hidden">
77
+ {/* Header */}
78
+ <div className="flex items-center gap-2.5 px-4 py-3 border-b border-border/40">
79
+ <div className="w-7 h-7 rounded-lg bg-primary/10 flex items-center justify-center">
80
+ <Globe className="size-3.5 text-primary" />
81
+ </div>
82
+ <div>
83
+ <h3 className="text-sm font-semibold text-foreground leading-tight">Domain</h3>
84
+ <p className="text-[11px] text-muted-foreground">Where your site will be accessible</p>
85
+ </div>
86
+ </div>
87
+
88
+ <div className="p-4 space-y-3">
89
+ {/* Toggle */}
90
+ <div className="flex items-center bg-border/50 rounded-lg p-0.5">
91
+ <button
92
+ type="button"
93
+ onClick={() => setDomainType("free")}
94
+ className={`flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-all ${
95
+ domainType === "free"
96
+ ? "bg-card text-foreground shadow-sm border border-border/50"
97
+ : "text-muted-foreground hover:text-foreground border border-transparent"
98
+ }`}
99
+ >
100
+ Free Subdomain
101
+ </button>
102
+ <button
103
+ type="button"
104
+ onClick={() => setDomainType("custom")}
105
+ className={`flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-all ${
106
+ domainType === "custom"
107
+ ? "bg-card text-foreground shadow-sm border border-border/50"
108
+ : "text-muted-foreground hover:text-foreground border border-transparent"
109
+ }`}
110
+ >
111
+ Custom Domain
112
+ </button>
113
+ </div>
114
+
115
+ {/* Input */}
116
+ {domainType === "free" ? (
117
+ <div>
118
+ <div className="flex items-center bg-muted/30 border border-border rounded-lg overflow-hidden focus-within:ring-2 focus-within:ring-primary/20 transition-all">
119
+ <input
120
+ type="text"
121
+ value={domain}
122
+ onChange={(e) => setDomain(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ""))}
123
+ placeholder={projectName || "my-project"}
124
+ className="flex-1 px-3 py-2 bg-transparent text-sm text-foreground placeholder:text-muted-foreground/50 focus:outline-none"
125
+ />
126
+ <div className="px-3 py-2 bg-muted/60 border-l border-border">
127
+ <span className="text-sm font-semibold text-foreground">.opsh.io</span>
128
+ </div>
129
+ </div>
130
+ <p className="text-[11px] text-muted-foreground mt-1.5 flex items-center gap-1">
131
+ <Shield className="size-3 text-emerald-500 shrink-0" />
132
+ SSL included &mdash; live at{" "}
133
+ <span className="font-medium text-foreground ml-0.5">
134
+ {domain || projectName || "my-project"}.opsh.io
135
+ </span>
136
+ </p>
137
+ </div>
138
+ ) : (
139
+ <div className="space-y-2">
140
+ <input
141
+ type="text"
142
+ value={customDomain}
143
+ onChange={(e) => setCustomDomain(e.target.value.toLowerCase())}
144
+ placeholder="example.com"
145
+ className="w-full px-3 py-2 bg-muted/30 border border-border rounded-lg text-sm text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all"
146
+ />
147
+
148
+ {/* Inline DNS hint */}
149
+ {customDomain && (
150
+ <div className="rounded-lg border border-border/50 bg-muted/20 overflow-hidden">
151
+ <div className="flex items-center gap-2 px-3 py-2">
152
+ <Server className="size-3 text-muted-foreground shrink-0" />
153
+ {loading ? (
154
+ <p className="text-[11px] text-muted-foreground flex-1">Fetching DNS records...</p>
155
+ ) : hasRecords ? (
156
+ <p className="text-[11px] text-muted-foreground flex-1">
157
+ Add a <span className="font-medium text-foreground">{routeRecord?.type}</span> and{" "}
158
+ <span className="font-medium text-foreground">TXT</span> record at your DNS provider
159
+ </p>
160
+ ) : (
161
+ <p className="text-[11px] text-muted-foreground flex-1">
162
+ Enter a valid domain to see required DNS records
163
+ </p>
164
+ )}
165
+ {hasRecords && (
166
+ <button
167
+ type="button"
168
+ onClick={() => setShowDnsModal(true)}
169
+ className="text-[11px] text-primary hover:text-primary/80 font-medium shrink-0 transition-colors"
170
+ >
171
+ View records
172
+ </button>
173
+ )}
174
+ </div>
175
+ </div>
176
+ )}
177
+ </div>
178
+ )}
179
+ </div>
180
+
181
+ {/* DNS Modal */}
182
+ {showDnsModal && hasRecords && (
183
+ <div
184
+ className="fixed inset-0 bg-background/80 backdrop-blur-sm flex items-center justify-center z-50 p-4"
185
+ onClick={() => setShowDnsModal(false)}
186
+ >
187
+ <div className="max-w-xl w-full" onClick={(e) => e.stopPropagation()}>
188
+ <div className="relative bg-card rounded-xl border border-border/50 shadow-lg overflow-hidden animate-in fade-in zoom-in-95 duration-200">
189
+ <button
190
+ onClick={() => setShowDnsModal(false)}
191
+ className="absolute top-3 right-3 w-8 h-8 bg-muted/50 rounded-lg flex items-center justify-center hover:bg-muted transition-colors z-10"
192
+ >
193
+ <X className="size-4 text-muted-foreground" />
194
+ </button>
195
+
196
+ {/* Header */}
197
+ <div className="flex items-center gap-3 px-5 py-4 border-b border-border/40">
198
+ <div className="w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center">
199
+ <Server className="size-4 text-primary" />
200
+ </div>
201
+ <div>
202
+ <h3 className="text-sm font-semibold text-foreground">DNS Configuration</h3>
203
+ <p className="text-xs text-muted-foreground">
204
+ Add these records for{" "}
205
+ <span className="font-medium text-foreground">{customDomain}</span>
206
+ </p>
207
+ </div>
208
+ </div>
209
+
210
+ {/* Records */}
211
+ <div className="p-5 space-y-3">
212
+ {dnsRecords.map((record, i) => (
213
+ <div key={i} className="bg-muted/30 rounded-xl border border-border/50 p-4">
214
+ <div className="flex items-center gap-3 mb-3">
215
+ <span className="px-2.5 py-1 bg-foreground text-background text-xs font-bold rounded-lg">
216
+ {record.type}
217
+ </span>
218
+ <span className="text-xs text-muted-foreground">
219
+ {RECORD_LABELS[record.type] ?? ""}
220
+ </span>
221
+ </div>
222
+ <div className="grid grid-cols-2 gap-3">
223
+ <div>
224
+ <p className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1.5">
225
+ Name / Host
226
+ </p>
227
+ <div className="flex items-center gap-2 bg-background rounded-lg border border-border/50 px-3 py-2">
228
+ <code className="flex-1 text-sm font-medium text-foreground">{record.host}</code>
229
+ <button
230
+ onClick={() => copy(record.host, `${i}-h`)}
231
+ className="p-1 hover:bg-muted rounded-md transition-colors shrink-0"
232
+ >
233
+ {copied === `${i}-h` ? (
234
+ <Check className="size-3.5 text-emerald-500" />
235
+ ) : (
236
+ <Copy className="size-3.5 text-muted-foreground" />
237
+ )}
238
+ </button>
239
+ </div>
240
+ </div>
241
+ <div>
242
+ <p className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1.5">
243
+ Value / Target
244
+ </p>
245
+ <div className="flex items-center gap-2 bg-background rounded-lg border border-border/50 px-3 py-2">
246
+ <code className="flex-1 text-sm font-medium text-foreground truncate">
247
+ {record.value}
248
+ </code>
249
+ <button
250
+ onClick={() => copy(record.value, `${i}-v`)}
251
+ className="p-1 hover:bg-muted rounded-md transition-colors shrink-0"
252
+ >
253
+ {copied === `${i}-v` ? (
254
+ <Check className="size-3.5 text-emerald-500" />
255
+ ) : (
256
+ <Copy className="size-3.5 text-muted-foreground" />
257
+ )}
258
+ </button>
259
+ </div>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ ))}
264
+
265
+ <div className="flex items-start gap-2 p-3 bg-primary/5 rounded-xl border border-primary/10">
266
+ <Info className="size-3.5 text-primary shrink-0 mt-0.5" />
267
+ <p className="text-xs text-muted-foreground leading-relaxed">
268
+ {dnsMode === "selfhosted" ? (
269
+ <>
270
+ Add the <span className="font-medium text-foreground">A record</span> pointing to your server IP,
271
+ then the <span className="font-medium text-foreground">TXT record</span> for verification.
272
+ </>
273
+ ) : (
274
+ <>
275
+ Add the <span className="font-medium text-foreground">CNAME record</span> for routing,
276
+ then the <span className="font-medium text-foreground">TXT record</span> for verification.
277
+ </>
278
+ )}{" "}
279
+ DNS changes can take up to 48 hours.
280
+ </p>
281
+ </div>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ )}
287
+ </div>
288
+ );
289
+ };
290
+
291
+ export default DomainSettings;
@@ -0,0 +1,71 @@
1
+ import React from "react";
2
+ import { ArrowLeft, GitBranch, Globe, Lock } from "lucide-react";
3
+ import { HeaderProps } from "@/components/import-project/types";
4
+ import Link from "next/link";
5
+ import { generateIcon } from "@/utils/icons";
6
+
7
+ const Header: React.FC<HeaderProps> = ({ repoData }) => {
8
+ return (
9
+ <div className="mb-6 relative mt-10">
10
+ <div className="flex items-center justify-between">
11
+ <div className="flex items-center gap-4">
12
+ {/* <Link
13
+ href="/deployments"
14
+ className="inline-flex items-center justify-center w-10 h-10 rounded-xl hover:bg-gray-100 text-gray-600 hover:text-black transition-all"
15
+ >
16
+ <ArrowLeft className="w-5 h-5" />
17
+ </Link>
18
+ */}
19
+ <div>
20
+ <h1
21
+ className="font-bold text-black mb-2"
22
+ style={{ fontSize: '2.4rem', letterSpacing: '-0.5px' }}
23
+ >
24
+ {repoData.owner}/{repoData.repo}
25
+ </h1>
26
+ <div className="flex items-center gap-2 text-sm">
27
+ {repoData.private ? (
28
+ <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-amber-500/10 text-amber-700 ring-1 ring-amber-500/20 font-semibold text-xs">
29
+ <Lock className="w-3 h-3" />
30
+ Private
31
+ </span>
32
+ ) : (
33
+ <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-emerald-500/10 text-emerald-700 ring-1 ring-emerald-500/20 font-semibold text-xs">
34
+ <Globe className="w-3 h-3" />
35
+ Public
36
+ </span>
37
+ )}
38
+ <span className="text-gray-300">•</span>
39
+ <span className="inline-flex items-center gap-1 text-gray-500">
40
+ <GitBranch className="w-3 h-3" />
41
+ <span className="text-xs font-medium">{repoData.branch}</span>
42
+ </span>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <button
48
+ className="flex items-center gap-2 px-4 py-2 bg-gray-100 text-gray-600 hover:text-black text-sm font-medium rounded-[15px] transition-all"
49
+ type="button"
50
+ >
51
+ {generateIcon('help%20sign-50-1658435663.png', 16, 'currentColor')}
52
+ <span>Help</span>
53
+ </button>
54
+ </div>
55
+
56
+ {/* Gradient underline */}
57
+ {/* <div
58
+ className="absolute left-14"
59
+ style={{
60
+ bottom: '-1.5rem',
61
+ width: '100px',
62
+ height: '4px',
63
+ background: 'linear-gradient(90deg, #36b37e, #00c6ff)',
64
+ borderRadius: '30px',
65
+ }}
66
+ ></div> */}
67
+ </div>
68
+ );
69
+ };
70
+
71
+ export default React.memo(Header);
@@ -0,0 +1,270 @@
1
+ import React, { useCallback } from "react";
2
+ import { GitBranch, Rocket, Github, Loader2, Globe, Database, Container, Server, Layers, Check, AlertCircle, Key, Cloud, Monitor } from "lucide-react";
3
+ import { CustomSelect } from "@/components/ui/CustomSelect";
4
+ import DomainSettings from "./DomainSettings";
5
+ import BuildSummary from "./BuildSummary";
6
+ import { useDeployment } from "@/context/DeploymentContext";
7
+ import { useRouter } from "next/navigation";
8
+ import type { BuildStrategy } from "@/context/deployment/types";
9
+
10
+ // ─── Deploy checklist for compose ────────────────────────────────────────────
11
+
12
+ const ComposeChecklist: React.FC = () => {
13
+ const { config } = useDeployment();
14
+ const services = config.services || [];
15
+ if (services.length === 0) return null;
16
+
17
+ const isDb = (svc: { image?: string; name: string }) =>
18
+ /postgres|mysql|mariadb|mongo|redis|memcached|cassandra|clickhouse|influx|minio/i.test(
19
+ svc.image || svc.name,
20
+ );
21
+
22
+ const exposedServices = services.filter((s) => s.exposed);
23
+ const exposableServices = services.filter(
24
+ (s) => s.ports.length > 0 && !isDb(s),
25
+ );
26
+ const envConfigured = services.filter(
27
+ (s) => Object.keys(s.environment).length > 0,
28
+ ).length;
29
+ const totalEnvVars = services.reduce(
30
+ (acc, s) => acc + Object.keys(s.environment).length,
31
+ 0,
32
+ );
33
+ const dbServices = services.filter((s) => isDb(s));
34
+ const buildServices = services.filter((s) => s.build);
35
+
36
+ const checks = [
37
+ {
38
+ label: "Services detected",
39
+ value: `${services.length} services`,
40
+ ok: services.length > 0,
41
+ icon: Layers,
42
+ },
43
+ {
44
+ label: "Public domains",
45
+ value: exposedServices.length > 0
46
+ ? `${exposedServices.length} of ${exposableServices.length} exposed`
47
+ : `${exposableServices.length} can be exposed`,
48
+ ok: exposedServices.length > 0,
49
+ warn: exposedServices.length === 0 && exposableServices.length > 0,
50
+ icon: Globe,
51
+ },
52
+ ...(buildServices.length > 0
53
+ ? [{
54
+ label: "Build services",
55
+ value: `${buildServices.length} to build`,
56
+ ok: true,
57
+ icon: Container,
58
+ }]
59
+ : []),
60
+ ...(dbServices.length > 0
61
+ ? [{
62
+ label: "Databases",
63
+ value: `${dbServices.length} ${dbServices.length === 1 ? "instance" : "instances"}`,
64
+ ok: true,
65
+ icon: Database,
66
+ }]
67
+ : []),
68
+ {
69
+ label: "Environment",
70
+ value: totalEnvVars > 0
71
+ ? `${totalEnvVars} vars across ${envConfigured} services`
72
+ : "No env vars set",
73
+ ok: totalEnvVars > 0,
74
+ icon: Key,
75
+ },
76
+ ];
77
+
78
+ return (
79
+ <div className="bg-card rounded-xl border border-border/50 p-4 space-y-3">
80
+ <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
81
+ Deploy Checklist
82
+ </p>
83
+ <div className="space-y-2">
84
+ {checks.map((check) => {
85
+ const Icon = check.icon;
86
+ return (
87
+ <div key={check.label} className="flex items-start gap-2.5">
88
+ <div className={`mt-0.5 p-1 rounded-md ${
89
+ check.ok
90
+ ? "bg-emerald-500/10 text-emerald-500"
91
+ : (check as any).warn
92
+ ? "bg-amber-500/10 text-amber-500"
93
+ : "bg-muted/50 text-muted-foreground/50"
94
+ }`}>
95
+ {check.ok ? (
96
+ <Check className="size-3" />
97
+ ) : (check as any).warn ? (
98
+ <AlertCircle className="size-3" />
99
+ ) : (
100
+ <Icon className="size-3" />
101
+ )}
102
+ </div>
103
+ <div className="flex-1 min-w-0">
104
+ <p className="text-sm font-medium text-foreground leading-tight">
105
+ {check.label}
106
+ </p>
107
+ <p className="text-xs text-muted-foreground leading-snug">
108
+ {check.value}
109
+ </p>
110
+ </div>
111
+ </div>
112
+ );
113
+ })}
114
+ </div>
115
+
116
+ {/* Exposed domains quick list */}
117
+ {exposedServices.length > 0 && (
118
+ <div className="pt-2 border-t border-border/30 space-y-1.5">
119
+ <p className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wider">
120
+ Domains
121
+ </p>
122
+ {exposedServices.map((svc) => {
123
+ const domain =
124
+ svc.domainType === "custom" && svc.customDomain
125
+ ? svc.customDomain
126
+ : `${svc.domain || svc.name}.opsh.io`;
127
+ return (
128
+ <div key={svc.name} className="flex items-center gap-2">
129
+ <Globe className="size-3 text-primary" />
130
+ <span className="text-sm text-primary font-medium truncate">{domain}</span>
131
+ <span className="text-xs text-muted-foreground ml-auto">{svc.name}</span>
132
+ </div>
133
+ );
134
+ })}
135
+ </div>
136
+ )}
137
+ </div>
138
+ );
139
+ };
140
+
141
+ // ─── Sidebar ─────────────────────────────────────────────────────────────────
142
+
143
+ const Sidebar: React.FC = () => {
144
+ const { config, state, updateConfig, startDeployment } = useDeployment();
145
+ const router = useRouter();
146
+ const isServices = config.projectType === "services";
147
+
148
+ const handleDeploy = useCallback(async () => {
149
+ const deploymentId = await startDeployment();
150
+ if (deploymentId) {
151
+ router.push(`/build/${deploymentId}`);
152
+ }
153
+ }, [startDeployment, router]);
154
+
155
+ return (
156
+ <div className="lg:sticky lg:top-6 h-fit space-y-4">
157
+ {/* Repository Info */}
158
+ <div className="border border-border/50 rounded-xl bg-card overflow-hidden">
159
+ <div className="flex items-center gap-1.5 px-4 pt-3 pb-0">
160
+ <span className="w-2.5 h-2.5 rounded-full bg-[#ef4444]/60" />
161
+ <span className="w-2.5 h-2.5 rounded-full bg-[#eab308]/60" />
162
+ <span className="w-2.5 h-2.5 rounded-full bg-[#22c55e]/60" />
163
+ </div>
164
+ <div className="p-4 pt-3">
165
+ <div className="flex items-center gap-3">
166
+ <Github className="size-4 text-muted-foreground shrink-0" />
167
+ <div className="flex-1 min-w-0">
168
+ <p className="text-sm font-medium text-foreground truncate">
169
+ {config.owner}/{config.repo}
170
+ </p>
171
+ </div>
172
+ </div>
173
+ {config.branches.length > 0 && (
174
+ <div className="mt-3">
175
+ <CustomSelect
176
+ value={config.branch}
177
+ onChange={(val) => updateConfig({ branch: val })}
178
+ options={config.branches.map(branch => ({
179
+ value: branch,
180
+ label: branch,
181
+ icon: <GitBranch className="w-3.5 h-3.5" />
182
+ }))}
183
+ placeholder="Select branch"
184
+ className="w-full"
185
+ />
186
+ </div>
187
+ )}
188
+ {config.branches.length === 0 && config.branch && (
189
+ <div className="flex items-center gap-1 mt-2 text-xs text-muted-foreground">
190
+ <GitBranch className="size-3" />
191
+ {config.branch}
192
+ </div>
193
+ )}
194
+ </div>
195
+ </div>
196
+
197
+ {/* Build Location — prominent toggle for where the build runs */}
198
+ {config.projectType === "app" && config.options.hasBuild && (
199
+ <div className="border border-border/50 rounded-xl bg-card p-4 space-y-2.5">
200
+ <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
201
+ Build Location
202
+ </p>
203
+ <div className="flex gap-1.5 p-1 bg-muted/30 rounded-lg border border-border/50">
204
+ {([
205
+ { value: "server" as BuildStrategy, label: "Server", desc: "Build in cloud workspace", icon: <Cloud className="w-4 h-4" /> },
206
+ { value: "local" as BuildStrategy, label: "Local", desc: "Build on this machine", icon: <Monitor className="w-4 h-4" /> },
207
+ ]).map((opt) => (
208
+ <button
209
+ key={opt.value}
210
+ onClick={() => updateConfig({ buildStrategy: opt.value })}
211
+ className={`flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-all ${
212
+ config.buildStrategy === opt.value
213
+ ? "bg-background text-foreground shadow-sm border border-border/50"
214
+ : "text-muted-foreground hover:text-foreground"
215
+ }`}
216
+ >
217
+ {opt.icon}
218
+ {opt.label}
219
+ </button>
220
+ ))}
221
+ </div>
222
+ <p className="text-xs text-muted-foreground">
223
+ {config.buildStrategy === "local"
224
+ ? "Build runs on your machine, then artifacts are uploaded"
225
+ : "Source is sent to the server for building"}
226
+ </p>
227
+ </div>
228
+ )}
229
+
230
+ {/* Domain — per-service for compose, checklist for others */}
231
+ {isServices ? (
232
+ <ComposeChecklist />
233
+ ) : (
234
+ <DomainSettings
235
+ projectName={config.projectName}
236
+ domain={config.domain}
237
+ setDomain={(val) => updateConfig({ domain: val })}
238
+ customDomain={config.customDomain}
239
+ setCustomDomain={(val) => updateConfig({ customDomain: val })}
240
+ domainType={config.domainType}
241
+ setDomainType={(val) => updateConfig({ domainType: val })}
242
+ />
243
+ )}
244
+
245
+ {/* Deploy */}
246
+ <button
247
+ onClick={handleDeploy}
248
+ disabled={state.isDeploying}
249
+ className="w-full inline-flex items-center justify-center gap-2 px-5 py-3 bg-primary text-primary-foreground text-sm font-medium rounded-xl hover:bg-primary/90 transition-all hover:shadow-lg hover:shadow-primary/25 hover:-translate-y-0.5 disabled:opacity-50 disabled:cursor-not-allowed"
250
+ >
251
+ {state.isDeploying ? (
252
+ <>
253
+ <Loader2 className="size-4 animate-spin" />
254
+ Deploying…
255
+ </>
256
+ ) : (
257
+ <>
258
+ <Rocket className="size-4" />
259
+ Deploy
260
+ </>
261
+ )}
262
+ </button>
263
+
264
+ {/* Build Summary */}
265
+ <BuildSummary />
266
+ </div>
267
+ );
268
+ };
269
+
270
+ export default React.memo(Sidebar);