Add Docker deployment with automatic HTTPS
Deploy game to GitHub Pages / deploy (push) Failing after 21m51s

A two-stage image runs the tests and builds the game with Node, then
serves dist/ with Caddy, which obtains and renews Let's Encrypt
certificates for robotmarkets.ir (configurable via SITE_ADDRESS).
HTTPS is required for installing the PWA and for GPS mode.

- Caddyfile: gzip/zstd, long-lived caching for hashed assets, no-cache
  for the page, service worker and manifest, SPA fallback, permissions
  policy for location and motion sensors
- docker-compose.yml: ports 80/443, persistent certificate volumes
- README: server setup, updates, running behind an existing proxy, and
  workarounds when Docker Hub is unreachable from servers in Iran

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 09:59:01 +03:30
co-authored by Claude Opus 5.5
parent 2f74788caa
commit 47b2b72ea9
5 changed files with 94 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
node_modules
dist
.git
.github
.idea
.claude
*.log
+26
View File
@@ -0,0 +1,26 @@
# SITE_ADDRESS is the domain list, e.g. "robotmarkets.ir, www.robotmarkets.ir" (automatic HTTPS),
# or ":80" for plain HTTP behind another proxy or for a local test.
{$SITE_ADDRESS::80} {
root * /srv
encode zstd gzip
# Hashed build files never change; the page, service worker and manifest must always be revalidated
# so installed copies pick up new versions.
@hashed path /assets/*
header @hashed Cache-Control "public, max-age=31536000, immutable"
@fresh path / /index.html /sw.js /manifest.webmanifest
header @fresh Cache-Control "no-cache"
@manifest path /manifest.webmanifest
header @manifest Content-Type "application/manifest+json"
header {
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
# Real-world mode needs location and the compass; the camera is reserved for the AR phase.
Permissions-Policy "geolocation=(self), camera=(self), accelerometer=(self), gyroscope=(self), magnetometer=(self)"
-Server
}
try_files {path} /index.html
file_server
}
+13
View File
@@ -0,0 +1,13 @@
# Stage 1: run the tests and build the static game with Vite.
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --no-audit --no-fund
COPY . .
RUN node --test tests/*.test.js && npm run build
# Stage 2: serve dist/ with Caddy, which also obtains and renews the HTTPS certificate.
FROM caddy:2.8.4-alpine
COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /srv
EXPOSE 80 443 443/udp
+28
View File
@@ -80,6 +80,34 @@ node --test tests/*.test.js
- `?quality=low` یا دکمهٔ «گرافیک» سایه‌ها را خاموش می‌کند (پیش‌فرض گوشی‌ها). - `?quality=low` یا دکمهٔ «گرافیک» سایه‌ها را خاموش می‌کند (پیش‌فرض گوشی‌ها).
- `?debug` وضعیت بازی را در `window.game` در دسترس می‌گذارد. - `?debug` وضعیت بازی را در `window.game` در دسترس می‌گذارد.
## انتشار روی سرور با Docker
تصویر Docker دو مرحله دارد: Node تست‌ها را اجرا و بازی را build می‌کند، و بعد Caddy فایل‌ها را سرو می‌کند. Caddy گواهی HTTPS را از Let's Encrypt خودکار می‌گیرد و تمدید می‌کند. HTTPS برای نصب روی گوشی و GPS ضروری است.
**پیش‌نیاز:**
- رکورد A دامنه‌های `robotmarkets.ir` و `www.robotmarkets.ir` به IP سرور اشاره کند.
- پورت‌های ۸۰ و ۴۴۳ سرور باز و آزاد باشند.
```sh
git clone git@github.com:hamedAbdollahzade/vending-machine-game.git && cd vending-machine-game
docker compose up -d --build
docker compose logs -f
```
- **دامنهٔ دیگر:** `SITE_ADDRESS="example.ir, www.example.ir" docker compose up -d --build`
- **به‌روزرسانی:** `git pull && docker compose up -d --build`. گواهی‌ها در volume `caddy_data` می‌مانند. بازی‌های نصب‌شده روی گوشی پیام نسخهٔ جدید را نشان می‌دهند.
- **اگر روی سرور از قبل Nginx یا پروکسی دیگری روی ۸۰ و ۴۴۳ هست:** در `docker-compose.yml` پورت‌ها را به `"8080:80"` تغییر دهید و `SITE_ADDRESS=":80"` بگذارید. بعد دامنه را در همان پروکسی به `127.0.0.1:8080` بفرستید. در این حالت HTTPS با همان پروکسی است.
**سرور داخل ایران:** Docker Hub و گاهی npm به IPهای ایران سرویس نمی‌دهند. دو راه وجود دارد:
1. **mirror داخلی برای Docker Hub:** در `/etc/docker/daemon.json` یک mirror تعریف کنید، مثلاً `{"registry-mirrors": ["https://docker.arvancloud.ir"]}`، و بعد `sudo systemctl restart docker`.
2. **ساخت روی کامپیوتر خودتان و انتقال تصویر آماده:**
```sh
docker compose build
docker save robot-market-game:latest | gzip > robot-market-game.tar.gz
scp robot-market-game.tar.gz docker-compose.yml user@server:~/game/
ssh user@server 'cd ~/game && gunzip -c robot-market-game.tar.gz | docker load && docker compose up -d --no-build'
```
## ساخت دوبارهٔ دادهٔ نقشه ## ساخت دوبارهٔ دادهٔ نقشه
`src/data/mellat.json` و `src/data/iran.json` از خروجی خام OpenStreetMap (Overpass API) و Natural Earth ساخته می‌شوند. کوئری‌ها در ابتدای اسکریپت آمده‌اند: `src/data/mellat.json` و `src/data/iran.json` از خروجی خام OpenStreetMap (Overpass API) و Natural Earth ساخته می‌شوند. کوئری‌ها در ابتدای اسکریپت آمده‌اند:
+20
View File
@@ -0,0 +1,20 @@
services:
game:
build: .
image: robot-market-game:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
environment:
# Point these domains' DNS A records at the server before the first start.
SITE_ADDRESS: ${SITE_ADDRESS:-robotmarkets.ir, www.robotmarkets.ir}
volumes:
# Keeps the HTTPS certificates across restarts and image updates.
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config: