feat: add ImageFind application and release pipelines
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
".url": {
|
||||
"imagefind.Application": {
|
||||
"title": "ImageFind",
|
||||
"icon": "images/icon_{0}.png",
|
||||
"type": "url",
|
||||
"protocol": "",
|
||||
"url": "/app/imagefind/",
|
||||
"gatewayPrefix": "/app/imagefind",
|
||||
"gatewaySocket": "imagefind.sock",
|
||||
"allUsers": false,
|
||||
"control": {
|
||||
"accessPerm": "readonly"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
DATA_ROOT="${TRIM_PKGVAR:-}"
|
||||
ENABLED="${wizard_direct_access:-false}"
|
||||
PORT="${wizard_direct_port:-8765}"
|
||||
unset wizard_direct_access wizard_direct_port
|
||||
|
||||
fail() {
|
||||
MESSAGE="$1"
|
||||
printf '%s\n' "$MESSAGE" >&2
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
printf '%s\n' "$MESSAGE" >>"$TRIM_TEMP_LOGFILE" 2>/dev/null || true
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -n "$DATA_ROOT" ] || fail "ImageFind 配置失败:fnOS 未提供应用数据目录。"
|
||||
case "$ENABLED" in
|
||||
true|false) ;;
|
||||
*) fail "ImageFind 配置失败:直接访问开关无效。" ;;
|
||||
esac
|
||||
case "$PORT" in
|
||||
''|*[!0-9]*) fail "ImageFind 配置失败:端口必须是数字。" ;;
|
||||
esac
|
||||
[ "$PORT" -ge 1 ] && [ "$PORT" -le 65535 ] || fail "ImageFind 配置失败:端口必须在 1–65535 之间。"
|
||||
|
||||
mkdir -p "$DATA_ROOT"
|
||||
ACCESS_TEMP="$DATA_ROOT/access.json.tmp.$$"
|
||||
trap 'rm -f "$ACCESS_TEMP"' EXIT
|
||||
printf '{"direct_access":{"enabled":%s,"port":%s}}\n' "$ENABLED" "$PORT" >"$ACCESS_TEMP"
|
||||
chmod 0600 "$ACCESS_TEMP"
|
||||
mv "$ACCESS_TEMP" "$DATA_ROOT/access.json"
|
||||
trap - EXIT
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
DATA_ROOT="${TRIM_PKGVAR:-}"
|
||||
APP_ROOT="${TRIM_APPDEST:-}"
|
||||
COMMAND_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
PASSWORD="${wizard_admin_password:-}"
|
||||
PASSWORD_CONFIRM="${wizard_admin_password_confirm:-}"
|
||||
POSTGRES_TOKEN="${wizard_postgres_enrollment_token:-}"
|
||||
unset wizard_admin_password wizard_admin_password_confirm wizard_postgres_enrollment_token
|
||||
|
||||
fail() {
|
||||
MESSAGE="$1"
|
||||
printf '%s\n' "$MESSAGE" >&2
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
printf '%s\n' "$MESSAGE" >>"$TRIM_TEMP_LOGFILE" 2>/dev/null || true
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -n "$DATA_ROOT" ] || fail "ImageFind 初始化失败:fnOS 未提供应用数据目录。"
|
||||
[ -n "$APP_ROOT" ] || fail "ImageFind 初始化失败:fnOS 未提供应用安装目录。"
|
||||
[ "$PASSWORD" = "$PASSWORD_CONFIRM" ] || fail "ImageFind 初始化失败:两次输入的管理员密码不一致。"
|
||||
[ "${#PASSWORD}" -ge 10 ] || fail "ImageFind 初始化失败:管理员密码至少需要 10 个字符。"
|
||||
[ "${#PASSWORD}" -le 256 ] || fail "ImageFind 初始化失败:管理员密码不能超过 256 个字符。"
|
||||
[ "${#POSTGRES_TOKEN}" -ge 20 ] || fail "ImageFind 初始化失败:PostgreSQL 应用接入令牌至少需要 20 个字符。"
|
||||
[ "${#POSTGRES_TOKEN}" -le 256 ] || fail "ImageFind 初始化失败:PostgreSQL 应用接入令牌不能超过 256 个字符。"
|
||||
case "$POSTGRES_TOKEN" in
|
||||
*'
|
||||
'*) fail "ImageFind 初始化失败:PostgreSQL 应用接入令牌不能包含换行。" ;;
|
||||
esac
|
||||
[ -x "$COMMAND_ROOT/runtime_bootstrap" ] || fail "ImageFind 初始化失败:找不到运行环境初始化程序。"
|
||||
|
||||
mkdir -p "$DATA_ROOT/data" "$DATA_ROOT/cache"
|
||||
printf '%s\n' 'imagefind-data-v1' >"$DATA_ROOT/.imagefind-data-root"
|
||||
if [ ! -f "$DATA_ROOT/access.json" ]; then
|
||||
ACCESS_TEMP="$DATA_ROOT/access.json.tmp.$$"
|
||||
printf '%s\n' '{"direct_access":{"enabled":false,"port":8765}}' >"$ACCESS_TEMP"
|
||||
chmod 0600 "$ACCESS_TEMP"
|
||||
mv "$ACCESS_TEMP" "$DATA_ROOT/access.json"
|
||||
fi
|
||||
export IMAGEFIND_DATA_DIR="$DATA_ROOT/data"
|
||||
export XDG_CACHE_HOME="$DATA_ROOT/cache"
|
||||
|
||||
if ! TRIM_APPDEST="$APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" "$COMMAND_ROOT/runtime_bootstrap" \
|
||||
>>"${TRIM_TEMP_LOGFILE:-/dev/null}" 2>&1; then
|
||||
fail "ImageFind 初始化失败:Python 3.12 核心运行环境安装失败。"
|
||||
fi
|
||||
PYTHON="$DATA_ROOT/runtime/current/bin/python"
|
||||
TOKEN_SEED="$DATA_ROOT/postgres-enrollment-token.seed"
|
||||
TOKEN_TEMP="$DATA_ROOT/.postgres-enrollment-token.seed.tmp.$$"
|
||||
printf '%s' "$POSTGRES_TOKEN" >"$TOKEN_TEMP"
|
||||
chmod 0600 "$TOKEN_TEMP"
|
||||
mv "$TOKEN_TEMP" "$TOKEN_SEED"
|
||||
if ! "$PYTHON" -m imagefind.main postgres-enroll \
|
||||
>>"${TRIM_TEMP_LOGFILE:-/dev/null}" 2>&1; then
|
||||
fail "ImageFind 初始化失败:无法接入 PostgreSQL 共享服务,请确认 nxsir.postgresql 已启动且接入令牌正确。"
|
||||
fi
|
||||
if ! printf '%s' "$PASSWORD" | "$PYTHON" -m imagefind.main admin-password --stdin --replace \
|
||||
>>"${TRIM_TEMP_LOGFILE:-/dev/null}" 2>&1; then
|
||||
fail "ImageFind 初始化失败:无法保存管理员密码。"
|
||||
fi
|
||||
|
||||
unset PASSWORD PASSWORD_CONFIRM POSTGRES_TOKEN
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Pre-install runs before fnOS has guaranteed that TRIM_PKGVAR exists and is
|
||||
# writable by the package account. Runtime startup creates its own data tree.
|
||||
exit 0
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
PACKAGE_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
APP_ROOT="${TRIM_APPDEST:-$PACKAGE_ROOT/app}"
|
||||
DATA_ROOT="${TRIM_PKGVAR:-$PACKAGE_ROOT/var}"
|
||||
RUN_ROOT="$DATA_ROOT/run"
|
||||
LOG_ROOT="$DATA_ROOT/log"
|
||||
PID_FILE="$RUN_ROOT/imagefind.pid"
|
||||
LOG_FILE="$LOG_ROOT/imagefind.log"
|
||||
ACCESS_CONFIG="$DATA_ROOT/access.json"
|
||||
GATEWAY_SOCKET="$APP_ROOT/imagefind.sock"
|
||||
|
||||
mkdir -p "$RUN_ROOT" "$LOG_ROOT" "$DATA_ROOT/data" "$DATA_ROOT/tmp"
|
||||
|
||||
is_running() {
|
||||
[ -f "$PID_FILE" ] || return 1
|
||||
PID=$(sed -n '1p' "$PID_FILE")
|
||||
[ -n "$PID" ] && kill -0 "$PID" 2>/dev/null
|
||||
}
|
||||
|
||||
report_start_failure() {
|
||||
MESSAGE="ImageFind failed to start. Runtime log: $LOG_FILE"
|
||||
printf '%s\n' "$MESSAGE" >&2
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
{
|
||||
printf '%s\n' "$MESSAGE"
|
||||
printf '%s\n' '--- imagefind.log (last 100 lines) ---'
|
||||
tail -n 100 "$LOG_FILE" 2>/dev/null || true
|
||||
} >>"$TRIM_TEMP_LOGFILE" 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
start_app() {
|
||||
if is_running; then
|
||||
return 0
|
||||
fi
|
||||
rm -f "$PID_FILE"
|
||||
if ! TRIM_APPDEST="$APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" "$PACKAGE_ROOT/cmd/runtime_bootstrap" \
|
||||
>>"$LOG_FILE" 2>&1; then
|
||||
report_start_failure
|
||||
return 1
|
||||
fi
|
||||
SERVER="$DATA_ROOT/runtime/current/bin/python"
|
||||
export IMAGEFIND_DATA_DIR="$DATA_ROOT/data"
|
||||
export IMAGEFIND_FRONTEND_DIR="$APP_ROOT/frontend"
|
||||
export IMAGEFIND_BUNDLED_AI_RUNTIME=false
|
||||
export IMAGEFIND_RUNTIME_REQUIREMENTS_DIR="$APP_ROOT/runtime/ai"
|
||||
export IMAGEFIND_BUNDLED_TOOLS_DIR="$APP_ROOT/bin"
|
||||
export IMAGEFIND_POSTGRES_CONF="$DATA_ROOT/postgres-client.conf"
|
||||
export IMAGEFIND_POSTGRES_ENROLLMENT_TOKEN_SEED="$DATA_ROOT/postgres-enrollment-token.seed"
|
||||
export PYTHONPATH="$DATA_ROOT/runtime/ai-current${PYTHONPATH:+:$PYTHONPATH}"
|
||||
export XDG_CACHE_HOME="$DATA_ROOT/cache"
|
||||
# Model conversion can temporarily exceed the small system /tmp volume.
|
||||
# Keep all large, recoverable conversion files on the application data volume.
|
||||
export TMPDIR="$DATA_ROOT/tmp"
|
||||
DIRECT_ENABLED=true
|
||||
DIRECT_PORT=8765
|
||||
if [ -f "$ACCESS_CONFIG" ]; then
|
||||
CONFIG_ENABLED=$(sed -n 's/.*"enabled"[[:space:]]*:[[:space:]]*\(true\|false\).*/\1/p' "$ACCESS_CONFIG" | head -n 1)
|
||||
CONFIG_PORT=$(sed -n 's/.*"port"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$ACCESS_CONFIG" | head -n 1)
|
||||
[ -z "$CONFIG_ENABLED" ] || DIRECT_ENABLED="$CONFIG_ENABLED"
|
||||
[ -z "$CONFIG_PORT" ] || DIRECT_PORT="$CONFIG_PORT"
|
||||
fi
|
||||
export IMAGEFIND_HOST="0.0.0.0"
|
||||
export IMAGEFIND_PORT="$DIRECT_PORT"
|
||||
export IMAGEFIND_DIRECT_ACCESS="$DIRECT_ENABLED"
|
||||
export IMAGEFIND_GATEWAY_PREFIX="/app/imagefind"
|
||||
export IMAGEFIND_GATEWAY_SOCKET="$GATEWAY_SOCKET"
|
||||
# Keep CPU fallback responsive on a NAS. Administrators can override any
|
||||
# value before launch, while the default avoids saturating every core.
|
||||
export IMAGEFIND_AI_CPU_THREADS="${IMAGEFIND_AI_CPU_THREADS:-1}"
|
||||
export OMP_NUM_THREADS="${OMP_NUM_THREADS:-$IMAGEFIND_AI_CPU_THREADS}"
|
||||
export OPENBLAS_NUM_THREADS="${OPENBLAS_NUM_THREADS:-$IMAGEFIND_AI_CPU_THREADS}"
|
||||
export MKL_NUM_THREADS="${MKL_NUM_THREADS:-$IMAGEFIND_AI_CPU_THREADS}"
|
||||
export LD_LIBRARY_PATH="$APP_ROOT/bin${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
if [ ! -f "$IMAGEFIND_POSTGRES_CONF" ] && [ -f "$IMAGEFIND_POSTGRES_ENROLLMENT_TOKEN_SEED" ]; then
|
||||
if ! "$SERVER" -m imagefind.main postgres-enroll >>"$LOG_FILE" 2>&1; then
|
||||
report_start_failure
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
nohup "$SERVER" -m imagefind.main >>"$LOG_FILE" 2>&1 &
|
||||
printf '%s\n' "$!" > "$PID_FILE"
|
||||
sleep 1
|
||||
if ! is_running; then
|
||||
report_start_failure
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop_pid() {
|
||||
FILE="$1"
|
||||
[ -f "$FILE" ] || return 0
|
||||
PID=$(sed -n '1p' "$FILE")
|
||||
if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
|
||||
kill "$PID" 2>/dev/null || true
|
||||
COUNT=0
|
||||
while kill -0 "$PID" 2>/dev/null && [ "$COUNT" -lt 20 ]; do
|
||||
sleep 1
|
||||
COUNT=$((COUNT + 1))
|
||||
done
|
||||
kill -9 "$PID" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "$FILE"
|
||||
}
|
||||
|
||||
case "${1:-status}" in
|
||||
start) start_app ;;
|
||||
stop) stop_pid "$PID_FILE"; rm -f "$GATEWAY_SOCKET" ;;
|
||||
restart) "$0" stop; "$0" start ;;
|
||||
status)
|
||||
if is_running; then
|
||||
exit 0
|
||||
fi
|
||||
# fnOS reserves 3 for a valid status check whose service is stopped.
|
||||
exit 3
|
||||
;;
|
||||
*) printf 'usage: %s {start|stop|restart|status}\n' "$0" >&2; exit 2 ;;
|
||||
esac
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
PACKAGE_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
APP_ROOT="${TRIM_APPDEST:-$PACKAGE_ROOT/app}"
|
||||
DATA_ROOT="${TRIM_PKGVAR:-$PACKAGE_ROOT/var}"
|
||||
VERSION_FILE="$APP_ROOT/runtime/VERSION"
|
||||
WHEEL_ROOT="$APP_ROOT/runtime/wheels"
|
||||
CORE_REQUIREMENTS="$APP_ROOT/runtime/runtime-core.txt"
|
||||
RUNTIME_ROOT="$DATA_ROOT/runtime"
|
||||
|
||||
fail() {
|
||||
printf '%s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -f "$VERSION_FILE" ] || fail "ImageFind 运行环境初始化失败:安装包缺少版本信息。"
|
||||
[ -d "$WHEEL_ROOT" ] || fail "ImageFind 运行环境初始化失败:安装包缺少核心 wheelhouse。"
|
||||
[ -f "$CORE_REQUIREMENTS" ] || fail "ImageFind 运行环境初始化失败:安装包缺少核心依赖锁文件。"
|
||||
|
||||
VERSION=$(sed -n '1p' "$VERSION_FILE")
|
||||
case "$VERSION" in
|
||||
''|*[!0-9.]* ) fail "ImageFind 运行环境初始化失败:版本信息无效。" ;;
|
||||
esac
|
||||
PACKAGE_ID=$(sed -n '2p' "$VERSION_FILE")
|
||||
case "$PACKAGE_ID" in
|
||||
''|*[!0-9a-f]* ) fail "ImageFind 运行环境初始化失败:安装包指纹无效。" ;;
|
||||
esac
|
||||
[ "${#PACKAGE_ID}" -eq 64 ] || fail "ImageFind 运行环境初始化失败:安装包指纹长度无效。"
|
||||
|
||||
if [ -n "${IMAGEFIND_PYTHON_PATH:-}" ]; then
|
||||
SYSTEM_PYTHON="$IMAGEFIND_PYTHON_PATH"
|
||||
else
|
||||
SYSTEM_PYTHON="/var/apps/python312/target/bin/python3"
|
||||
fi
|
||||
[ -x "$SYSTEM_PYTHON" ] || fail "ImageFind 需要 fnOS Python 3.12 依赖,请先确认 python312 已安装。"
|
||||
PYTHON_VERSION=$($SYSTEM_PYTHON -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null) \
|
||||
|| fail "ImageFind 无法运行 fnOS Python 3.12。"
|
||||
[ "$PYTHON_VERSION" = "3.12" ] || fail "ImageFind 需要 Python 3.12,当前为 $PYTHON_VERSION。"
|
||||
|
||||
mkdir -p "$RUNTIME_ROOT"
|
||||
TARGET="$RUNTIME_ROOT/core-$VERSION"
|
||||
STAGE="$RUNTIME_ROOT/.core-$VERSION.tmp.$$"
|
||||
CURRENT="$RUNTIME_ROOT/current"
|
||||
|
||||
runtime_valid() {
|
||||
[ -x "$1/bin/python" ] || return 1
|
||||
[ -f "$1/.imagefind-package-id" ] || return 1
|
||||
[ "$(sed -n '1p' "$1/.imagefind-package-id")" = "$PACKAGE_ID" ] || return 1
|
||||
RUNTIME_VERSION=$(
|
||||
IMAGEFIND_DATA_DIR="$DATA_ROOT/data" "$1/bin/python" -c \
|
||||
'import imagefind; print(imagefind.__version__)' 2>"$RUNTIME_ROOT/version-check.err"
|
||||
) || return 1
|
||||
[ "$RUNTIME_VERSION" = "$VERSION" ]
|
||||
}
|
||||
|
||||
if ! runtime_valid "$TARGET"; then
|
||||
rm -rf "$STAGE"
|
||||
"$SYSTEM_PYTHON" -m venv "$STAGE" \
|
||||
|| { rm -rf "$STAGE"; fail "ImageFind 无法创建 Python 3.12 核心运行环境。"; }
|
||||
APP_WHEEL=""
|
||||
for candidate in "$APP_ROOT"/runtime/imagefind-*.whl; do
|
||||
if [ -f "$candidate" ]; then
|
||||
APP_WHEEL="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -n "$APP_WHEEL" ] \
|
||||
|| { rm -rf "$STAGE"; fail "ImageFind 运行环境初始化失败:安装包缺少应用 wheel。"; }
|
||||
if ! "$STAGE/bin/python" -m pip install \
|
||||
--disable-pip-version-check --no-input --no-index \
|
||||
--find-links "$WHEEL_ROOT" --requirement "$CORE_REQUIREMENTS" "$APP_WHEEL"; then
|
||||
rm -rf "$STAGE"
|
||||
fail "ImageFind 核心运行环境离线安装失败,请重新安装应用。"
|
||||
fi
|
||||
printf '%s\n' "$PACKAGE_ID" >"$STAGE/.imagefind-package-id"
|
||||
runtime_valid "$STAGE" \
|
||||
|| {
|
||||
if [ -s "$RUNTIME_ROOT/version-check.err" ]; then
|
||||
cat "$RUNTIME_ROOT/version-check.err" >&2 || true
|
||||
fi
|
||||
rm -rf "$STAGE"
|
||||
fail "ImageFind 核心运行环境校验失败。"
|
||||
}
|
||||
if [ -e "$TARGET" ]; then
|
||||
BACKUP="$RUNTIME_ROOT/core-$VERSION.invalid.$(date +%s)"
|
||||
mv "$TARGET" "$BACKUP"
|
||||
fi
|
||||
mv "$STAGE" "$TARGET"
|
||||
fi
|
||||
|
||||
LINK="$RUNTIME_ROOT/.current.$$"
|
||||
rm -f "$LINK"
|
||||
ln -s "$TARGET" "$LINK"
|
||||
if [ -d "$CURRENT" ] && [ ! -L "$CURRENT" ]; then
|
||||
mv "$CURRENT" "$RUNTIME_ROOT/current.previous.$(date +%s)"
|
||||
fi
|
||||
# mv follows a destination symlink that points at a directory and would place
|
||||
# LINK *inside* the old runtime. Replace the directory entry itself so an
|
||||
# upgrade atomically switches current even when it is a valid or dangling
|
||||
# symlink left by an earlier release.
|
||||
if ! "$SYSTEM_PYTHON" -c \
|
||||
'import os,sys; os.replace(sys.argv[1], sys.argv[2])' \
|
||||
"$LINK" "$CURRENT"; then
|
||||
rm -f "$LINK"
|
||||
fail "ImageFind 核心运行环境切换失败:无法原子更新 current。"
|
||||
fi
|
||||
if ! runtime_valid "$CURRENT"; then
|
||||
if [ -L "$CURRENT" ]; then
|
||||
CURRENT_LINK=$(readlink "$CURRENT" 2>/dev/null || printf '%s' '无法读取')
|
||||
elif [ -e "$CURRENT" ]; then
|
||||
CURRENT_LINK='不是符号链接'
|
||||
else
|
||||
CURRENT_LINK='不存在'
|
||||
fi
|
||||
if [ -x "$CURRENT/bin/python" ]; then
|
||||
ACTUAL_VERSION=$(
|
||||
IMAGEFIND_DATA_DIR="$DATA_ROOT/data" "$CURRENT/bin/python" -c \
|
||||
'import imagefind; print(imagefind.__version__)' 2>"$RUNTIME_ROOT/version-check.err" \
|
||||
|| printf '%s' '无法读取'
|
||||
)
|
||||
else
|
||||
ACTUAL_VERSION='Python 不可执行'
|
||||
fi
|
||||
fail "ImageFind 核心运行环境切换失败:期望版本 $VERSION,current=$CURRENT_LINK,实际版本=$ACTUAL_VERSION。"
|
||||
fi
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
exit 0
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
fail() {
|
||||
MESSAGE="$1"
|
||||
printf '%s\n' "$MESSAGE" >&2
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
printf '%s\n' "$MESSAGE" >>"$TRIM_TEMP_LOGFILE" 2>/dev/null || true
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
case "${wizard_clear_data:-false}" in
|
||||
1|true|TRUE|yes|YES|on|ON) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
DATA_ROOT="${TRIM_PKGVAR:-}"
|
||||
[ -n "$DATA_ROOT" ] || fail "ImageFind 数据清理失败:fnOS 未提供应用数据目录。"
|
||||
[ -e "$DATA_ROOT" ] || exit 0
|
||||
[ -d "$DATA_ROOT" ] || fail "ImageFind 数据清理失败:应用数据路径不是目录。"
|
||||
|
||||
DATA_ROOT_REAL=$(readlink -f -- "$DATA_ROOT") || fail "ImageFind 数据清理失败:无法解析应用数据路径。"
|
||||
case "$DATA_ROOT_REAL" in
|
||||
""|/|/home|/var|/var/apps) fail "ImageFind 数据清理失败:拒绝清理不安全的路径。" ;;
|
||||
esac
|
||||
[ "$(dirname -- "$DATA_ROOT_REAL")" != "/" ] || fail "ImageFind 数据清理失败:拒绝清理顶层目录。"
|
||||
[ -f "$DATA_ROOT_REAL/.imagefind-data-root" ] || fail "ImageFind 数据清理失败:应用数据目录缺少安全标记。"
|
||||
[ "$(sed -n '1p' "$DATA_ROOT_REAL/.imagefind-data-root")" = "imagefind-data-v1" ] || \
|
||||
fail "ImageFind 数据清理失败:应用数据目录安全标记无效。"
|
||||
|
||||
SCRIPT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
"$SCRIPT_ROOT/main" stop || fail "ImageFind 数据清理失败:无法停止应用服务。"
|
||||
|
||||
# Only clear TRIM_PKGVAR. Shared video directories and user-authorized paths
|
||||
# live outside this directory and are deliberately never traversed here.
|
||||
find "$DATA_ROOT_REAL" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + || \
|
||||
fail "ImageFind 数据清理失败:部分内部数据无法删除。"
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
DATA_ROOT="${TRIM_PKGVAR:-}"
|
||||
APP_ROOT="${TRIM_APPDEST:-}"
|
||||
COMMAND_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
# Password changes belong to ImageFind's authenticated account page. Older
|
||||
# upgrade wizards exported these variables, sometimes as placeholder strings
|
||||
# when both fields were left blank. Deliberately ignore them so a package
|
||||
# update can never fail after files have already been replaced.
|
||||
unset wizard_admin_password wizard_admin_password_confirm
|
||||
|
||||
fail() {
|
||||
MESSAGE="$1"
|
||||
printf '%s\n' "$MESSAGE" >&2
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
printf '%s\n' "$MESSAGE" >>"$TRIM_TEMP_LOGFILE" 2>/dev/null || true
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -n "$DATA_ROOT" ] || fail "ImageFind 升级失败:fnOS 未提供应用数据目录。"
|
||||
[ -n "$APP_ROOT" ] || fail "ImageFind 升级失败:fnOS 未提供应用安装目录。"
|
||||
umask 077
|
||||
mkdir -p "$DATA_ROOT/data" "$DATA_ROOT/cache"
|
||||
if [ ! -f "$DATA_ROOT/.imagefind-data-root" ]; then
|
||||
printf '%s\n' 'imagefind-data-v1' >"$DATA_ROOT/.imagefind-data-root"
|
||||
fi
|
||||
if [ ! -f "$DATA_ROOT/access.json" ]; then
|
||||
ACCESS_TEMP="$DATA_ROOT/access.json.tmp.$$"
|
||||
printf '%s\n' '{"direct_access":{"enabled":true,"port":8765}}' >"$ACCESS_TEMP"
|
||||
chmod 0600 "$ACCESS_TEMP"
|
||||
mv "$ACCESS_TEMP" "$DATA_ROOT/access.json"
|
||||
fi
|
||||
|
||||
if ! TRIM_APPDEST="$APP_ROOT" TRIM_PKGVAR="$DATA_ROOT" "$COMMAND_ROOT/runtime_bootstrap" \
|
||||
>>"${TRIM_TEMP_LOGFILE:-/dev/null}" 2>&1; then
|
||||
fail "ImageFind 升级失败:Python 3.12 核心运行环境安装失败。"
|
||||
fi
|
||||
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
printf '%s\n' "ImageFind 升级数据检查完成,现有数据库、配置和管理员密码均已保留。" \
|
||||
>>"$TRIM_TEMP_LOGFILE" 2>/dev/null || true
|
||||
fi
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
|
||||
printf '%s\n' "ImageFind 0.5.45 使用 PostgreSQL 数据层;本版本不执行旧数据迁移(包括 SQLite)。" \
|
||||
>>"$TRIM_TEMP_LOGFILE" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"defaults": {
|
||||
"run-as": "package"
|
||||
},
|
||||
"join-groups": ["video", "render"]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"data-share": {
|
||||
"shares": [
|
||||
{
|
||||
"name": "imagefind/videos"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
appname=imagefind
|
||||
version=0.5.45
|
||||
display_name=ImageFind
|
||||
desc=支持本地、AList 加密与直连的私有 AI 媒体库
|
||||
platform=x86
|
||||
source=thirdparty
|
||||
maintainer=ImageFind Contributors
|
||||
os_min_version=1.2.0
|
||||
desktop_uidir=ui
|
||||
desktop_applaunchname=imagefind.Application
|
||||
checkport=false
|
||||
ctl_stop=true
|
||||
disable_authorization_path=false
|
||||
install_dep_apps=python312,nxsir.postgresql
|
||||
@@ -0,0 +1,33 @@
|
||||
[
|
||||
{
|
||||
"stepTitle": "ImageFind 访问方式",
|
||||
"items": [
|
||||
{
|
||||
"type": "tips",
|
||||
"helpText": "飞牛桌面始终通过统一网关访问。需要继续使用 http://NAS_IP:8765 或 REST API 客户端时,可额外开启直接访问。"
|
||||
},
|
||||
{
|
||||
"type": "switch",
|
||||
"field": "wizard_direct_access",
|
||||
"label": "启用直接 Web/API 访问",
|
||||
"initValue": "false"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"field": "wizard_direct_port",
|
||||
"label": "直接访问端口",
|
||||
"initValue": "8765",
|
||||
"rules": [
|
||||
{
|
||||
"required": true,
|
||||
"message": "请输入端口"
|
||||
},
|
||||
{
|
||||
"pattern": "^(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$",
|
||||
"message": "请输入 1–65535 之间的端口"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,68 @@
|
||||
[
|
||||
{
|
||||
"stepTitle": "设置 ImageFind 管理员密码",
|
||||
"items": [
|
||||
{
|
||||
"type": "tips",
|
||||
"helpText": "此密码用于登录 ImageFind,不是飞牛 fnOS 的系统密码。请先安装并启动 nxsir.postgresql 共享服务。"
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"field": "wizard_postgres_enrollment_token",
|
||||
"label": "PostgreSQL 应用接入令牌",
|
||||
"rules": [
|
||||
{
|
||||
"required": true,
|
||||
"message": "请输入 PostgreSQL 共享服务的应用接入令牌"
|
||||
},
|
||||
{
|
||||
"min": 20,
|
||||
"message": "应用接入令牌至少需要 20 个字符"
|
||||
},
|
||||
{
|
||||
"max": 256,
|
||||
"message": "应用接入令牌不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"field": "wizard_admin_password",
|
||||
"label": "管理员密码",
|
||||
"rules": [
|
||||
{
|
||||
"required": true,
|
||||
"message": "请输入管理员密码"
|
||||
},
|
||||
{
|
||||
"min": 10,
|
||||
"message": "管理员密码至少需要 10 个字符"
|
||||
},
|
||||
{
|
||||
"max": 256,
|
||||
"message": "管理员密码不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"field": "wizard_admin_password_confirm",
|
||||
"label": "再次输入密码",
|
||||
"rules": [
|
||||
{
|
||||
"required": true,
|
||||
"message": "请再次输入管理员密码"
|
||||
},
|
||||
{
|
||||
"min": 10,
|
||||
"message": "管理员密码至少需要 10 个字符"
|
||||
},
|
||||
{
|
||||
"max": 256,
|
||||
"message": "管理员密码不能超过 256 个字符"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"stepTitle": "卸载 ImageFind",
|
||||
"items": [
|
||||
{
|
||||
"type": "tips",
|
||||
"helpText": "默认保留数据库、索引和模型,方便以后重新安装。无论如何选择,都不会删除共享目录或其他资料库中的源视频。"
|
||||
},
|
||||
{
|
||||
"type": "switch",
|
||||
"field": "wizard_clear_data",
|
||||
"label": "清除应用内部数据",
|
||||
"initValue": "false",
|
||||
"helpText": "清除数据库、索引、缩略图、缓存、已下载模型、凭据和日志。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"stepTitle": "升级 ImageFind",
|
||||
"items": [
|
||||
{
|
||||
"type": "tips",
|
||||
"helpText": "升级会保留数据库、AI 索引、模型、访问配置和管理员密码。密码请在 ImageFind 个人中心内修改。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user