feat: add Dockerfile, .dockerignore, and deployment documentation for Coolify integration
This commit is contained in:
parent
019ea56e6f
commit
2505fccd5f
30
.dockerignore
Normal file
30
.dockerignore
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
**/node_modules
|
||||||
|
**/.next
|
||||||
|
**/.turbo
|
||||||
|
**/dist
|
||||||
|
**/build
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.env
|
||||||
|
**/.env.local
|
||||||
|
**/.env.*.local
|
||||||
|
**/npm-debug.log
|
||||||
|
**/yarn-debug.log
|
||||||
|
**/yarn-error.log
|
||||||
|
**/*.log
|
||||||
|
**/cypress/videos
|
||||||
|
**/cypress/screenshots
|
||||||
|
**/.DS_Store
|
||||||
|
**/Thumbs.db
|
||||||
|
**/.idea
|
||||||
|
**/.vscode
|
||||||
|
**/coverage
|
||||||
|
**/.cache
|
||||||
|
**/.eslintcache
|
||||||
|
**/test-results
|
||||||
|
**/playwright-report
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
README.md
|
||||||
|
docs
|
||||||
|
*.pdf
|
||||||
102
DEPLOY_COOLIFY.md
Normal file
102
DEPLOY_COOLIFY.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# Garage dashboard (garage-erp) — Coolify deploy
|
||||||
|
|
||||||
|
Domain: **tenant.reparee.com**
|
||||||
|
Build: Docker (multi-stage), Next.js standalone output
|
||||||
|
Port: **3000** inside container; Coolify maps to 80/443 + TLS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. New Application in Coolify
|
||||||
|
|
||||||
|
1. **+ New Resource → Application → Public Repository** (or GitHub App).
|
||||||
|
2. Repo: this monorepo. Branch: `main` (or whatever).
|
||||||
|
3. **Build Pack: Dockerfile**.
|
||||||
|
4. **Base Directory**: `garage-erp` (where the Dockerfile lives — relative to repo root).
|
||||||
|
- If `garage-erp` IS the repo root, leave blank.
|
||||||
|
5. **Dockerfile Location**: `Dockerfile` (default).
|
||||||
|
6. **Port**: `3000`.
|
||||||
|
|
||||||
|
## 2. Environment variables
|
||||||
|
|
||||||
|
Coolify → Application → **Environment Variables**. Add:
|
||||||
|
|
||||||
|
| Key | Value |
|
||||||
|
|---|---|
|
||||||
|
| `NEXT_PUBLIC_API_URL` | `https://api.reparee.com` |
|
||||||
|
| `NEXT_PUBLIC_SAAS_URL` | `https://reparee.com` |
|
||||||
|
| `NEXT_PUBLIC_GARAGE_HOST_PATTERN` | `*.reparee.com` |
|
||||||
|
| `NODE_ENV` | `production` |
|
||||||
|
| `HOSTNAME` | `0.0.0.0` |
|
||||||
|
| `PORT` | `3000` |
|
||||||
|
| `NEXT_TELEMETRY_DISABLED` | `1` |
|
||||||
|
|
||||||
|
> `NEXT_PUBLIC_*` values are **baked into the JS bundle at build time**. Changing them requires a redeploy, not just a restart.
|
||||||
|
|
||||||
|
## 3. Domain + TLS
|
||||||
|
|
||||||
|
Coolify → Application → **Domains** → add `https://tenant.reparee.com`. Coolify auto-provisions Let's Encrypt cert. DNS A record must already point to the Coolify server's public IP.
|
||||||
|
|
||||||
|
## 4. Healthcheck
|
||||||
|
|
||||||
|
Coolify → Application → **Healthcheck**:
|
||||||
|
|
||||||
|
- **Path**: `/api/proxy/health` — set this once the backend is reachable; otherwise `/` works for the first deploy.
|
||||||
|
- **Port**: `3000`
|
||||||
|
- **Interval**: 30s
|
||||||
|
- **Timeout**: 10s
|
||||||
|
|
||||||
|
## 5. Build options
|
||||||
|
|
||||||
|
- **Build pack**: Dockerfile
|
||||||
|
- **Custom build command**: leave blank (Dockerfile handles it)
|
||||||
|
- **Build args**: none required; all envs are runtime
|
||||||
|
|
||||||
|
## 6. Deploy
|
||||||
|
|
||||||
|
Click **Deploy**. Watch build logs:
|
||||||
|
|
||||||
|
- Stage 1 (`deps`): `pnpm install --frozen-lockfile` — ~2-4 min.
|
||||||
|
- Stage 3 (`builder`): `pnpm --filter @garage/dashboard build` — ~3-6 min. Watch for Next.js trace warnings; they are non-fatal.
|
||||||
|
- Stage 4 (`runner`): copies standalone output; tiny.
|
||||||
|
|
||||||
|
First build is slow. Subsequent builds reuse Docker layer cache.
|
||||||
|
|
||||||
|
## 7. Smoke test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -I https://tenant.reparee.com
|
||||||
|
# → 200 OK + Set-Cookie not yet set (no session)
|
||||||
|
|
||||||
|
# Activation: full flow goes SaaS → /activate/handoff/{token}?ws=&api=
|
||||||
|
# Cookies expected after redirect: auth_token, auth_user, workspace_uuid, api_base_url
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. CORS / cookies sanity
|
||||||
|
|
||||||
|
The dashboard only sets cookies on its own origin (`tenant.reparee.com`). The Next.js server proxy at `/api/proxy/*` strips upstream `Set-Cookie` headers — Laravel cookies never reach the browser. Workspace context is read from cookies on every server-side proxy call.
|
||||||
|
|
||||||
|
Verify in browser DevTools after handoff:
|
||||||
|
|
||||||
|
- 4 cookies on `tenant.reparee.com`, all HttpOnly + SameSite=Strict + Secure.
|
||||||
|
- Network tab: all `XHR/fetch` go to `tenant.reparee.com/api/proxy/...`. **Zero direct calls** to `api.reparee.com` from the browser.
|
||||||
|
|
||||||
|
## 9. Logs
|
||||||
|
|
||||||
|
Coolify → Application → **Logs** tab streams container stdout/stderr. Next.js logs requests as `GET / 200 in 45ms` etc.
|
||||||
|
|
||||||
|
## 10. Rollback
|
||||||
|
|
||||||
|
Coolify → **Deployments** tab → pick a previous successful deployment → **Redeploy**.
|
||||||
|
|
||||||
|
## 11. Notes on monorepo
|
||||||
|
|
||||||
|
- The Dockerfile builds **only** the `@garage/dashboard` app. `@garage/api` is built as part of dashboard's `prebuild` hook (it generates OpenAPI types from `postman/collection.json`).
|
||||||
|
- If you split apps later (e.g. a separate admin app), copy the Dockerfile and change the `--filter @garage/dashboard` line.
|
||||||
|
- `output: 'standalone'` in `apps/dashboard/next.config.mjs` is required for the runner stage to work. Do not remove.
|
||||||
|
|
||||||
|
## 12. Cypress / e2e
|
||||||
|
|
||||||
|
Out of scope for the runtime container. If you want CI e2e runs:
|
||||||
|
|
||||||
|
- Use Coolify's **GitHub Actions integration** to run `pnpm test:e2e` in CI before the deploy webhook fires.
|
||||||
|
- Update `apps/dashboard/cypress.config.ts` to read `baseUrl` from env (currently hard-coded to `https://newgarage.yslootahtech.com`).
|
||||||
65
Dockerfile
Normal file
65
Dockerfile
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Multi-stage Dockerfile for garage-erp dashboard (Coolify).
|
||||||
|
# Monorepo: turborepo + pnpm; @garage/dashboard depends on @garage/api.
|
||||||
|
# Build context = garage-erp/ (repo root for this app).
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
# Stage 1: base — pnpm via corepack, shared layer
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
FROM node:22-alpine AS base
|
||||||
|
RUN corepack enable && corepack prepare pnpm@9.0.0 --activate
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
# Stage 2: deps — install all workspace deps
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
FROM base AS deps
|
||||||
|
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json ./
|
||||||
|
COPY apps/dashboard/package.json ./apps/dashboard/
|
||||||
|
COPY packages/api/package.json ./packages/api/
|
||||||
|
COPY packages/ui/package.json ./packages/ui/
|
||||||
|
COPY packages/eslint-config/package.json ./packages/eslint-config/
|
||||||
|
COPY packages/typescript-config/package.json ./packages/typescript-config/
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
# Stage 3: builder — build dashboard with standalone output
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
FROM base AS builder
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY --from=deps /app/apps/dashboard/node_modules ./apps/dashboard/node_modules
|
||||||
|
COPY --from=deps /app/packages/api/node_modules ./packages/api/node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
# Build only the dashboard (turbo handles @garage/api codegen via prebuild hook)
|
||||||
|
RUN pnpm --filter @garage/dashboard build
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
# Stage 4: runner — minimal runtime, standalone Next.js
|
||||||
|
# ────────────────────────────────────────────────────────────
|
||||||
|
FROM node:22-alpine AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs && \
|
||||||
|
adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Standalone server + traced node_modules
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/apps/dashboard/.next/standalone ./
|
||||||
|
# Static assets
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/apps/dashboard/.next/static ./apps/dashboard/.next/static
|
||||||
|
# Public assets (logos, etc.)
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/apps/dashboard/public ./apps/dashboard/public
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Standalone entry is at apps/dashboard/server.js after copy
|
||||||
|
CMD ["node", "apps/dashboard/server.js"]
|
||||||
16
apps/dashboard/.env.production.example
Normal file
16
apps/dashboard/.env.production.example
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Production env — Coolify provides these at deploy time.
|
||||||
|
|
||||||
|
# Fallback backend URL — only used if the per-session `api_base_url` cookie
|
||||||
|
# is not set. SaaS handoff sets that cookie; server proxy uses it.
|
||||||
|
NEXT_PUBLIC_API_URL=https://api.reparee.com
|
||||||
|
|
||||||
|
# Used by "Workspace session not found" page CTA back to SaaS.
|
||||||
|
NEXT_PUBLIC_SAAS_URL=https://reparee.com
|
||||||
|
|
||||||
|
# Image remotePatterns wildcard for SaaS-provisioned subdomains.
|
||||||
|
NEXT_PUBLIC_GARAGE_HOST_PATTERN=*.reparee.com
|
||||||
|
|
||||||
|
# Forces Next to bind 0.0.0.0 in Docker. Coolify normally sets this.
|
||||||
|
HOSTNAME=0.0.0.0
|
||||||
|
PORT=3000
|
||||||
|
NODE_ENV=production
|
||||||
@ -1,5 +1,6 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
|
output: 'standalone',
|
||||||
images: {
|
images: {
|
||||||
|
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
@ -21,6 +22,24 @@ const nextConfig = {
|
|||||||
port: '',
|
port: '',
|
||||||
pathname: '/**',
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'api.reparee.com',
|
||||||
|
port: '',
|
||||||
|
pathname: '/**',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'reparee.com',
|
||||||
|
port: '',
|
||||||
|
pathname: '/**',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'tenant.reparee.com',
|
||||||
|
port: '',
|
||||||
|
pathname: '/**',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: process.env.NEXT_PUBLIC_GARAGE_HOST_PATTERN ?? '*.reparee.com',
|
hostname: process.env.NEXT_PUBLIC_GARAGE_HOST_PATTERN ?? '*.reparee.com',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user