#!/usr/bin/env bash set -euo pipefail REMOTE_DIR="${REMOTE_DIR:-/opt/hyb-oa-trialrun}" REMOTE_ENV_FILE="${REMOTE_ENV_FILE:-${REMOTE_DIR}/deploy/trialrun/.env}" COMPOSE_FILE="${COMPOSE_FILE:-deploy/trialrun/docker-compose.yml}" COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-hyb-oa-trialrun}" POSTGRES_DATA_DIR="${POSTGRES_DATA_DIR:-}" STORAGE_HOST_DIR="${STORAGE_HOST_DIR:-}" BUILD_SHA="${BUILD_SHA:-}" VITE_BUILD_SHA="${VITE_BUILD_SHA:-}" SERVICE_NAME="${SERVICE_NAME:-hyb-oa-warch3-ops}" RUN_AT="${RUN_AT:-07:30}" MODE="${1:-install}" SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" TIMER_FILE="/etc/systemd/system/${SERVICE_NAME}.timer" need_root() { if [[ "$(id -u)" != "0" ]]; then echo "[ERROR] 请以 root 执行,或使用 sudo" >&2 exit 1 fi } validate_data_path() { local name="$1" local value="$2" if [[ -z "$value" || "$value" != /* || ! "$value" =~ ^/[A-Za-z0-9._/-]+$ ]]; then echo "[ERROR] $name 必须显式提供受限字符集的绝对路径" >&2 exit 1 fi if [[ "$value" == "/" || "$value" == */ || "$value" == *"//"* \ || "/$value/" == *"/../"* || "/$value/" == *"/./"* ]]; then echo "[ERROR] $name 不得是根目录、尾随斜杠或包含空、.、.. 路径段" >&2 exit 1 fi } uninstall_timer() { need_root systemctl disable --now "${SERVICE_NAME}.timer" >/dev/null 2>&1 || true systemctl stop "${SERVICE_NAME}.service" >/dev/null 2>&1 || true rm -f "$SERVICE_FILE" "$TIMER_FILE" systemctl daemon-reload echo "[warch3-ops-timer] uninstalled ${SERVICE_NAME}" } install_timer() { need_root if [[ ! -d "$REMOTE_DIR" ]]; then echo "[ERROR] REMOTE_DIR 不存在: $REMOTE_DIR" >&2 exit 1 fi if [[ ! -f "$REMOTE_ENV_FILE" ]]; then echo "[ERROR] 远端 env 不存在: $REMOTE_ENV_FILE" >&2 exit 1 fi if [[ ! "$COMPOSE_PROJECT_NAME" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then echo "[ERROR] COMPOSE_PROJECT_NAME 格式无效" >&2 exit 1 fi validate_data_path POSTGRES_DATA_DIR "$POSTGRES_DATA_DIR" validate_data_path STORAGE_HOST_DIR "$STORAGE_HOST_DIR" if [[ "$POSTGRES_DATA_DIR" == "$STORAGE_HOST_DIR" \ || "$POSTGRES_DATA_DIR" == "$STORAGE_HOST_DIR"/* \ || "$STORAGE_HOST_DIR" == "$POSTGRES_DATA_DIR"/* ]]; then echo "[ERROR] PostgreSQL 与附件宿主目录必须相互独立" >&2 exit 1 fi if [[ ! -f "$POSTGRES_DATA_DIR/PG_VERSION" ]]; then echo "[ERROR] POSTGRES_DATA_DIR 必须是包含 PG_VERSION 的现存 PGDATA" >&2 exit 1 fi if [[ ! -d "$STORAGE_HOST_DIR" ]]; then echo "[ERROR] STORAGE_HOST_DIR 必须是现存目录" >&2 exit 1 fi if [[ ! "$BUILD_SHA" =~ ^[0-9a-fA-F]{40,64}$ ]]; then echo "[ERROR] BUILD_SHA 必须是 40-64 位十六进制完整提交标识" >&2 exit 1 fi BUILD_SHA="${BUILD_SHA,,}" VITE_BUILD_SHA="${VITE_BUILD_SHA,,}" if [[ ! "$VITE_BUILD_SHA" =~ ^[0-9a-f]{12}$ || "${BUILD_SHA:0:12}" != "$VITE_BUILD_SHA" ]]; then echo "[ERROR] VITE_BUILD_SHA 必须是 BUILD_SHA 的 12 位十六进制前缀" >&2 exit 1 fi local compose_exec if docker compose version >/dev/null 2>&1; then compose_exec="docker compose" elif command -v docker-compose >/dev/null 2>&1; then compose_exec="docker-compose" else echo "[ERROR] 远端既没有 docker compose,也没有 docker-compose" >&2 exit 1 fi cat >"$SERVICE_FILE" <"$TIMER_FILE" < POSTGRES_DATA_DIR= STORAGE_HOST_DIR= BUILD_SHA= VITE_BUILD_SHA=<12-char-prefix> bash deploy/trialrun/install-warch3-ops-timer.sh [install|uninstall]" ;; *) echo "[ERROR] unsupported mode: $MODE" >&2 exit 2 ;; esac