forked from molecule-ai/molecule-core
Single-container tenant architecture: Go platform (:8080) + Canvas Node.js (:3000) in one Fly machine, with Go's NoRoute handler reverse- proxying non-API routes to the canvas. Browser only talks to :8080. Changes: platform/Dockerfile.tenant — multi-stage build (Go + Node + runtime). Bakes workspace-configs-templates/ + org-templates/ into the image. Build context: repo root. platform/entrypoint-tenant.sh — starts both processes, kills both if either exits. Fly health check on :8080 covers the Go binary; canvas health is implicit (proxy returns 502 if canvas is down). platform/internal/router/canvas_proxy.go — httputil.ReverseProxy that forwards unmatched routes to CANVAS_PROXY_URL (http://localhost:3000). Activated by NoRoute when CANVAS_PROXY_URL env is set. platform/internal/router/router.go — wire NoRoute → canvasProxy when CANVAS_PROXY_URL is present; no-op otherwise (local dev unchanged). platform/internal/middleware/securityheaders.go — relaxed CSP to allow Next.js inline scripts/styles/eval + WebSocket + data: URIs. The strict `default-src 'self'` was blocking all canvas rendering. canvas/src/lib/api.ts — changed `||` to `??` for NEXT_PUBLIC_PLATFORM_URL so empty string means "same-origin" (combined image) instead of falling back to localhost:8080. canvas/src/components/tabs/TerminalTab.tsx — same `??` fix for WS URL. Verified: tenant machine boots, canvas renders, 8 runtime templates + 4 org templates visible, API routes work through the same port. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
64 lines
2.6 KiB
Docker
64 lines
2.6 KiB
Docker
# Dockerfile.tenant — combined platform (Go) + canvas (Next.js) image.
|
|
#
|
|
# Serves both the API (Go on :8080) and the UI (Node.js on :3000) in a
|
|
# single container. Fly listens on :8080 for health checks; an entrypoint
|
|
# script starts both processes. The Go router is the external-facing port;
|
|
# it reverse-proxies unknown routes to the canvas Node server so a single
|
|
# port handles everything.
|
|
#
|
|
# Build context: repo root (same as platform/Dockerfile).
|
|
#
|
|
# docker buildx build --platform linux/amd64 \
|
|
# -f platform/Dockerfile.tenant \
|
|
# --build-arg NEXT_PUBLIC_PLATFORM_URL="" \
|
|
# --build-arg NEXT_PUBLIC_WS_URL="" \
|
|
# -t registry.fly.io/molecule-tenant:latest \
|
|
# --push .
|
|
|
|
# ── Stage 1: Go platform binary ──────────────────────────────────────
|
|
FROM golang:1.25-alpine AS go-builder
|
|
WORKDIR /app
|
|
COPY platform/go.mod platform/go.sum ./
|
|
RUN go mod download
|
|
COPY platform/ .
|
|
RUN CGO_ENABLED=0 GOOS=linux go build -o /platform ./cmd/server
|
|
|
|
# ── Stage 2: Canvas Next.js standalone ────────────────────────────────
|
|
FROM node:20-alpine AS canvas-builder
|
|
WORKDIR /canvas
|
|
COPY canvas/package.json canvas/package-lock.json* ./
|
|
RUN npm install
|
|
COPY canvas/ .
|
|
# Platform URL is relative (same container) — empty string = same-origin.
|
|
# WebSocket URL uses relative path so the browser connects to the same host.
|
|
ARG NEXT_PUBLIC_PLATFORM_URL=""
|
|
ARG NEXT_PUBLIC_WS_URL=""
|
|
ENV NEXT_PUBLIC_PLATFORM_URL=$NEXT_PUBLIC_PLATFORM_URL
|
|
ENV NEXT_PUBLIC_WS_URL=$NEXT_PUBLIC_WS_URL
|
|
RUN npm run build
|
|
|
|
# ── Stage 3: Runtime ──────────────────────────────────────────────────
|
|
FROM node:20-alpine
|
|
RUN apk add --no-cache ca-certificates git tzdata
|
|
|
|
# Go platform binary
|
|
COPY --from=go-builder /platform /platform
|
|
COPY platform/migrations /migrations
|
|
COPY workspace-configs-templates /workspace-configs-templates
|
|
COPY org-templates /org-templates
|
|
|
|
# Canvas standalone (Next.js server.js + static assets)
|
|
WORKDIR /canvas
|
|
COPY --from=canvas-builder /canvas/.next/standalone ./
|
|
COPY --from=canvas-builder /canvas/.next/static ./.next/static
|
|
COPY --from=canvas-builder /canvas/public ./public
|
|
|
|
# Entrypoint starts both processes. Go on :8080, Canvas on :3000.
|
|
# The Go platform's router proxies unknown routes to :3000 so Fly
|
|
# only needs to expose :8080.
|
|
COPY platform/entrypoint-tenant.sh /entrypoint.sh
|
|
RUN chmod +x /entrypoint.sh
|
|
|
|
EXPOSE 8080
|
|
CMD ["/entrypoint.sh"]
|