Ctrl K
ComunidadGuías…

scripts/import-manifest.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
用法:
  IMAGESILO_BASE_URL=https://img.example.com \
  IMAGESILO_TOKEN=ist_xxx \
  scripts/import-manifest.sh --manifest ./imports.tsv --output ./imports-result.jsonl [--visibility public]

连接配置:
  IMAGESILO_BASE_URL  ImageSilo 根地址,不带结尾斜杠
  IMAGESILO_TOKEN     同时具备 images:upload 与 aliases:write Scope 的 API Token

运行参数:
  --manifest PATH    TSV 清单:每行“旧 URL path<TAB>本地图片路径”
  --output PATH      新建 JSONL 结果文件;为防止误覆盖,已存在时拒绝运行
  --visibility VALUE public 或 private,默认 public

脚本严格串行导入,并在每项成功后访问旧 URL、重新计算 SHA-256。
EOF
}

manifest=""
output=""
visibility="public"
while (($# > 0)); do
  case "$1" in
    --manifest)
      manifest="${2:-}"
      shift 2
      ;;
    --output)
      output="${2:-}"
      shift 2
      ;;
    --visibility)
      visibility="${2:-}"
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      printf '未知参数:%s\n' "$1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

base_url="${IMAGESILO_BASE_URL:-}"
token="${IMAGESILO_TOKEN:-}"
if [[ -z "$base_url" || -z "$token" || -z "$manifest" || -z "$output" ]]; then
  printf '缺少连接配置或必填参数。\n' >&2
  usage >&2
  exit 2
fi
if [[ "$visibility" != "public" && "$visibility" != "private" ]]; then
  printf 'visibility 只能是 public 或 private。\n' >&2
  exit 2
fi
if [[ ! -f "$manifest" ]]; then
  printf '清单不存在:%s\n' "$manifest" >&2
  exit 2
fi
if [[ -e "$output" ]]; then
  printf '结果文件已存在,拒绝覆盖:%s\n' "$output" >&2
  exit 2
fi
for command_name in curl jq; do
  if ! command -v "$command_name" >/dev/null 2>&1; then
    printf '缺少命令:%s\n' "$command_name" >&2
    exit 2
  fi
done

base_url="${base_url%/}"
temporary_directory="$(mktemp -d "${TMPDIR:-/tmp}/imagesilo-import.XXXXXX")"
trap 'rm -rf "$temporary_directory"' EXIT INT TERM
header_file="$temporary_directory/authorization-header"
printf 'Authorization: Bearer %s\n' "$token" >"$header_file"
chmod 600 "$header_file"
: >"$output"

hash_file() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
  else
    shasum -a 256 "$1" | awk '{print $1}'
  fi
}

successes=0
conflicts=0
errors=0
line_number=0
while IFS=$'\t' read -r alias_path image_path extra || [[ -n "${alias_path}${image_path}${extra}" ]]; do
  line_number=$((line_number + 1))
  if [[ -z "${alias_path// }" || "${alias_path:0:1}" == "#" ]]; then
    continue
  fi
  if [[ -z "$image_path" || -n "$extra" || ! -f "$image_path" ]]; then
    printf '第 %d 行格式错误或图片不存在。\n' "$line_number" >&2
    errors=$((errors + 1))
    jq -cn --argjson line "$line_number" --arg alias "$alias_path" --arg file "$image_path" \
      '{line:$line, alias:$alias, file:$file, status:"invalid_manifest"}' >>"$output"
    continue
  fi

  response_file="$temporary_directory/response-${line_number}.json"
  delivered_file="$temporary_directory/delivered-${line_number}"
  : >"$response_file"
  source_hash="$(hash_file "$image_path")"
  http_status="$(curl --silent --show-error \
    --output "$response_file" \
    --write-out '%{http_code}' \
    --header "@${header_file}" \
    --form "file=@${image_path}" \
    --form "alias=${alias_path}" \
    --form "visibility=${visibility}" \
    "${base_url}/api/v1/imports")" || http_status="000"

  if [[ "$http_status" == "201" ]]; then
    returned_hash="$(jq --raw-output '.sha256 // empty' "$response_file")"
    returned_alias="$(jq --raw-output '.alias.path // empty' "$response_file")"
    if [[ "$returned_hash" != "$source_hash" || "$returned_alias" != "$alias_path" ]]; then
      printf '第 %d 行响应校验失败:%s\n' "$line_number" "$alias_path" >&2
      errors=$((errors + 1))
      status="response_mismatch"
    elif ! curl --fail --silent --show-error --header "@${header_file}" --output "$delivered_file" "${base_url}${alias_path}"; then
      printf '第 %d 行旧 URL 访问失败:%s\n' "$line_number" "$alias_path" >&2
      errors=$((errors + 1))
      status="delivery_failed"
    elif [[ "$(hash_file "$delivered_file")" != "$source_hash" ]]; then
      printf '第 %d 行旧 URL 字节校验失败:%s\n' "$line_number" "$alias_path" >&2
      errors=$((errors + 1))
      status="hash_mismatch"
    else
      successes=$((successes + 1))
      status="imported"
      printf '已导入并验证:%s\n' "$alias_path"
    fi
  elif [[ "$http_status" == "409" ]]; then
    conflicts=$((conflicts + 1))
    status="conflict"
    printf '路径冲突:%s\n' "$alias_path" >&2
  else
    errors=$((errors + 1))
    status="error"
    printf '导入失败(HTTP %s):%s\n' "$http_status" "$alias_path" >&2
  fi

  jq -cn \
    --argjson line "$line_number" \
    --arg alias "$alias_path" \
    --arg file "$image_path" \
    --arg sourceSha256 "$source_hash" \
    --arg status "$status" \
    --arg httpStatus "$http_status" \
    --rawfile response "$response_file" \
    '{line:$line, alias:$alias, file:$file, sourceSha256:$sourceSha256, status:$status, httpStatus:$httpStatus, response:$response}' \
    >>"$output"
done <"$manifest"

printf '导入完成:成功=%d,冲突=%d,错误=%d,结果=%s\n' "$successes" "$conflicts" "$errors" "$output"
if ((conflicts > 0 || errors > 0)); then
  exit 1
fi