diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4b4dbf2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# Multi-stage Dockerfile for wsmud +FROM node:18-alpine AS build +WORKDIR /app + +# copy package and lock first for faster rebuilds +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev || npm i --no-audit + +# copy full source and build frontend with vite +COPY . . +RUN npm run build || true + +FROM node:18-alpine AS runtime +WORKDIR /app + +# create folder for logs and db +RUN mkdir -p /app/log /app/data /app/www + +# copy node_modules from build stage +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/www ./www +COPY --from=build /app . + +ENV NODE_ENV=production +EXPOSE 80 + +CMD ["node", "web.js"] diff --git a/README_DEPLOY.md b/README_DEPLOY.md new file mode 100644 index 0000000..856ef1a --- /dev/null +++ b/README_DEPLOY.md @@ -0,0 +1,75 @@ +# 部署说明(Docker / 本地 / 反向代理) + +本文档说明如何在服务器上运行本项目(Node + Express + Vite 前端构建)。 + +环境变量(必需) +- WEB_PORT: 前端/HTTP 服务端口(示例 80) +- WS_PORT: 后端 WebSocket 端口(示例 4000) +- MD5_PREFIX: 项目内部使用的 MD5 前缀 +- DESIV: DES IV(字符串) +- SESSION_SECRET: express-session 的 secret + +快速开始(Docker Compose) + +1. 在项目根(带有 docker-compose.yml 的目录)创建或修改 `.env`,填入必需变量。示例: + +``` +WEB_PORT=80 +WS_PORT=4000 +MD5_PREFIX=change_me +DESIV=change_me +SESSION_SECRET=change_me +``` + +2. 构建并启动 + +```bash +docker compose up --build -d +``` + +3. 检查日志 + +```bash +docker compose logs -f +``` + +本地开发 + +- 前端:在 `src/` 目录使用 Vite 开发服务器 + - npm run dev +- 后端(静态 + API): + - npm run web (启动 web.js) + - npm run os (启动 main.js,后台游戏服务) + +反向代理(使用 Nginx) + +示例 Nginx 配置(简化) + +``` +server { + listen 80; + server_name example.com; + + location / { + proxy_pass http://127.0.0.1:80; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +持久化与备份 + +- SQLite 数据文件位于 `data/` 目录(`database.db`),请确保卷挂载或定期备份。 +- 日志目录在 `log/`。 + +K8s / CI/CD + +- Dockerfile 为多阶段,已将前端构建产物输出到 `www/`。 +- 在 CI 中请先运行 `npm ci` 和 `npm run build`,然后构建镜像并推送到镜像仓库。 + +常见问题 + +- 端口冲突:确认 `WEB_PORT` 与宿主机端口映射一致 +- 权限问题:确保 `data/`、`log/` 可由容器写入(host 的 umask/owner) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7915fde --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +version: '3.8' +services: + wsmud: + build: . + image: wsmud:latest + ports: + - "80:80" + - "4000:4000" # optional websocket port mapping if used + environment: + - WEB_PORT=80 + - WS_PORT=4000 + - MD5_PREFIX=change_me + - DESIV=change_me + - SESSION_SECRET=change_me + volumes: + - ./data:/app/data + - ./log:/app/log + - ./www:/app/www + restart: unless-stopped diff --git a/scripts/build_and_push.ps1 b/scripts/build_and_push.ps1 new file mode 100644 index 0000000..704b014 --- /dev/null +++ b/scripts/build_and_push.ps1 @@ -0,0 +1,37 @@ +param( + [Parameter(Mandatory=$true)][string]$Registry, + [Parameter(Mandatory=$true)][string]$Owner, + [Parameter(Mandatory=$true)][string]$Repo, + [string]$Tag = 'latest', + [string]$Username = $null, + [string]$Token = $null +) + +if (-not $Registry -or -not $Owner -or -not $Repo) { + Write-Error "Usage: .\scripts\build_and_push.ps1 -Registry -Owner -Repo [-Tag ] [-Username ] [-Token ]" + exit 1 +} + +$localImage = "$Repo:$Tag" +$remoteImage = "$Registry/$Owner/$Repo:$Tag" + +Write-Host "Building image: $localImage" +docker build -t $localImage . + +Write-Host "Tagging image -> $remoteImage" +docker tag $localImage $remoteImage + +if ($Token) { + Write-Host "Logging in to $Registry using token" + $secureToken = ConvertTo-SecureString $Token -AsPlainText -Force + $credential = New-Object System.Management.Automation.PSCredential($Username, $secureToken) + $Token | docker login $Registry -u ${Username:-$Owner} --password-stdin +} else { + Write-Host "Logging in to $Registry interactively" + docker login $Registry -u ${Username:-$Owner} +} + +Write-Host "Pushing $remoteImage" +docker push $remoteImage + +Write-Host "Push finished: $remoteImage" diff --git a/scripts/build_and_push.sh b/scripts/build_and_push.sh new file mode 100644 index 0000000..64398f2 --- /dev/null +++ b/scripts/build_and_push.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +# build_and_push.sh [tag] [username] [token] +# Example: +# ./scripts/build_and_push.sh git.templarz.com templarz wsmud latest tempalrz + +REGISTRY=${1:-} +OWNER=${2:-} +REPO=${3:-} +TAG=${4:-latest} +USERNAME=${5:-} +TOKEN=${6:-} + +if [ -z "$REGISTRY" ] || [ -z "$OWNER" ] || [ -z "$REPO" ]; then + echo "Usage: $0 [tag] [username] [token]" + exit 1 +fi + +LOCAL_IMAGE="${REPO}:${TAG}" +REMOTE_IMAGE="${REGISTRY}/${OWNER}/${REPO}:${TAG}" + +echo "Building image: $LOCAL_IMAGE" +docker build -t "$LOCAL_IMAGE" . + +echo "Tagging image -> $REMOTE_IMAGE" +docker tag "$LOCAL_IMAGE" "$REMOTE_IMAGE" + +if [ -n "$TOKEN" ]; then + echo "Logging in to $REGISTRY using token (password-stdin)" + echo "$TOKEN" | docker login "$REGISTRY" -u "${USERNAME:-$OWNER}" --password-stdin +else + echo "Logging in to $REGISTRY interactively (you will be prompted)" + docker login "$REGISTRY" -u "${USERNAME:-$OWNER}" +fi + +echo "Pushing $REMOTE_IMAGE" +docker push "$REMOTE_IMAGE" + +echo "Push finished: $REMOTE_IMAGE"