#!/usr/bin/env bash set -euo pipefail VERSION="0.1.0" OUTPUT_DIR="" SKIP_BUILD=0 SKIP_PULL=0 usage() { cat <<'EOF' Usage: ./package-offline-server.sh [options] Options: --version VERSION Image/package version, default: 0.1.0 --output-dir DIR Output package directory, default: server/dist/SimCAEServerDockerPackage --skip-build Do not build simcae-update-server image --skip-pull Do not pull MinIO images -h, --help Show this help EOF } while [[ $# -gt 0 ]]; do case "$1" in --version) VERSION="${2:?--version requires a value}" shift 2 ;; --output-dir) OUTPUT_DIR="${2:?--output-dir requires a value}" shift 2 ;; --skip-build) SKIP_BUILD=1 shift ;; --skip-pull) SKIP_PULL=1 shift ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" >&2 usage >&2 exit 1 ;; esac done require_file() { local path="$1" local message="$2" if [[ ! -e "$path" ]]; then echo "$message" >&2 exit 1 fi } require_cmd() { local name="$1" if ! command -v "$name" >/dev/null 2>&1; then echo "Required command is missing: $name" >&2 exit 1 fi } require_cmd docker require_cmd tar SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" if [[ -z "$OUTPUT_DIR" ]]; then OUTPUT_DIR="$SCRIPT_DIR/dist/SimCAEServerDockerPackage" fi IMAGE_NAME="simcae-update-server:$VERSION" MINIO_IMAGE="minio/minio:RELEASE.2025-04-22T22-12-26Z" MC_IMAGE="minio/mc:RELEASE.2025-04-16T18-13-26Z" PACKAGE_DIR="$OUTPUT_DIR" IMAGES_DIR="$PACKAGE_DIR/images" KEYS_DIR="$PACKAGE_DIR/keys" TAR_FILE="$IMAGES_DIR/simcae-server-all-images_$VERSION.tar" ARCHIVE_FILE="$PACKAGE_DIR.tar.gz" require_file "$SCRIPT_DIR/Dockerfile" "server/Dockerfile is missing." require_file "$SCRIPT_DIR/docker-compose.image.yml" "server/docker-compose.image.yml is missing." require_file "$SCRIPT_DIR/.env.docker.example" "server/.env.docker.example is missing." require_file "$SCRIPT_DIR/keys/manifest_private_key.pem" "server/keys/manifest_private_key.pem is missing." require_file "$SCRIPT_DIR/keys/manifest_public_key.pem" "server/keys/manifest_public_key.pem is missing." if [[ "$SKIP_BUILD" -eq 0 ]]; then docker build -t "$IMAGE_NAME" -f "$SCRIPT_DIR/Dockerfile" "$PROJECT_ROOT" fi if [[ "$SKIP_PULL" -eq 0 ]]; then docker pull "$MINIO_IMAGE" docker pull "$MC_IMAGE" fi rm -rf "$PACKAGE_DIR" mkdir -p "$IMAGES_DIR" "$KEYS_DIR" cp "$SCRIPT_DIR/docker-compose.image.yml" "$PACKAGE_DIR/docker-compose.yml" cp "$SCRIPT_DIR/.env.docker.example" "$PACKAGE_DIR/.env.example" cat > "$PACKAGE_DIR/README.md" <<'EOF_README' # SimCAE 服务端 Docker 部署包说明 这个包已经包含服务端运行需要的 Docker 镜像、配置模板、签名密钥和编排文件。你的服务器无论能不能联网,都可以按下面流程部署。 ## 一、这个包里面有什么 解压后目录结构如下: ```text SimCAEServerDockerPackage/ README.md 当前说明文档 docker-compose.yml 容器编排文件 .env.example 环境变量模板 load-images.sh 导入 Docker 镜像并创建 .env 的脚本 images/ simcae-server-all-images___VERSION__.tar 三个 Docker 镜像:api、minio、minio-init keys/ manifest_private_key.pem 服务端 Manifest 签名私钥 manifest_public_key.pem 与客户端配套的验签公钥 ``` ## 二、你在服务器上怎么运行 先把 `SimCAEServerDockerPackage.tar.gz` 拷贝到要部署的 Ubuntu 服务器上,然后执行: ```bash tar -xzf SimCAEServerDockerPackage.tar.gz cd SimCAEServerDockerPackage pwd ls ``` 你需要确认当前目录就是解压后的 `SimCAEServerDockerPackage` 目录,并且能看到: ```text docker-compose.yml .env.example load-images.sh images/ keys/ ``` 后面所有 `docker compose` 命令都要在这个目录执行,因为 `docker-compose.yml` 和 `.env` 都在这里。 ## 三、导入镜像并生成 .env 在 `SimCAEServerDockerPackage` 目录执行: ```bash bash ./load-images.sh ``` 这个脚本会做两件事: ```text 1. docker load 导入 images/simcae-server-all-images___VERSION__.tar 里的镜像 2. 如果当前目录没有 .env,就从 .env.example 复制一份 .env ``` ## 四、修改 .env 继续在 `SimCAEServerDockerPackage` 目录执行: ```bash nano .env ``` `.env.example` 里已经填好一组可直接试跑的默认 token 和 MinIO 账号。你通常只需要先改服务器地址: ```text MINIO_PUBLIC_ENDPOINT=http://你的服务器IP:9000 ``` 正式部署时,建议同时修改这些默认值,避免所有环境共用同一套公开示例密码: ```text CLIENT_API_TOKEN=客户端 API token,对应客户端 app_config.json 的 client_token ADMIN_TOKEN=后台管理员 token,网页登录时填这个值 CRASH_REPORT_TOKEN=崩溃上传 token CRASH_SYMBOL_TOKEN=符号上传 token MINIO_ACCESS_KEY=MinIO 用户名 MINIO_SECRET_KEY=MinIO 密码 ``` 如果你在网页里点击“更改令牌”,新令牌只会在当前服务进程中立即生效。为了让服务重启后仍然使用新令牌,请同步修改 `.env` 里的 `ADMIN_TOKEN`,然后执行 `docker compose restart api`。 `MINIO_PUBLIC_ENDPOINT` 必须是 Windows 客户端能访问到的地址。比如服务器 IP 是 `192.168.229.128`,就填: ```text MINIO_PUBLIC_ENDPOINT=http://192.168.229.128:9000 ``` ## 五、启动服务 确认你仍然在 `SimCAEServerDockerPackage` 目录: ```bash pwd ``` 然后启动: ```bash docker compose up -d ``` 如果提示 `docker: 'compose' is not a docker command`,说明服务器没有安装 Docker Compose plugin,需要先安装它。 ## 六、查看状态和日志 这些命令也都在 `SimCAEServerDockerPackage` 目录执行: ```bash docker compose ps docker compose logs -f api ``` 正常情况下可以看到 `api`、`minio` 处于 running/healthy 状态。 ## 七、访问地址 把下面的 `你的服务器IP` 换成真实服务器 IP: ```text 后台/API: http://你的服务器IP:8000/ MinIO 控制台: http://你的服务器IP:9001/ ``` 如果服务器开启防火墙,至少放行: ```text 8000 后台/API 9000 客户端下载升级文件 9001 MinIO 控制台,可选 ``` ## 八、重要提醒 `keys/manifest_private_key.pem` 是服务端签 Manifest 的私钥,必须保护好。客户端 SDK 里的 `manifest_public_key.pem` 必须和这个私钥配套,否则客户端会 Manifest 验签失败。 EOF_README python3 - "$PACKAGE_DIR/README.md" "$VERSION" <<'PYREADME' from pathlib import Path import sys path = Path(sys.argv[1]) version = sys.argv[2] text = path.read_text(encoding='utf-8').replace('__VERSION__', version) path.write_text(text, encoding='utf-8') PYREADME cat > "$PACKAGE_DIR/load-images.sh" <