diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..121f74b --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,47 @@ +name: Docker Image CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Log in to Docker Hub + if: github.event_name == 'push' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + # Backend + - name: Build and push backend image + uses: docker/build-push-action@v6 + with: + context: ./backend + file: ./docker/Dockerfile.backend + target: development + push: ${{ github.event_name == 'push' }} + tags: callofcode07/coc-admin-backend:latest + + # Frontend + - name: Build and push frontend image + uses: docker/build-push-action@v6 + with: + context: ./frontend + file: ./docker/Dockerfile.frontend + target: development + push: ${{ github.event_name == 'push' }} + tags: callofcode07/coc-admin-frontend:latest + # change this if using this for production + build-args: | + VITE_API_URL=https://api.yourdomain.com diff --git a/.gitignore b/.gitignore index a14702c..026b2a4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json .env.test.local .env.production.local .env.local +.env.local.coc-api # caches .eslintcache diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..0b21138 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,351 @@ +# 🐳 Docker β€” Local Development Guide + +This guide explains how to spin up the full **COC-Admin** stack (COC API + Admin Backend + Admin Frontend) locally using Docker Compose. + +--- + +## πŸ“ Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Docker Compose Stack β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ coc-api │◄───│ server │◄───│ web β”‚ β”‚ +β”‚ β”‚ (COC API) β”‚ β”‚ (Admin Back.) β”‚ β”‚ (Admin Front.) β”‚ β”‚ +β”‚ β”‚ port: 3000 β”‚ β”‚ port: 8000 β”‚ β”‚ port: 5173 β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +| Service | Image / Build | Port | Description | +| ---------- | ------------------------------------------ | ------ | ----------------------------------- | +| `coc-api` | `callofcode07/coc-api:latest` (pulled) | `3000` | The external COC platform API | +| `server` | Built from `docker/Dockerfile.backend` | `8000` | COC-Admin Express/Bun backend | +| `web` | Built from `docker/Dockerfile.frontend` | `5173` | COC-Admin React/Vite frontend | + +> **Startup order enforced by health-checks:** +> `coc-api` must be healthy β†’ `server` starts β†’ `server` must be healthy β†’ `web` starts. + +--- + +## βœ… Prerequisites + +| Tool | Minimum version | Install link | +| -------------- | --------------- | -------------------------------------- | +| Docker | 24+ | https://docs.docker.com/get-docker/ | +| Docker Compose | v2 (plugin) | bundled with Docker Desktop / `docker compose` command | + +> **No Bun, Node, or npm required** on the host machine β€” everything runs inside containers. + +--- + +## πŸ“ File Reference + +``` +COC-Admin/ +β”œβ”€β”€ docker-compose.yml # Orchestrates all three services +└── docker/ + β”œβ”€β”€ Dockerfile.backend # Multi-stage: deps β†’ build β†’ development β†’ production + β”œβ”€β”€ Dockerfile.frontend # Multi-stage: deps β†’ build β†’ development β†’ production + β”œβ”€β”€ .env.example.coc-api # Template for COC API env vars β€” copy and fill in values + β”œβ”€β”€ .env.local.backend # Backend env vars (edit before running) + β”œβ”€β”€ .env.local.coc-api # COC API env vars (edit before running, gitignored) + └── .env.local.frontend # Frontend env vars (edit before running) +``` + +> **`.gitignore` note:** `docker/.env.local.coc-api` is excluded from version control. +> Use `docker/.env.example.coc-api` as the starting template. All other `docker/.env.local.*` files +> are tracked in the repo pre-filled with safe local defaults β€” review and adjust them before running. + +--- + +## βš™οΈ Environment Setup + +The `docker/` directory ships with environment files for each service. Review each before starting the stack. + +### 1. COC API (`docker/.env.local.coc-api`) + +Controls the `coc-api` container (the external COC platform API). This file is **gitignored** β€” copy from the example template first: + +```bash +cp docker/.env.example.coc-api docker/.env.local.coc-api +``` + +Then fill in the values: + +```env +# Supabase Postgres (connection pooler β€” used at runtime) +DATABASE_URL="postgresql://:@:5432/postgres" + +# Direct connection (session-mode pooler β€” used for Prisma migrations on container start) +DIRECT_URL="postgresql://:@:5432/postgres" + +# Supabase project details +SUPABASE_URL="https://.supabase.co" +SUPABASE_SERVICE_ROLE_KEY="" + +NODE_ENV='Production' +``` + +### 2. Admin Backend (`docker/.env.local.backend`) + +Controls the `server` (COC-Admin backend) container: + +```env +# Internal Docker network URL β€” do not change this +API_URL=http://coc-api:3000 +PORT=8000 + +# Auth secrets β€” change these to something random and strong +JWT_SECRET=COC_ADMIN_JWT_SECRET +REFRESH_SECRET=COC_ADMIN_REFRESH_SECRET + +# Token lifetimes +ACCESS_TTL=15 # minutes +REFRESH_TTL=7 # days + +# Rate limiting +RATE_LIMIT_WINDOW_MINUTES=15 +RATE_LIMIT_MAX_REQUESTS=100 + +# Password hashing cost factor +SALTING=8 + +# Email service (Resend) +EMAIL_ID="your-sender@example.com" +CONTACT_EMAIL_ID="callofcode07@gmail.com" +RESEND_API_KEY="re_xxxxxxxxxxxxxxxx" + +# CORS β€” must match the frontend URL visible to your browser +ALLOWED_ORIGINS=http://localhost:5173 + +# Community links injected into email templates +WHATSAPP_LINK=https://chat.whatsapp.com/your-group +DISCORD_LINK=https://discord.gg/your-server + +# Keep this set to 'production' β€” refresh tokens (HTTP-only cookies) require it +NODE_ENV='production' +``` + +> ⚠️ **Important:** `NODE_ENV` **must** be set to `'production'` even in the local Docker environment. +> This is required for refresh tokens to work correctly (they rely on `secure` cookie flags that only activate in production mode). + +### 3. Admin Frontend (`docker/.env.local.frontend`) + +Controls the `web` (COC-Admin frontend) container: + +```env +# Must be reachable from the browser (not the Docker network) +VITE_API_URL="http://localhost:8000" + +# Optional β€” loading GIF shown on auth pages +VITE_GIF_URL=https://media.tenor.com/WqfUsqUQWRsAAAAC/chala-ja.gif +``` + +> **Note:** `VITE_API_URL` is also passed as a Docker build-arg in `docker-compose.yml`. +> The default value `http://localhost:8000` works for local development without changes. + +--- + +## πŸš€ Starting the Stack + +```bash +# From the repository root β€” build images and start all services +docker compose up --build +``` + +| Flag | Purpose | +| ----------- | -------------------------------------------------- | +| `--build` | Rebuild the `server` and `web` images from source | +| `-d` | Run in detached (background) mode | +| `--watch` | Enable live file sync + auto-rebuild (see below) | + +Once all three services are healthy you can access: + +- **Admin Frontend** β†’ http://localhost:5173 +- **Admin Backend API** β†’ http://localhost:8000 +- **COC API** β†’ http://localhost:3000 + +--- + +## πŸ”₯ Hot-Reload with `--watch` + +Docker Compose Watch keeps your running containers in sync with local source changes β€” no manual rebuilds needed during development. + +```bash +# Build images and start the stack with file watching enabled +docker compose up --build --watch +``` + +### What happens on each change + +| You change… | Action | +| ---------------------------------------- | ------------------------------------------------------------------------------- | +| `backend/src/**` | Files are **synced** into the container; Bun (`bun --watch`) picks up the change automatically | +| `frontend/src/**` | Files are **synced** into the container; Vite HMR reloads the browser | +| `frontend/public/**` | Static assets are **synced** into the container | +| `backend/package.json` or `bun.lock` | Image is **rebuilt** (`bun install` re-runs) | +| `frontend/package.json` or `bun.lock` | Image is **rebuilt** (`bun install` re-runs) | + +> **Note:** The `coc-api` service is a pre-built image and is not watched β€” changes to the external API require re-pulling the image manually (see [Pull the latest COC API image](#pull-the-latest-coc-api-image)). + +--- + +## πŸ›‘ Stopping the Stack + +```bash +# Stop and remove containers (keeps images and volumes) +docker compose down + +# Stop, remove containers AND local images built by Compose +docker compose down --rmi local +``` + +--- + +## πŸ”„ Common Workflows + +### Rebuild a single service after code changes + +```bash +docker compose up --build server # rebuild & restart backend only +docker compose up --build web # rebuild & restart frontend only +``` + +### Enable watch mode (hot-reload during development) + +```bash +# Start fresh with watch mode β€” file changes sync automatically +docker compose up --build --watch + +# If the stack is already running, enable watch in a separate terminal +docker compose watch +``` + +### View logs + +```bash +docker compose logs -f # tail all services +docker compose logs -f server # tail backend only +docker compose logs -f web # tail frontend only +docker compose logs -f coc-api # tail the COC API only +``` + +### Open a shell inside a running container + +```bash +docker compose exec server sh # backend shell +docker compose exec web sh # frontend shell +docker compose exec coc-api sh # COC API shell +``` + +### Pull the latest COC API image + +The `coc-api` service uses `pull_policy: missing`, meaning Docker will only pull it once (when not already present locally). To update it manually: + +```bash +docker compose pull coc-api +docker compose up coc-api -d +``` + +--- + +## πŸ€– CI/CD β€” Automated Image Publishing + +The workflow at `.github/workflows/docker-image.yml` (**Docker Image CI**) automatically builds and pushes both the backend and frontend **development** images to Docker Hub on every push to `main`. + +| Event | Build | Push to Docker Hub | +| ------------------ | ----- | ------------------ | +| Push β†’ `main` | βœ… | βœ… | +| Pull Request β†’ `main` | βœ… | ❌ (build-only) | + +### Images published + +| Image | Registry tag | +| --------------------------------------- | ---------------------------------------- | +| `callofcode07/coc-admin-backend:latest` | Built from `docker/Dockerfile.backend` (`production` target) | +| `callofcode07/coc-admin-frontend:latest`| Built from `docker/Dockerfile.frontend` (`production` target) | + +### Required repository secrets + +Go to **Settings β†’ Secrets and variables β†’ Actions** in the GitHub repo and add: + +| Secret | Description | +| ------------------ | -------------------------------------------------------------- | +| `DOCKER_USERNAME` | Docker Hub username (`callofcode07`) | +| `DOCKER_PASSWORD` | Docker Hub access token (generate at hub.docker.com β†’ Security) | +| `VITE_API_URL` | Production backend URL injected as a build-arg into the frontend image (e.g. `https://api.example.com`) | + +> **Note:** The `coc-api` image (`callofcode07/coc-api:latest`) is managed by a separate upstream repository and workflow β€” it is only **consumed** here via `docker compose pull`. + +--- + +## πŸ› Troubleshooting + +### `server` or `web` container exits immediately + +Check the logs for the failing service: + +```bash +docker compose logs server +docker compose logs web +``` + +Common causes: +- Missing or malformed env variables in `docker/.env.local.backend` or `docker/.env.local.frontend` +- The `coc-api` health-check is failing (bad `DATABASE_URL` or `SUPABASE_*` credentials) +- `NODE_ENV` is not set to `'production'` in `docker/.env.local.backend` (breaks refresh tokens) + +### Port already in use + +```bash +# Find and kill the process using the port (example: 8000) +sudo lsof -ti:8000 | xargs kill -9 +``` + +Or change the host-side port mappings in `docker-compose.yml`: + +```yaml +ports: + - "8001:8000" # map host 8001 β†’ container 8000 +``` + +### `coc-api` health-check keeps failing + +The health-check hits `http://localhost:3000/health` inside the container. If it never passes: +1. Verify `DATABASE_URL` and `DIRECT_URL` in `docker/.env.local.coc-api` are correct. +2. Ensure your Supabase project is reachable from your machine. +3. Manually inspect the container: + +```bash +docker compose exec coc-api sh +wget -qO- http://localhost:3000/health +``` + +### Images are stale after code changes + +Force a clean rebuild: + +```bash +docker compose build --no-cache +docker compose up +``` + +### Bun watch mode not picking up changes + +If `bun --watch` inside the `server` container stops reacting to file syncs, restart the service: + +```bash +docker compose restart server +``` + +--- + +## πŸ“ Notes + +- The `development` build target in both Dockerfiles runs the source directly β€” the backend uses `bun --watch src/server.ts` for hot-reloading, and the frontend uses Vite's built-in HMR dev server. No production build step is needed for local development. +- The `production` build targets produce optimised, minimal images suitable for pushing to a registry; they are **not** used by the default `docker-compose.yml`. +- `NODE_ENV` must be `'production'` in `docker/.env.local.backend` for HTTP-only cookie-based refresh tokens to function correctly β€” this is intentional and not a mistake. +- All secrets in `docker/.env.local.*` should never be committed with real credentials. The `.env.local.coc-api` file is gitignored; the others contain safe placeholder defaults. diff --git a/README.md b/README.md index 78a9ac0..fcdad27 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,14 @@ This is a monorepo containing both frontend and backend applications: COC-Admin/ β”œβ”€β”€ frontend/ # React + TypeScript + Vite frontend β”œβ”€β”€ backend/ # Express + Bun backend API +β”œβ”€β”€ docker/ # Dockerfiles and env templates +β”‚ β”œβ”€β”€ Dockerfile.backend +β”‚ β”œβ”€β”€ Dockerfile.frontend +β”‚ β”œβ”€β”€ .env.example.coc-api # Template β€” copy to .env.local.coc-api +β”‚ β”œβ”€β”€ .env.local.backend +β”‚ β”œβ”€β”€ .env.local.coc-api # Gitignored β€” fill in Supabase credentials +β”‚ └── .env.local.frontend +β”œβ”€β”€ docker-compose.yml # Compose orchestration β”œβ”€β”€ package.json # Root package with dev scripts └── README.md # This file ``` @@ -87,7 +95,36 @@ COC-Admin/ ## πŸš€ Getting Started -### Prerequisites +### 🐳 Docker (Recommended) + +The fastest way to run the full stack locally is with Docker Compose β€” no Bun or Node installation required. + +The stack spins up three services: + +| Service | Image | Port | Description | +| --------- | ---------------------------------- | ------ | -------------------------- | +| `coc-api` | `callofcode07/coc-api:latest` | `3000` | External COC platform API | +| `server` | Built from `docker/Dockerfile.backend` | `8000` | COC-Admin backend | +| `web` | Built from `docker/Dockerfile.frontend` | `5173` | COC-Admin frontend | + +See **[DOCKER.md](DOCKER.md)** for the complete setup guide covering environment configuration, service startup, watch mode (hot-reload), and troubleshooting. + +```bash +# 1. Copy and fill in the COC API credentials (gitignored) +cp docker/.env.example.coc-api docker/.env.local.coc-api + +# 2. Start the full stack with live hot-reload (recommended for development) +docker compose up --build --watch + +# Or without watch mode +docker compose up --build +``` + +--- + +### Manual Setup (Without Docker) + +#### Prerequisites - [Bun](https://bun.sh) (v1.2.18 or higher) - Node.js (for compatibility) @@ -124,11 +161,33 @@ COC-Admin/ Backend (`backend/.env`): ```env - API_URL=your_api_url - SALTING=your_salt_rounds + PORT=8000 + + # Auth secrets β€” use long random strings in production JWT_SECRET=your_jwt_secret + REFRESH_SECRET=your_refresh_secret + + # Token lifetimes + ACCESS_TTL=15 # minutes + REFRESH_TTL=7 # days + + # Rate limiting RATE_LIMIT_WINDOW_MINUTES=15 RATE_LIMIT_MAX_REQUESTS=100 + + # Password hashing cost factor + SALTING=8 + + # Email service (Resend) + EMAIL_ID=your-sender@example.com + RESEND_API_KEY=re_xxxxxxxxxxxxxxxx + + # CORS β€” must match the frontend URL visible to your browser + ALLOWED_ORIGINS=http://localhost:5173 + + # Community links injected into email templates + WHATSAPP_LINK=https://chat.whatsapp.com/your-group + DISCORD_LINK=https://discord.gg/your-server ``` Refer to `backend/.env.example` for all available options. diff --git a/backend/.env.example b/backend/.env.example index e3d22b0..0ef31f1 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -11,7 +11,7 @@ ACCESS_TTL= #access token time in minitues ie.15 EMAIL_ID= CONTACT_EMAIL_ID= RESEND_API_KEY= -NODE_ENV= +NODE_ENV= # keep this production for refresh token to work ALLOWED_ORIGINS= WHATSAPP_LINK= #optional: injected as {{whatsapp_link}} in email templates DISCORD_LINK= #optional: injected as {{discord_link}} in email templates \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 9ec5ab5..d406913 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,7 +8,7 @@ }, "type": "module", "scripts": { - "dev": "bun src/server.ts", + "dev": "bun --watch src/server.ts", "test": "jest", "lint": "eslint src/", "format": "prettier --write src/", diff --git a/docker-compose.yml b/docker-compose.yml index f50ae0d..8f7b762 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,33 +1,83 @@ services: + + coc-api: + image: callofcode07/coc-api:latest + pull_policy: missing # pull only if not already present locally + ports: + - "3000:3000" + env_file: + - docker/.env.local.coc-api + environment: + - NODE_ENV=development + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"] + interval: 15s + timeout: 5s + retries: 3 + start_period: 15s + restart: unless-stopped + server: image: coc-admin-backend build: context: ./backend dockerfile: ../docker/Dockerfile.backend - target: production + target: development ports: - - "127.0.0.1:8000:8000" + - "8000:8000" env_file: - - backend/.env + - docker/.env.local.backend environment: - PORT=8000 + - API_URL=http://coc-api:3000 + depends_on: + coc-api: + condition: service_healthy healthcheck: - test: ["CMD", "curl", "-sf", "http://localhost:8000/health"] + test: ["CMD", "wget", "-qO-", "http://localhost:8000/health"] interval: 30s timeout: 3s retries: 3 + start_period: 10s + develop: + watch: + # sync source changes directly into the running container + - path: ./backend/src + action: sync + target: /backend/src + # rebuild image when dependencies change + - path: ./backend/package.json + action: rebuild + - path: ./backend/bun.lock + action: rebuild web: image: coc-admin-frontend build: context: ./frontend dockerfile: ../docker/Dockerfile.frontend - target: production + target: development args: - VITE_API_URL=${VITE_API_URL:-http://localhost:8000} depends_on: - - server + server: + condition: service_healthy env_file: - - frontend/.env + - docker/.env.local.frontend ports: - - "127.0.0.1:5173:5173" + - "5173:5173" + develop: + watch: + # sync source changes β€” Vite HMR handles hot-reload inside container + - path: ./frontend/src + action: sync + target: /frontend/src + # also sync public assets + - path: ./frontend/public + action: sync + target: /frontend/public + # rebuild image when dependencies change + - path: ./frontend/package.json + action: rebuild + - path: ./frontend/bun.lock + action: rebuild diff --git a/docker/.env.example.coc-api b/docker/.env.example.coc-api new file mode 100644 index 0000000..aa8b49d --- /dev/null +++ b/docker/.env.example.coc-api @@ -0,0 +1,15 @@ +# Copy this file to .env.local.coc-api and fill in the real values. + +# Supabase Postgres (connection pooler URL) +DATABASE_URL= + +# Direct connection β€” used for prisma migrate deploy on container start +DIRECT_URL= + +# Supabase project URL +SUPABASE_URL= + +# Supabase service-role key (keep this secret!) +SUPABASE_SERVICE_ROLE_KEY= + +NODE_ENV= diff --git a/docker/.env.local.backend b/docker/.env.local.backend new file mode 100644 index 0000000..8881861 --- /dev/null +++ b/docker/.env.local.backend @@ -0,0 +1,17 @@ +## local setup ENV +API_URL=http://coc-api:3000 +SALTING=8 +JWT_SECRET=COC_ADMIN_JWT_SECRET +REFRESH_SECRET=COC_ADMIN_REFRESH_SECRET +RATE_LIMIT_WINDOW_MINUTES=15 +RATE_LIMIT_MAX_REQUESTS=100 +REFRESH_TTL=7 +ACCESS_TTL=15 +EMAIL_ID = "Temp@gmail.com" # your email for mail service +CONTACT_EMAIL_ID = "callofcode07@gmail.com" +RESEND_API_KEY= "Resend_api_key" # RESEND API KEY for mail Service +NODE_ENV='production' #keep this production only then refresh token will work +ALLOWED_ORIGINS=http://localhost:5173 +WHATSAPP_LINK=https://chat.whatsapp.com/your-group +DISCORD_LINK=https://discord.gg/your-server +PORT=8000 diff --git a/docker/.env.local.frontend b/docker/.env.local.frontend new file mode 100644 index 0000000..2ec6364 --- /dev/null +++ b/docker/.env.local.frontend @@ -0,0 +1,3 @@ +## local setup ENV +VITE_API_URL="http://localhost:8000" +VITE_GIF_URL=https://media.tenor.com/WqfUsqUQWRsAAAAC/chala-ja.gif \ No newline at end of file diff --git a/docker/Dockerfile.backend b/docker/Dockerfile.backend index 400e014..0288925 100644 --- a/docker/Dockerfile.backend +++ b/docker/Dockerfile.backend @@ -1,4 +1,4 @@ -FROM oven/bun:1.3 AS base +FROM oven/bun:1-alpine AS base WORKDIR /backend # ----------------------------- @@ -17,26 +17,23 @@ FROM deps AS build COPY . . RUN bun build --target=bun --production --outdir=dist src/server.ts - # ----------------------------- # development stage # ----------------------------- FROM deps AS development COPY . . EXPOSE 8000 -CMD ["bun", "src/server.ts"] +CMD ["bun", "dev"] # ----------------------------- # production stage # ----------------------------- - - -FROM oven/bun:1-slim AS production +FROM oven/bun:1-alpine AS production WORKDIR /var/www/admin-server -RUN groupadd -g 1001 nodejs && useradd -u 1001 -g nodejs -m bunjs -COPY --from=build --chown=bunjs:nodejs /backend/dist ./var/www/admin-server -RUN chown -R bunjs:nodejs /var/www/admin-server +RUN addgroup -g 1001 nodejs && adduser -u 1001 -G nodejs -D bunjs + +COPY --from=build --chown=bunjs:nodejs /backend/dist/server.js ./server.js USER bunjs EXPOSE 8000 @@ -44,4 +41,4 @@ EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget -qO- http://localhost:8000/health || exit 1 -CMD ["bun", "/var/www/admin-server/server.js"] +CMD ["bun", "server.js"] diff --git a/docker/Dockerfile.frontend b/docker/Dockerfile.frontend index 24646a2..734c958 100644 --- a/docker/Dockerfile.frontend +++ b/docker/Dockerfile.frontend @@ -1,4 +1,4 @@ -FROM oven/bun:1.3 AS base +FROM oven/bun:1-alpine AS base WORKDIR /frontend # ----------------------------- @@ -21,28 +21,24 @@ ENV VITE_API_URL=$VITE_API_URL COPY . . RUN bun run build - # ----------------------------- # development stage # ----------------------------- FROM deps AS development COPY . . EXPOSE 5173 -CMD ["bun", "run", "dev", "--port", "5173", "--host", "0.0.0.0"] +CMD ["bun", "run", "dev", "--port", "5173", "--host"] # ----------------------------- # production stage # ----------------------------- - - -FROM oven/bun:1-slim AS production +FROM oven/bun:1-alpine AS production WORKDIR /var/www/admin-web -RUN groupadd -g 1001 nodejs && useradd -u 1001 -g nodejs -m bunjs +RUN addgroup -g 1001 nodejs && adduser -u 1001 -G nodejs -D bunjs COPY --from=build --chown=bunjs:nodejs /frontend/dist ./dist COPY --from=build --chown=bunjs:nodejs /frontend/serve.ts ./serve.ts -RUN chown -R bunjs:nodejs /var/www/admin-web USER bunjs EXPOSE 5173 diff --git a/frontend/.env.example b/frontend/.env.example index e80df16..510f20a 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1,2 +1,2 @@ -VITE_API_URL="http://localhost:3000" +VITE_API_URL="http://localhost:8000" VITE_GIF_URL= \ No newline at end of file diff --git a/frontend/serve.ts b/frontend/serve.ts index 1c0afe4..4f4818e 100644 --- a/frontend/serve.ts +++ b/frontend/serve.ts @@ -12,6 +12,14 @@ serve({ const url = new URL(req.url); let path = url.pathname; + // Health-check endpoint β€” used by Docker HEALTHCHECK + if (path === "/health") { + return new Response(JSON.stringify({ status: "ok" }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + // Default to index.html for root if (path === "/") path = "/index.html";