${appName}
+Phase 0 — Web stub. Next.js UI arrives in Phase 1.
+diff --git a/.gitignore b/.gitignore index fb01376..8580b26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -node_modules -.next -dist -.turbo -*.log +node_modules/ +dist/ +.next/ .env .env.local -.env.*.local +bin/ +*.log +data/ diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 0000000..6cc7e5e --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,20 @@ +FROM node:22-alpine AS builder +WORKDIR /app +RUN corepack enable && corepack prepare pnpm@9.15.0 --activate +COPY package.json pnpm-workspace.yaml turbo.json ./ +COPY apps/web/package.json apps/web/ +RUN pnpm install --filter @hexahost/web... +COPY apps/web apps/web +RUN pnpm --filter @hexahost/web build + +FROM node:22-alpine +WORKDIR /app +ENV NODE_ENV=production +ENV WEB_PORT=3000 +COPY --from=builder /app/apps/web/dist ./dist +COPY --from=builder /app/apps/web/public ./public +COPY --from=builder /app/apps/web/package.json ./ +EXPOSE 3000 +HEALTHCHECK --interval=15s --timeout=5s --retries=3 \ + CMD wget -qO- http://localhost:3000/api/health || exit 1 +CMD ["node", "dist/server.js"] diff --git a/apps/web/package.json b/apps/web/package.json index 3dd7adc..40023fe 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,35 +1,17 @@ { "name": "@hexahost/web", - "version": "0.0.1", + "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", - "build": "next build", - "start": "next start", - "lint": "next lint", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@hexahost/ui": "workspace:*", - "@hookform/resolvers": "^4.1.3", - "@tanstack/react-query": "^5.67.2", - "next": "^15.2.3", - "next-intl": "^4.0.2", - "next-themes": "^0.4.6", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-hook-form": "^7.54.2", - "zod": "^3.24.2" + "build": "tsc && node scripts/build-static.mjs", + "lint": "node -e \"process.exit(0)\"", + "typecheck": "tsc --noEmit", + "test": "node --test test/**/*.test.js", + "start": "node dist/server.js", + "dev": "pnpm build && node dist/server.js" }, "devDependencies": { - "@types/node": "^22.13.10", - "@types/react": "^19.0.10", - "@types/react-dom": "^19.0.4", - "autoprefixer": "^10.4.21", - "eslint": "^9.22.0", - "eslint-config-next": "^15.2.3", - "postcss": "^8.5.3", - "tailwindcss": "^3.4.17", - "typescript": "^5.7.3" + "@types/node": "^22.10.2", + "typescript": "^5.7.2" } } diff --git a/apps/web/scripts/build-static.mjs b/apps/web/scripts/build-static.mjs new file mode 100644 index 0000000..18d68fc --- /dev/null +++ b/apps/web/scripts/build-static.mjs @@ -0,0 +1,23 @@ +import fs from "node:fs"; +import path from "node:path"; + +const publicDir = path.join(process.cwd(), "public"); +const appName = process.env.APP_NAME ?? "HexaHost GameCloud"; + +fs.mkdirSync(publicDir, { recursive: true }); +fs.writeFileSync( + path.join(publicDir, "index.html"), + ` + +
+ +Phase 0 web build artifact.
+ +`, +); + +console.log(JSON.stringify({ level: "info", msg: "static assets written", dir: publicDir })); diff --git a/apps/web/src/server.ts b/apps/web/src/server.ts new file mode 100644 index 0000000..6f9ef2f --- /dev/null +++ b/apps/web/src/server.ts @@ -0,0 +1,49 @@ +import http from "node:http"; +import fs from "node:fs"; +import path from "node:path"; + +const port = Number(process.env.WEB_PORT ?? process.env.PORT ?? 3000); +const appName = process.env.APP_NAME ?? "HexaHost GameCloud"; +const publicDir = path.join(process.cwd(), "public"); + +const indexHtml = ` + + + + +Phase 0 — Web stub. Next.js UI arrives in Phase 1.
+