#!/bin/sh # shellcheck shell=sh set -e TS() { date "+%Y-%m-%d %H:%M:%S"; } log() { echo "[$(TS)] [entrypoint] $*"; } # ----------------------------------------------------------------------------- # 基础目录 # ----------------------------------------------------------------------------- mkdir -p /app/logs /app/data /app/temp # Redis 数据目录放在 /app/data/redis,这样能随备份一起保存/恢复 REDIS_DIR="${REDIS_DIR:-/app/data/redis}" mkdir -p "${REDIS_DIR}" # ----------------------------------------------------------------------------- # 导出备份相关环境变量(给子进程可见) # ----------------------------------------------------------------------------- export HF_TOKEN="${HF_TOKEN:-}" export DATASET_ID="${DATASET_ID:-}" export SYNC_INTERVAL MAX_BACKUPS BACKUP_PREFIX BACKUP_PATHS BACKUP_EXCLUDE # 若未指定,给出对持久化友好的默认值(相对路径,配合 tar -C /) BACKUP_PATHS="${BACKUP_PATHS:-app/data,app/data/redis}" BACKUP_EXCLUDE="${BACKUP_EXCLUDE:-*.tmp,*.cache,**/.DS_Store}" export BACKUP_PATHS BACKUP_EXCLUDE # ----------------------------------------------------------------------------- # 在任何服务启动前先尝试恢复数据(包含 Redis 的 RDB/AOF) # ----------------------------------------------------------------------------- if [ -n "$HF_TOKEN" ] && [ -n "$DATASET_ID" ]; then /app/tools/hf-backup.sh restore || log "Restore skipped/failed, continue." else log "未配置 HF_TOKEN/DATASET_ID,跳过恢复。" fi # ----------------------------------------------------------------------------- # 启动(内置或外置)Redis # ----------------------------------------------------------------------------- REDIS_HOST="${REDIS_HOST:-127.0.0.1}" REDIS_PORT="${REDIS_PORT:-6379}" start_internal_redis="false" case "$REDIS_HOST" in ""|"127.0.0.1"|"localhost") start_internal_redis="true" ;; esac if [ "$start_internal_redis" = "true" ]; then log "Starting embedded Redis on 127.0.0.1:${REDIS_PORT} ..." if [ -n "${REDIS_PASSWORD:-}" ]; then redis-server \ --port "${REDIS_PORT}" --bind 127.0.0.1 \ --dir "${REDIS_DIR}" \ --dbfilename dump.rdb \ --appendonly yes --appendfilename appendonly.aof \ --appendfsync everysec \ --save 60 1 \ --requirepass "${REDIS_PASSWORD}" & else redis-server \ --port "${REDIS_PORT}" --bind 127.0.0.1 \ --dir "${REDIS_DIR}" \ --dbfilename dump.rdb \ --appendonly yes --appendfilename appendonly.aof \ --appendfsync everysec \ --save 60 1 & fi else log "Using EXTERNAL Redis at ${REDIS_HOST}:${REDIS_PORT}, skip embedded Redis." fi # 等待 Redis 就绪 log "Waiting for Redis on ${REDIS_HOST}:${REDIS_PORT} ..." i=0 while :; do if [ -n "${REDIS_PASSWORD:-}" ]; then if redis-cli -h "${REDIS_HOST}" -p "${REDIS_PORT}" -a "${REDIS_PASSWORD}" ping >/dev/null 2>&1; then break fi else if redis-cli -h "${REDIS_HOST}" -p "${REDIS_PORT}" ping >/dev/null 2>&1; then break fi fi i=$((i+1)) if [ "$i" -ge 60 ]; then log "Redis not ready after 60s, exit." exit 1 fi sleep 1 done log "Redis is ready." # ----------------------------------------------------------------------------- # 备份守护进程(可选:开机先做一份一次性备份) # BACKUP_ON_START=true 时,先执行一次上传,随后开启守护 # ----------------------------------------------------------------------------- if [ -n "$HF_TOKEN" ] && [ -n "$DATASET_ID" ]; then if [ "${BACKUP_ON_START:-false}" = "true" ]; then /app/tools/hf-backup.sh once || log "First backup failed (ignored)." fi # 同时输出到控制台和日志文件,便于在 HF 控制台直接看到 /app/tools/hf-backup.sh daemon 2>&1 | tee -a /app/logs/hf-backup.log & log "启动 HF Dataset 备份服务(间隔 ${SYNC_INTERVAL:-3600}s,保留 ${MAX_BACKUPS:-10} 份)" else log "未配置 HF_TOKEN/DATASET_ID,跳过备份守护进程。" fi # ----------------------------------------------------------------------------- # 可选:强制重置管理员(会删除 init.json) # ----------------------------------------------------------------------------- if [ "${FORCE_ADMIN_RESET:-false}" = "true" ]; then log "FORCE_ADMIN_RESET=true, remove /app/data/init.json" rm -f /app/data/init.json fi # ----------------------------------------------------------------------------- # 初始化管理员 / 应用准备 # ----------------------------------------------------------------------------- if [ -n "${ADMIN_USERNAME:-}" ] && [ -n "${ADMIN_PASSWORD:-}" ]; then log "Bootstrapping admin user: ${ADMIN_USERNAME}" else log "ADMIN_USERNAME/ADMIN_PASSWORD 未设置,将按已有 init.json 或默认逻辑处理。" fi npm run setup # ----------------------------------------------------------------------------- # 启动应用 # ----------------------------------------------------------------------------- HOST="${HOST:-0.0.0.0}" PORT="${PORT:-7860}" log "Starting app on ${HOST}:${PORT} ..." exec node src/app.js