mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Merge pull request #372 from opastorello/feat/docker-support
Add optional Docker / Docker Compose support
This commit is contained in:
commit
5cbc7faf08
17
.dockerignore
Normal file
17
.dockerignore
Normal file
@ -0,0 +1,17 @@
|
||||
.git
|
||||
.github
|
||||
.next
|
||||
node_modules
|
||||
docs
|
||||
mcp-server
|
||||
supabase
|
||||
*.md
|
||||
!README.md
|
||||
.env*
|
||||
!.env.local.example
|
||||
Dockerfile
|
||||
docker-compose*.yml
|
||||
.dockerignore
|
||||
.vscode
|
||||
.idea
|
||||
coverage
|
||||
55
Dockerfile
Normal file
55
Dockerfile
Normal file
@ -0,0 +1,55 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Stage 1 — install dependencies (cached until package*.json change)
|
||||
# ---------------------------------------------------------------
|
||||
FROM node:20-alpine AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Stage 2 — build
|
||||
#
|
||||
# NEXT_PUBLIC_* values are inlined into the client bundle at build
|
||||
# time, so they must be provided as build args (docker-compose.yml
|
||||
# forwards them from .env.local). Server-only secrets (service role
|
||||
# key, ENCRYPTION_KEY, META_APP_SECRET, ...) are read at runtime and
|
||||
# must NOT be baked into the image.
|
||||
# ---------------------------------------------------------------
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
ARG NEXT_PUBLIC_SUPABASE_URL
|
||||
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
ARG NEXT_PUBLIC_SITE_URL
|
||||
ARG NEXT_PUBLIC_APP_LOCALE=en
|
||||
ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \
|
||||
NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL \
|
||||
NEXT_PUBLIC_APP_LOCALE=$NEXT_PUBLIC_APP_LOCALE \
|
||||
NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Stage 3 — minimal runtime (standalone output)
|
||||
# ---------------------------------------------------------------
|
||||
FROM node:20-alpine AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production \
|
||||
NEXT_TELEMETRY_DISABLED=1 \
|
||||
PORT=3000 \
|
||||
HOSTNAME=0.0.0.0
|
||||
|
||||
RUN addgroup -S nextjs && adduser -S nextjs -G nextjs
|
||||
|
||||
COPY --from=builder --chown=nextjs:nextjs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nextjs /app/.next/static ./.next/static
|
||||
COPY --from=builder --chown=nextjs:nextjs /app/public ./public
|
||||
|
||||
USER nextjs
|
||||
EXPOSE 3000
|
||||
CMD ["node", "server.js"]
|
||||
@ -90,6 +90,9 @@ npm run dev
|
||||
Open <http://localhost:3000>. You'll be redirected to `/login` (or
|
||||
`/dashboard` if already signed in).
|
||||
|
||||
Prefer containers? See [docs/docker.md](./docs/docker.md) for the
|
||||
Dockerfile + Docker Compose setup.
|
||||
|
||||
## 🚀 Deploy on Hostinger (recommended)
|
||||
|
||||
<p align="center">
|
||||
|
||||
40
docker-compose.yml
Normal file
40
docker-compose.yml
Normal file
@ -0,0 +1,40 @@
|
||||
name: wacrm
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
# NEXT_PUBLIC_* vars are inlined at build time — forward them
|
||||
# from .env.local as build args. Changing any of them requires
|
||||
# a rebuild (docker compose up --build).
|
||||
args:
|
||||
NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL}
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY}
|
||||
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-}
|
||||
NEXT_PUBLIC_APP_LOCALE: ${NEXT_PUBLIC_APP_LOCALE:-en}
|
||||
# Runtime (server-only) secrets come from .env.local; they are
|
||||
# never baked into the image.
|
||||
env_file:
|
||||
- .env.local
|
||||
# The server always listens on 3000 inside the container. Pinned
|
||||
# here because `environment` wins over `env_file`, so a stray PORT
|
||||
# in .env.local can't desync the app from the mapping and the
|
||||
# healthcheck below.
|
||||
environment:
|
||||
PORT: 3000
|
||||
# Host side only — HOST_PORT=8080 publishes the app on :8080.
|
||||
ports:
|
||||
- '${HOST_PORT:-3000}:3000'
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'node',
|
||||
'-e',
|
||||
"fetch('http://localhost:3000').then((r)=>process.exit(r.ok||r.status<500?0:1)).catch(()=>process.exit(1))",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
66
docs/docker.md
Normal file
66
docs/docker.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Running with Docker
|
||||
|
||||
The repo ships a multi-stage `Dockerfile` (Next.js standalone output,
|
||||
runs as a non-root user) and a `docker-compose.yml` with a single
|
||||
`app` service. Supabase is external — point the app at your hosted
|
||||
(or self-hosted) Supabase project via env vars; no database container
|
||||
is included.
|
||||
|
||||
## Quick start
|
||||
|
||||
1. Copy the env template and fill it in:
|
||||
|
||||
```bash
|
||||
cp .env.local.example .env.local
|
||||
```
|
||||
|
||||
2. Build and start (the `--env-file` flag is required — Compose only
|
||||
reads `.env` by default for `${VAR}` substitution, and this project
|
||||
keeps its config in `.env.local`):
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env.local up --build -d
|
||||
```
|
||||
|
||||
3. The app is served on [http://localhost:3000](http://localhost:3000)
|
||||
(publish it elsewhere with `HOST_PORT=8080` in `.env.local`).
|
||||
|
||||
> Use `HOST_PORT`, not `PORT`, to move the published port. `PORT` is
|
||||
> what the server listens on _inside_ the container, and `env_file`
|
||||
> would inject it there — leaving the app on a port the mapping and
|
||||
> the healthcheck don't target. Compose pins it to 3000 for that
|
||||
> reason.
|
||||
|
||||
## Build-time vs runtime variables
|
||||
|
||||
- `NEXT_PUBLIC_*` variables are **inlined into the client bundle at
|
||||
build time**. They are passed as Docker build args by
|
||||
`docker-compose.yml`. If you change any of them, rebuild:
|
||||
`docker compose --env-file .env.local up --build -d`.
|
||||
- Everything else (`SUPABASE_SERVICE_ROLE_KEY`, `ENCRYPTION_KEY`,
|
||||
`META_APP_SECRET`, …) is read at **runtime** from `.env.local` via
|
||||
`env_file` and is never baked into the image — safe to change with
|
||||
just a container restart.
|
||||
|
||||
## Plain Docker (no Compose)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co \
|
||||
--build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key \
|
||||
-t wacrm .
|
||||
|
||||
docker run -d --env-file .env.local -e PORT=3000 -p 3000:3000 wacrm
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Database migrations under `supabase/` are **not** run by the
|
||||
container — apply them with the Supabase CLI as described in the
|
||||
README.
|
||||
- Nothing inside the container is scheduled. If you use automation
|
||||
Wait steps or flows, point an external scheduler at
|
||||
`GET /api/automations/cron` and `GET /api/flows/cron` on this
|
||||
deployment, sending the shared secret in the `x-cron-secret` header
|
||||
(`AUTOMATION_CRON_SECRET`, see `.env.local.example`). Both return
|
||||
503 until that variable is set.
|
||||
@ -64,6 +64,11 @@ const SECURITY_HEADERS = [
|
||||
] as const;
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
// Emit a self-contained server bundle (.next/standalone) so the
|
||||
// Docker image can run without node_modules or the Next CLI.
|
||||
// Harmless outside Docker: `next start` keeps working as before.
|
||||
output: "standalone",
|
||||
|
||||
/**
|
||||
* Cross-origin dev access (Next.js 16).
|
||||
*
|
||||
|
||||
Loading…
Reference in New Issue
Block a user