openship 0.1.0

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 (111) hide show
  1. package/.env.example +22 -0
  2. package/.nvmrc +1 -0
  3. package/.prettierrc +7 -0
  4. package/CONTRIBUTING.md +40 -0
  5. package/README.md +164 -0
  6. package/apps/api/.env.example +30 -0
  7. package/apps/api/Dockerfile +24 -0
  8. package/apps/api/package.json +37 -0
  9. package/apps/api/src/app.ts +26 -0
  10. package/apps/api/src/config/env.ts +39 -0
  11. package/apps/api/src/config/index.ts +1 -0
  12. package/apps/api/src/index.ts +8 -0
  13. package/apps/api/src/middleware/auth.ts +24 -0
  14. package/apps/api/src/middleware/error-handler.ts +21 -0
  15. package/apps/api/src/middleware/index.ts +3 -0
  16. package/apps/api/src/middleware/rate-limiter.ts +26 -0
  17. package/apps/api/src/modules/auth/auth.controller.ts +26 -0
  18. package/apps/api/src/modules/auth/auth.routes.ts +10 -0
  19. package/apps/api/src/modules/auth/auth.schema.ts +12 -0
  20. package/apps/api/src/modules/auth/auth.service.ts +23 -0
  21. package/apps/api/src/modules/billing/billing.controller.ts +60 -0
  22. package/apps/api/src/modules/billing/billing.routes.ts +26 -0
  23. package/apps/api/src/modules/billing/billing.schema.ts +9 -0
  24. package/apps/api/src/modules/billing/billing.service.ts +33 -0
  25. package/apps/api/src/modules/deployments/deployment.controller.ts +35 -0
  26. package/apps/api/src/modules/deployments/deployment.routes.ts +11 -0
  27. package/apps/api/src/modules/deployments/deployment.schema.ts +7 -0
  28. package/apps/api/src/modules/deployments/deployment.service.ts +23 -0
  29. package/apps/api/src/modules/domains/domain.controller.ts +21 -0
  30. package/apps/api/src/modules/domains/domain.routes.ts +9 -0
  31. package/apps/api/src/modules/domains/domain.service.ts +19 -0
  32. package/apps/api/src/modules/health/health.routes.ts +10 -0
  33. package/apps/api/src/modules/projects/project.controller.ts +29 -0
  34. package/apps/api/src/modules/projects/project.routes.ts +10 -0
  35. package/apps/api/src/modules/projects/project.schema.ts +9 -0
  36. package/apps/api/src/modules/projects/project.service.ts +23 -0
  37. package/apps/api/src/modules/webhooks/webhook.controller.ts +22 -0
  38. package/apps/api/src/modules/webhooks/webhook.routes.ts +12 -0
  39. package/apps/api/src/modules/webhooks/webhook.service.ts +11 -0
  40. package/apps/api/tsconfig.json +12 -0
  41. package/apps/cli/package.json +28 -0
  42. package/apps/cli/src/commands/deploy.ts +11 -0
  43. package/apps/cli/src/commands/init.ts +8 -0
  44. package/apps/cli/src/commands/login.ts +8 -0
  45. package/apps/cli/src/commands/logs.ts +9 -0
  46. package/apps/cli/src/index.ts +21 -0
  47. package/apps/cli/src/lib/api-client.ts +35 -0
  48. package/apps/cli/tsconfig.json +12 -0
  49. package/apps/dashboard/Dockerfile +24 -0
  50. package/apps/dashboard/next.config.mjs +6 -0
  51. package/apps/dashboard/package.json +28 -0
  52. package/apps/dashboard/src/app/(auth)/layout.tsx +6 -0
  53. package/apps/dashboard/src/app/(auth)/login/page.tsx +11 -0
  54. package/apps/dashboard/src/app/(auth)/register/page.tsx +10 -0
  55. package/apps/dashboard/src/app/(dashboard)/billing/page.tsx +10 -0
  56. package/apps/dashboard/src/app/(dashboard)/deployments/page.tsx +8 -0
  57. package/apps/dashboard/src/app/(dashboard)/domains/page.tsx +8 -0
  58. package/apps/dashboard/src/app/(dashboard)/layout.tsx +37 -0
  59. package/apps/dashboard/src/app/(dashboard)/monitoring/page.tsx +8 -0
  60. package/apps/dashboard/src/app/(dashboard)/projects/page.tsx +8 -0
  61. package/apps/dashboard/src/app/(dashboard)/settings/page.tsx +8 -0
  62. package/apps/dashboard/src/app/globals.css +3 -0
  63. package/apps/dashboard/src/app/layout.tsx +15 -0
  64. package/apps/dashboard/src/app/page.tsx +6 -0
  65. package/apps/dashboard/tsconfig.json +11 -0
  66. package/apps/web/Dockerfile +24 -0
  67. package/apps/web/next.config.mjs +6 -0
  68. package/apps/web/package.json +26 -0
  69. package/apps/web/src/app/(marketing)/docs/page.tsx +10 -0
  70. package/apps/web/src/app/(marketing)/layout.tsx +24 -0
  71. package/apps/web/src/app/(marketing)/page.tsx +2 -0
  72. package/apps/web/src/app/(marketing)/pricing/page.tsx +10 -0
  73. package/apps/web/src/app/globals.css +3 -0
  74. package/apps/web/src/app/layout.tsx +16 -0
  75. package/apps/web/src/app/page.tsx +10 -0
  76. package/apps/web/tsconfig.json +11 -0
  77. package/docker-compose.yml +87 -0
  78. package/package.json +31 -0
  79. package/packages/adapters/package.json +27 -0
  80. package/packages/adapters/src/base-adapter.ts +49 -0
  81. package/packages/adapters/src/docker-adapter.ts +43 -0
  82. package/packages/adapters/src/index.ts +4 -0
  83. package/packages/adapters/src/oblien-adapter.ts +46 -0
  84. package/packages/adapters/src/registry.ts +22 -0
  85. package/packages/adapters/tsconfig.json +9 -0
  86. package/packages/core/package.json +27 -0
  87. package/packages/core/src/constants.ts +28 -0
  88. package/packages/core/src/errors.ts +49 -0
  89. package/packages/core/src/index.ts +4 -0
  90. package/packages/core/src/types.ts +47 -0
  91. package/packages/core/src/utils.ts +35 -0
  92. package/packages/core/tsconfig.json +9 -0
  93. package/packages/db/package.json +32 -0
  94. package/packages/db/prisma/schema.prisma +160 -0
  95. package/packages/db/src/index.ts +11 -0
  96. package/packages/db/tsconfig.json +9 -0
  97. package/packages/ui/package.json +38 -0
  98. package/packages/ui/src/components/badge.tsx +29 -0
  99. package/packages/ui/src/components/button.tsx +44 -0
  100. package/packages/ui/src/components/card.tsx +26 -0
  101. package/packages/ui/src/components/status-dot.tsx +25 -0
  102. package/packages/ui/src/globals.css +18 -0
  103. package/packages/ui/src/index.tsx +8 -0
  104. package/packages/ui/src/lib/cn.ts +7 -0
  105. package/packages/ui/tsconfig.json +9 -0
  106. package/pnpm-workspace.yaml +4 -0
  107. package/tooling/tsconfig/base.json +19 -0
  108. package/tooling/tsconfig/nextjs.json +10 -0
  109. package/tooling/tsconfig/node.json +10 -0
  110. package/tooling/tsconfig/package.json +7 -0
  111. package/turbo.json +32 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Billing service — Stripe integration for cloud pricing.
3
+ *
4
+ * Self-hosted instances skip billing entirely (gated by CLOUD_MODE env var).
5
+ */
6
+
7
+ export async function createCheckoutSession(userId: string, planId: string) {
8
+ // TODO: Create Stripe Checkout session
9
+ }
10
+
11
+ export async function createCustomer(userId: string, email: string) {
12
+ // TODO: Create Stripe customer
13
+ }
14
+
15
+ export async function getSubscription(userId: string) {
16
+ // TODO: Fetch active subscription from DB
17
+ }
18
+
19
+ export async function cancelSubscription(subscriptionId: string) {
20
+ // TODO: Cancel Stripe subscription at period end
21
+ }
22
+
23
+ export async function recordUsage(userId: string, metric: string, quantity: number) {
24
+ // TODO: Record metered usage for billing
25
+ }
26
+
27
+ export async function getUsageSummary(userId: string) {
28
+ // TODO: Aggregate usage for current billing period
29
+ }
30
+
31
+ export async function handleStripeEvent(event: unknown) {
32
+ // TODO: Handle subscription.created, invoice.paid, etc.
33
+ }
@@ -0,0 +1,35 @@
1
+ import type { Context } from "hono";
2
+
3
+ export async function list(c: Context) {
4
+ // TODO: List deployments (optionally filtered by projectId)
5
+ return c.json({ data: [] });
6
+ }
7
+
8
+ export async function create(c: Context) {
9
+ // TODO: Trigger new deployment via adapter
10
+ return c.json({ message: "deployment queued" }, 202);
11
+ }
12
+
13
+ export async function getById(c: Context) {
14
+ const id = c.req.param("id");
15
+ // TODO: Get deployment details + status
16
+ return c.json({ data: { id } });
17
+ }
18
+
19
+ export async function logs(c: Context) {
20
+ const id = c.req.param("id");
21
+ // TODO: Stream or return deployment logs
22
+ return c.json({ data: { id, logs: [] } });
23
+ }
24
+
25
+ export async function rollback(c: Context) {
26
+ const id = c.req.param("id");
27
+ // TODO: Rollback to this deployment
28
+ return c.json({ message: "rollback initiated" });
29
+ }
30
+
31
+ export async function cancel(c: Context) {
32
+ const id = c.req.param("id");
33
+ // TODO: Cancel in-progress deployment
34
+ return c.json({ message: "deployment cancelled" });
35
+ }
@@ -0,0 +1,11 @@
1
+ import { Hono } from "hono";
2
+ import * as deploymentController from "./deployment.controller";
3
+
4
+ export const deploymentRoutes = new Hono();
5
+
6
+ deploymentRoutes.get("/", deploymentController.list);
7
+ deploymentRoutes.post("/", deploymentController.create);
8
+ deploymentRoutes.get("/:id", deploymentController.getById);
9
+ deploymentRoutes.get("/:id/logs", deploymentController.logs);
10
+ deploymentRoutes.post("/:id/rollback", deploymentController.rollback);
11
+ deploymentRoutes.post("/:id/cancel", deploymentController.cancel);
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+
3
+ export const createDeploymentSchema = z.object({
4
+ projectId: z.string().uuid(),
5
+ branch: z.string().default("main"),
6
+ environment: z.enum(["production", "preview"]).default("production"),
7
+ });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Deployment service — orchestrates builds and deployments via adapters.
3
+ */
4
+
5
+ export async function queueDeployment(projectId: string, opts: { branch?: string }) {
6
+ // TODO: Create deployment record, push to BullMQ queue
7
+ }
8
+
9
+ export async function processDeployment(deploymentId: string) {
10
+ // TODO: Pull adapter (Docker / Oblien), build, deploy
11
+ }
12
+
13
+ export async function getDeploymentLogs(deploymentId: string) {
14
+ // TODO: Fetch logs from adapter or storage
15
+ }
16
+
17
+ export async function rollbackDeployment(deploymentId: string) {
18
+ // TODO: Re-activate a previous deployment
19
+ }
20
+
21
+ export async function cancelDeployment(deploymentId: string) {
22
+ // TODO: Signal adapter to stop build
23
+ }
@@ -0,0 +1,21 @@
1
+ import type { Context } from "hono";
2
+
3
+ export async function list(c: Context) {
4
+ return c.json({ data: [] });
5
+ }
6
+
7
+ export async function add(c: Context) {
8
+ // TODO: Add custom domain, generate DNS verification record
9
+ return c.json({ message: "domain added" }, 201);
10
+ }
11
+
12
+ export async function remove(c: Context) {
13
+ const id = c.req.param("id");
14
+ return c.json({ message: "domain removed" });
15
+ }
16
+
17
+ export async function verify(c: Context) {
18
+ const id = c.req.param("id");
19
+ // TODO: Check DNS records to verify ownership
20
+ return c.json({ message: "verification started" });
21
+ }
@@ -0,0 +1,9 @@
1
+ import { Hono } from "hono";
2
+ import * as domainController from "./domain.controller";
3
+
4
+ export const domainRoutes = new Hono();
5
+
6
+ domainRoutes.get("/", domainController.list);
7
+ domainRoutes.post("/", domainController.add);
8
+ domainRoutes.delete("/:id", domainController.remove);
9
+ domainRoutes.post("/:id/verify", domainController.verify);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Domain service — manage custom domains and SSL certificates.
3
+ */
4
+
5
+ export async function addDomain(projectId: string, domain: string) {
6
+ // TODO: Store domain, create TXT verification record
7
+ }
8
+
9
+ export async function verifyDomain(domainId: string) {
10
+ // TODO: DNS lookup to check verification
11
+ }
12
+
13
+ export async function removeDomain(domainId: string) {
14
+ // TODO: Remove domain and revoke certificate
15
+ }
16
+
17
+ export async function provisionSSL(domain: string) {
18
+ // TODO: Issue Let's Encrypt certificate
19
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Health check module — used by load balancers and Docker health checks.
3
+ */
4
+ import { Hono } from "hono";
5
+
6
+ export const healthRoutes = new Hono();
7
+
8
+ healthRoutes.get("/", (c) => {
9
+ return c.json({ status: "ok", timestamp: new Date().toISOString() });
10
+ });
@@ -0,0 +1,29 @@
1
+ import type { Context } from "hono";
2
+
3
+ export async function list(c: Context) {
4
+ // TODO: List all projects for authenticated user
5
+ return c.json({ data: [] });
6
+ }
7
+
8
+ export async function create(c: Context) {
9
+ // TODO: Create new project
10
+ return c.json({ message: "created" }, 201);
11
+ }
12
+
13
+ export async function getById(c: Context) {
14
+ const id = c.req.param("id");
15
+ // TODO: Fetch project by ID
16
+ return c.json({ data: { id } });
17
+ }
18
+
19
+ export async function update(c: Context) {
20
+ const id = c.req.param("id");
21
+ // TODO: Update project
22
+ return c.json({ data: { id } });
23
+ }
24
+
25
+ export async function remove(c: Context) {
26
+ const id = c.req.param("id");
27
+ // TODO: Soft-delete project
28
+ return c.json({ message: "deleted" });
29
+ }
@@ -0,0 +1,10 @@
1
+ import { Hono } from "hono";
2
+ import * as projectController from "./project.controller";
3
+
4
+ export const projectRoutes = new Hono();
5
+
6
+ projectRoutes.get("/", projectController.list);
7
+ projectRoutes.post("/", projectController.create);
8
+ projectRoutes.get("/:id", projectController.getById);
9
+ projectRoutes.patch("/:id", projectController.update);
10
+ projectRoutes.delete("/:id", projectController.remove);
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+
3
+ export const createProjectSchema = z.object({
4
+ name: z.string().min(1).max(100),
5
+ repo: z.string().url().optional(),
6
+ framework: z.enum(["nextjs", "node", "static", "docker"]).optional(),
7
+ });
8
+
9
+ export const updateProjectSchema = createProjectSchema.partial();
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Project service — CRUD operations for user projects.
3
+ */
4
+
5
+ export async function listProjects(userId: string) {
6
+ // TODO: Query DB for user's projects
7
+ }
8
+
9
+ export async function createProject(userId: string, data: { name: string; repo?: string }) {
10
+ // TODO: Insert project, set up initial config
11
+ }
12
+
13
+ export async function getProject(projectId: string) {
14
+ // TODO: Fetch single project
15
+ }
16
+
17
+ export async function updateProject(projectId: string, data: Partial<{ name: string }>) {
18
+ // TODO: Update project fields
19
+ }
20
+
21
+ export async function deleteProject(projectId: string) {
22
+ // TODO: Soft-delete project and associated deployments
23
+ }
@@ -0,0 +1,22 @@
1
+ import type { Context } from "hono";
2
+
3
+ export async function github(c: Context) {
4
+ // TODO: Verify GitHub signature, parse push event, trigger deployment
5
+ return c.json({ received: true });
6
+ }
7
+
8
+ export async function gitlab(c: Context) {
9
+ // TODO: Verify GitLab token, parse push event
10
+ return c.json({ received: true });
11
+ }
12
+
13
+ export async function bitbucket(c: Context) {
14
+ // TODO: Verify Bitbucket signature
15
+ return c.json({ received: true });
16
+ }
17
+
18
+ export async function custom(c: Context) {
19
+ const projectId = c.req.param("projectId");
20
+ // TODO: Trigger deployment for project from custom webhook
21
+ return c.json({ received: true, projectId });
22
+ }
@@ -0,0 +1,12 @@
1
+ import { Hono } from "hono";
2
+ import * as webhookController from "./webhook.controller";
3
+
4
+ export const webhookRoutes = new Hono();
5
+
6
+ /* Git provider webhooks (push events trigger deployments) */
7
+ webhookRoutes.post("/github", webhookController.github);
8
+ webhookRoutes.post("/gitlab", webhookController.gitlab);
9
+ webhookRoutes.post("/bitbucket", webhookController.bitbucket);
10
+
11
+ /* Generic webhook for custom integrations */
12
+ webhookRoutes.post("/custom/:projectId", webhookController.custom);
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Webhook service — verifies signatures and dispatches events.
3
+ */
4
+
5
+ export async function verifyGitHubSignature(payload: string, signature: string, secret: string) {
6
+ // TODO: HMAC-SHA256 verification
7
+ }
8
+
9
+ export async function handlePushEvent(projectId: string, branch: string, commitSha: string) {
10
+ // TODO: Queue new deployment
11
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "openship-tsconfig/node.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "paths": {
6
+ "@/*": ["./src/*"]
7
+ },
8
+ "outDir": "dist"
9
+ },
10
+ "include": ["src/**/*.ts"],
11
+ "exclude": ["node_modules", "dist"]
12
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "openship-cli",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "openship": "./dist/index.js"
7
+ },
8
+ "scripts": {
9
+ "dev": "tsx src/index.ts",
10
+ "build": "tsup src/index.ts --format esm --dts --clean",
11
+ "lint": "tsc --noEmit",
12
+ "clean": "rm -rf dist .turbo node_modules"
13
+ },
14
+ "dependencies": {
15
+ "openship-core": "workspace:*",
16
+ "commander": "^12.1.0",
17
+ "chalk": "^5.3.0",
18
+ "ora": "^8.0.0",
19
+ "prompts": "^2.4.0"
20
+ },
21
+ "devDependencies": {
22
+ "openship-tsconfig": "workspace:*",
23
+ "@types/prompts": "^2.4.0",
24
+ "tsx": "^4.11.0",
25
+ "tsup": "^8.1.0",
26
+ "typescript": "^5.4.0"
27
+ }
28
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from "commander";
2
+
3
+ export const deployCommand = new Command("deploy")
4
+ .description("Deploy the current project")
5
+ .option("--prod", "Deploy to production")
6
+ .option("--preview", "Create a preview deployment")
7
+ .action(async (opts) => {
8
+ // TODO: Read openship.json, upload or push, trigger deployment via API
9
+ const env = opts.prod ? "production" : "preview";
10
+ console.log(`Deploying to ${env}...`);
11
+ });
@@ -0,0 +1,8 @@
1
+ import { Command } from "commander";
2
+
3
+ export const initCommand = new Command("init")
4
+ .description("Initialize an Openship project in the current directory")
5
+ .action(async () => {
6
+ // TODO: Create openship.json config, detect framework, link to account
7
+ console.log("Initializing Openship project...");
8
+ });
@@ -0,0 +1,8 @@
1
+ import { Command } from "commander";
2
+
3
+ export const loginCommand = new Command("login")
4
+ .description("Authenticate with your Openship account")
5
+ .action(async () => {
6
+ // TODO: Open browser for OAuth or prompt for API token
7
+ console.log("Logging in...");
8
+ });
@@ -0,0 +1,9 @@
1
+ import { Command } from "commander";
2
+
3
+ export const logsCommand = new Command("logs")
4
+ .description("Stream deployment logs")
5
+ .argument("[deploymentId]", "Deployment ID (defaults to latest)")
6
+ .action(async (deploymentId?: string) => {
7
+ // TODO: Connect to API and stream logs
8
+ console.log(`Streaming logs for ${deploymentId || "latest deployment"}...`);
9
+ });
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import { deployCommand } from "./commands/deploy";
5
+ import { loginCommand } from "./commands/login";
6
+ import { initCommand } from "./commands/init";
7
+ import { logsCommand } from "./commands/logs";
8
+
9
+ const program = new Command();
10
+
11
+ program
12
+ .name("openship")
13
+ .description("Openship CLI — deploy from your terminal")
14
+ .version("0.1.0");
15
+
16
+ program.addCommand(initCommand);
17
+ program.addCommand(loginCommand);
18
+ program.addCommand(deployCommand);
19
+ program.addCommand(logsCommand);
20
+
21
+ program.parse();
@@ -0,0 +1,35 @@
1
+ /**
2
+ * CLI HTTP client — communicates with the Openship API.
3
+ */
4
+
5
+ const DEFAULT_API_URL = "http://localhost:4000/api";
6
+
7
+ export function getApiUrl(): string {
8
+ return process.env.OPENSHIP_API_URL || DEFAULT_API_URL;
9
+ }
10
+
11
+ export async function apiRequest(path: string, options?: RequestInit) {
12
+ const url = `${getApiUrl()}${path}`;
13
+ const token = getStoredToken();
14
+
15
+ const res = await fetch(url, {
16
+ ...options,
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
20
+ ...options?.headers,
21
+ },
22
+ });
23
+
24
+ if (!res.ok) {
25
+ const body = await res.json().catch(() => ({}));
26
+ throw new Error(body.error || `API error: ${res.status}`);
27
+ }
28
+
29
+ return res.json();
30
+ }
31
+
32
+ function getStoredToken(): string | null {
33
+ // TODO: Read token from ~/.openship/config.json
34
+ return null;
35
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "openship-tsconfig/node.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "paths": {
6
+ "@/*": ["./src/*"]
7
+ },
8
+ "outDir": "dist"
9
+ },
10
+ "include": ["src/**/*.ts"],
11
+ "exclude": ["node_modules", "dist"]
12
+ }
@@ -0,0 +1,24 @@
1
+ # ─── Build stage ────────────────────────────────────────
2
+ FROM node:20-alpine AS builder
3
+ RUN corepack enable && corepack prepare pnpm@9.0.0 --activate
4
+ WORKDIR /app
5
+
6
+ COPY pnpm-workspace.yaml pnpm-lock.yaml* package.json ./
7
+ COPY tooling/ ./tooling/
8
+ COPY packages/ ./packages/
9
+ COPY apps/dashboard/ ./apps/dashboard/
10
+
11
+ RUN pnpm install --frozen-lockfile
12
+ RUN pnpm run build --filter=openship-dashboard...
13
+
14
+ # ─── Production stage ──────────────────────────────────
15
+ FROM node:20-alpine AS runner
16
+ WORKDIR /app
17
+
18
+ COPY --from=builder /app/apps/dashboard/.next/standalone ./
19
+ COPY --from=builder /app/apps/dashboard/.next/static ./.next/static
20
+ COPY --from=builder /app/apps/dashboard/public ./public
21
+
22
+ ENV NODE_ENV=production
23
+ EXPOSE 3001
24
+ CMD ["node", "server.js"]
@@ -0,0 +1,6 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ transpilePackages: ["openship-ui", "openship-core", "openship-db"],
4
+ };
5
+
6
+ export default nextConfig;
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "openship-dashboard",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev --port 3001",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint",
10
+ "clean": "rm -rf .next .turbo node_modules"
11
+ },
12
+ "dependencies": {
13
+ "openship-ui": "workspace:*",
14
+ "openship-core": "workspace:*",
15
+ "openship-db": "workspace:*",
16
+ "next": "^14.2.0",
17
+ "react": "^18.3.0",
18
+ "react-dom": "^18.3.0",
19
+ "next-auth": "^5.0.0-beta.0"
20
+ },
21
+ "devDependencies": {
22
+ "openship-tsconfig": "workspace:*",
23
+ "@types/node": "^20.0.0",
24
+ "@types/react": "^18.3.0",
25
+ "@types/react-dom": "^18.3.0",
26
+ "typescript": "^5.4.0"
27
+ }
28
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Auth layout — minimal shell without sidebar, used for login/register pages.
3
+ */
4
+ export default function AuthLayout({ children }: { children: React.ReactNode }) {
5
+ return <div className="min-h-screen bg-gray-50">{children}</div>;
6
+ }
@@ -0,0 +1,11 @@
1
+ export default function LoginPage() {
2
+ return (
3
+ <div className="flex min-h-screen items-center justify-center">
4
+ <div className="w-full max-w-sm rounded-lg border p-8">
5
+ <h1 className="mb-6 text-2xl font-bold">Sign in to Openship</h1>
6
+ {/* Auth form will go here — next-auth / credentials / OAuth */}
7
+ <p className="text-sm text-gray-500">Authentication coming soon.</p>
8
+ </div>
9
+ </div>
10
+ );
11
+ }
@@ -0,0 +1,10 @@
1
+ export default function RegisterPage() {
2
+ return (
3
+ <div className="flex min-h-screen items-center justify-center">
4
+ <div className="w-full max-w-sm rounded-lg border p-8">
5
+ <h1 className="mb-6 text-2xl font-bold">Create an account</h1>
6
+ <p className="text-sm text-gray-500">Registration coming soon.</p>
7
+ </div>
8
+ </div>
9
+ );
10
+ }
@@ -0,0 +1,10 @@
1
+ export default function BillingPage() {
2
+ return (
3
+ <div>
4
+ <h1 className="text-2xl font-bold">Billing</h1>
5
+ <p className="mt-2 text-gray-600">
6
+ Manage your subscription, usage, and payment methods.
7
+ </p>
8
+ </div>
9
+ );
10
+ }
@@ -0,0 +1,8 @@
1
+ export default function DeploymentsPage() {
2
+ return (
3
+ <div>
4
+ <h1 className="text-2xl font-bold">Deployments</h1>
5
+ <p className="mt-2 text-gray-600">View and manage your deployment history.</p>
6
+ </div>
7
+ );
8
+ }
@@ -0,0 +1,8 @@
1
+ export default function DomainsPage() {
2
+ return (
3
+ <div>
4
+ <h1 className="text-2xl font-bold">Domains</h1>
5
+ <p className="mt-2 text-gray-600">Manage custom domains and DNS records.</p>
6
+ </div>
7
+ );
8
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Dashboard shell layout — sidebar + top bar wrapper.
3
+ * All authenticated dashboard pages live under this layout.
4
+ */
5
+ export default function DashboardLayout({ children }: { children: React.ReactNode }) {
6
+ return (
7
+ <div className="flex h-screen">
8
+ {/* Sidebar */}
9
+ <aside className="w-64 shrink-0 border-r bg-gray-50 p-4">
10
+ <div className="mb-8 text-lg font-bold">Openship</div>
11
+ <nav className="flex flex-col gap-1 text-sm">
12
+ <a href="/projects" className="rounded px-3 py-2 hover:bg-gray-200">
13
+ Projects
14
+ </a>
15
+ <a href="/deployments" className="rounded px-3 py-2 hover:bg-gray-200">
16
+ Deployments
17
+ </a>
18
+ <a href="/domains" className="rounded px-3 py-2 hover:bg-gray-200">
19
+ Domains
20
+ </a>
21
+ <a href="/monitoring" className="rounded px-3 py-2 hover:bg-gray-200">
22
+ Monitoring
23
+ </a>
24
+ <a href="/settings" className="rounded px-3 py-2 hover:bg-gray-200">
25
+ Settings
26
+ </a>
27
+ <a href="/billing" className="rounded px-3 py-2 hover:bg-gray-200">
28
+ Billing
29
+ </a>
30
+ </nav>
31
+ </aside>
32
+
33
+ {/* Main content */}
34
+ <main className="flex-1 overflow-y-auto p-6">{children}</main>
35
+ </div>
36
+ );
37
+ }
@@ -0,0 +1,8 @@
1
+ export default function MonitoringPage() {
2
+ return (
3
+ <div>
4
+ <h1 className="text-2xl font-bold">Monitoring</h1>
5
+ <p className="mt-2 text-gray-600">Logs, metrics, and health checks for your services.</p>
6
+ </div>
7
+ );
8
+ }
@@ -0,0 +1,8 @@
1
+ export default function ProjectsPage() {
2
+ return (
3
+ <div>
4
+ <h1 className="text-2xl font-bold">Projects</h1>
5
+ <p className="mt-2 text-gray-600">Create and manage your deployment projects.</p>
6
+ </div>
7
+ );
8
+ }