会员服务器:上传肥牛应用源码 v1.5.1(fnnas.huiyuan 完整 fpk 工程结构)

含 app/server/server.cjs 服务端、app/www 网页(admin/auth/项目云/库备份/settings)、
app/ui 图标、cmd 安装脚本、config、wizard、manifest、图标与对接文档。
按仓库规则排除构建产物 *.fpk 与 .mimosa 缓存。
This commit is contained in:
dqb-dev
2026-10-02 00:03:20 +08:00
parent d9be73be89
commit 65755bf520
30 changed files with 2718 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
#!/bin/bash
### 配置变更回调(应用设置向导提交后):更新 app.env,向导留空的项保持不变
CFG="${TRIM_PKGETC}/app.env"
[ -f "${CFG}" ] || exit 0
if [ -n "${wizard_port}" ]; then
sed -i "s|^HY_PORT=.*|HY_PORT=${wizard_port}|" "${CFG}"
fi
if [ -n "${wizard_dbdir}" ]; then
if [ ! -d "${wizard_dbdir}" ]; then
if mkdir -p "${wizard_dbdir}" 2>/dev/null; then
[ -n "${TRIM_UID}" ] && chown "${TRIM_UID}:${TRIM_GID}" "${wizard_dbdir}" 2>/dev/null
else
echo "警告:数据目录 ${wizard_dbdir} 创建失败,保持原目录不变" >> "${TRIM_TEMP_LOGFILE}" 2>/dev/null
fi
fi
[ -d "${wizard_dbdir}" ] && sed -i "s|^HY_DB_DIR=.*|HY_DB_DIR=${wizard_dbdir}|" "${CFG}"
fi
# 管理密码(留空不修改;更新盐+哈希两行,不存在则追加)
if [ -n "${wizard_admin_pwd}" ]; then
SALT="$(date +%s%N)$RANDOM"
HASH="$(printf '%s' "${SALT}${wizard_admin_pwd}" | sha256sum | cut -d' ' -f1)"
if grep -q "^ADMIN_PWD_SALT=" "${CFG}"; then
sed -i "s|^ADMIN_PWD_SALT=.*|ADMIN_PWD_SALT=${SALT}|" "${CFG}"
else
echo "ADMIN_PWD_SALT=${SALT}" >> "${CFG}"
fi
if grep -q "^ADMIN_PWD_SHA256=" "${CFG}"; then
sed -i "s|^ADMIN_PWD_SHA256=.*|ADMIN_PWD_SHA256=${HASH}|" "${CFG}"
else
echo "ADMIN_PWD_SHA256=${HASH}" >> "${CFG}"
fi
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') - config updated port=${wizard_port:-保持} dbdir=${wizard_dbdir:-保持} admin_pwd=$([ -n "${wizard_admin_pwd}" ] && echo 已修改 || echo 保持)" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
exit 0
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
### This script is called before the user change environment variables in application setting page.
exit 0
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
### 安装完成回调:把向导值合并写入 app.env(cmd/main 与服务进程读取)。
### 合并式:只覆盖本脚本管理的键(HY_PORT/HY_DB_DIR/ADMIN_*),设置页保存的其他键(如 HY_REG_OPEN)保留。
mkdir -p "${TRIM_PKGETC}" 2>/dev/null
CFG="${TRIM_PKGETC}/app.env"
PORT="${wizard_port}"
DBDIR="${wizard_dbdir}"
[ -z "${PORT}" ] && PORT="${TRIM_SERVICE_PORT:-12701}"
# 向导填了自定义数据目录:目录不存在则创建,新建目录归属应用用户(已存在的目录不动,由 ACL/目录授权控制)
if [ -n "${DBDIR}" ] && [ ! -d "${DBDIR}" ]; then
if mkdir -p "${DBDIR}" 2>/dev/null; then
[ -n "${TRIM_UID}" ] && chown "${TRIM_UID}:${TRIM_GID}" "${DBDIR}" 2>/dev/null
else
echo "警告:数据目录 ${DBDIR} 创建失败,将使用默认目录" >> "${TRIM_TEMP_LOGFILE}" 2>/dev/null
DBDIR=""
fi
fi
# 合并旧配置:跳过本脚本管理的键,其余原样保留
TMP="${CFG}.new"
: > "${TMP}"
if [ -f "${CFG}" ]; then
while IFS='=' read -r k v; do
[ -z "${k}" ] && continue
case "${k}" in
HY_PORT|HY_DB_DIR|ADMIN_PWD_SALT|ADMIN_PWD_SHA256) ;;
*) printf '%s=%s\n' "${k}" "${v}" >> "${TMP}" ;;
esac
done < "${CFG}"
fi
# 管理密码(向导必填;留空则保留旧哈希,若从未设置过则服务端登录时会提示未设置)
if [ -n "${wizard_admin_pwd}" ]; then
SALT="$(date +%s%N)$RANDOM${TRIM_APPNAME:-hy}"
HASH="$(printf '%s' "${SALT}${wizard_admin_pwd}" | sha256sum | cut -d' ' -f1)"
printf 'ADMIN_PWD_SALT=%s\n' "${SALT}" >> "${TMP}"
printf 'ADMIN_PWD_SHA256=%s\n' "${HASH}" >> "${TMP}"
fi
printf 'HY_PORT=%s\n' "${PORT}" >> "${TMP}"
printf 'HY_DB_DIR=%s\n' "${DBDIR}" >> "${TMP}"
mv "${TMP}" "${CFG}"
echo "$(date '+%Y-%m-%d %H:%M:%S') - installed v${TRIM_APPVER} port=${PORT} dbdir=${DBDIR:-默认} admin_pwd=$([ -n "${wizard_admin_pwd}" ] && echo 已设置 || echo 保持)" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
exit 0
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
### 安装初始化:建默认数据目录;若数据目录存在上次卸载前备份的 TLS/RSA 密钥与配置则恢复
### (保证已导入的正规 SSL 证书跨升级保留)。安装包不带任何会员数据,空库由服务首次启动创建。
DB_DIR="${TRIM_DATA_SHARE_PATHS%%:*}"
[ -z "${DB_DIR}" ] && DB_DIR="${TRIM_PKGVAR}/files"
mkdir -p "${DB_DIR}" 2>/dev/null
BAK="${DB_DIR}/.huiyuan-密钥备份"
if [ -d "${BAK}" ]; then
mkdir -p "${TRIM_PKGETC}" 2>/dev/null
for f in tls.key tls.crt huiyuan_rsa.pem app.env; do
if [ -f "${BAK}/${f}" ] && [ ! -f "${TRIM_PKGETC}/${f}" ]; then
cp -f "${BAK}/${f}" "${TRIM_PKGETC}/${f}" 2>/dev/null
fi
done
echo "$(date '+%Y-%m-%d %H:%M:%S') - 已从 ${BAK} 恢复 TLS/RSA 密钥与配置" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
fi
exit 0
+130
View File
@@ -0,0 +1,130 @@
#!/bin/bash
LOG_FILE="${TRIM_PKGVAR}/info.log"
PID_FILE="${TRIM_PKGVAR}/app.pid"
NODE_BIN="/var/apps/nodejs_v22/target/bin/node"
[ ! -x "${NODE_BIN}" ] && NODE_BIN="$(command -v node || echo node)"
SERVER="${TRIM_APPDEST}/server/server.cjs"
# 从 app.env 读取向导持久化的配置(install_callback/config_callback 写入)
CFG_FILE="${TRIM_PKGETC}/app.env"
HY_PORT=""
HY_DB_DIR=""
if [ -f "${CFG_FILE}" ]; then
while IFS='=' read -r k v; do
case "$k" in
HY_PORT) HY_PORT="$v" ;;
HY_DB_DIR) HY_DB_DIR="$v" ;;
esac
done < "${CFG_FILE}"
fi
[ -z "${HY_PORT}" ] && HY_PORT="${TRIM_SERVICE_PORT:-12701}"
CMD="${NODE_BIN} ${SERVER}"
log_msg() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> ${LOG_FILE}
}
start_process() {
if status; then
return 0
fi
log_msg "Starting process (port=${HY_PORT} dbdir=${HY_DB_DIR:-默认}) ..."
# run cmd to start process
HY_PORT="${HY_PORT}" HY_DB_DIR="${HY_DB_DIR}" HY_CFG="${CFG_FILE}" \
bash -c "${CMD}" >> ${LOG_FILE} 2>&1 &
# write pid to pidfile
printf "%s" "$!" > "${PID_FILE}"
sleep 2
# 启动后确认进程仍在,失败时提示日志位置
if status; then
return 0
fi
echo "会员服务器启动失败,请查看日志: ${LOG_FILE}" >&2
return 1
}
stop_process() {
log_msg "Stopping process ..."
if [ -r "${PID_FILE}" ]; then
pid=$(head -n 1 "${PID_FILE}" | tr -d '[:space:]')
log_msg "pid=${pid}"
if ! check_process "${pid}"; then
# process not exist, delete pidfile
rm -f "${PID_FILE}"
log_msg "remove pid file 1"
return
fi
log_msg "send TERM signal to PID:${pid}..."
kill -TERM ${pid} >> ${LOG_FILE} 2>&1
local count=0
while check_process "${pid}" && [ $count -lt 10 ]; do
sleep 1
count=$((count + 1))
log_msg "waiting process terminal... (${count}s/10s)"
done
if check_process "${pid}"; then
log_msg "send KILL signal to PID:${pid}..."
kill -KILL ${pid}
sleep 1
rm -f "${PID_FILE}"
else
log_msg "process killed... "
fi
fi
return 0
}
check_process() {
local pid=$1
if kill -0 "$pid" 2>/dev/null; then
return 0 # process exist
else
return 1
fi
}
status() {
if [ -f "${PID_FILE}" ]; then
pid=$(head -n 1 "${PID_FILE}" | tr -d '[:space:]')
if check_process "${pid}"; then
return 0
else
# Process is not running but pidfile exists - clean it up
rm -f "${PID_FILE}"
fi
fi
return 1
}
case $1 in
start)
# run start command. exit 0 if success, exit 1 if failed
start_process
;;
stop)
# run stop command. exit 0 if success, exit 1 if failed
stop_process
;;
status)
# check application status command. exit 0 if running, exit 3 if not running
if status; then
exit 0
else
exit 3
fi
;;
*)
exit 1
;;
esac
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
### This script is called after the user uninstalls the application.
exit 0
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
### 卸载初始化:把 TLS 证书/RSA 密钥/配置备份到数据目录(数据目录在应用目录外,卸载不清除),
### 这样升级重装后 install_init 可以恢复,已导入的正规 SSL 证书不会丢失。
DB_DIR="${TRIM_DATA_SHARE_PATHS%%:*}"
# 自定义数据目录优先(与 install_callback 的写法一致)
CFG="${TRIM_PKGETC}/app.env"
if [ -f "${CFG}" ]; then
while IFS='=' read -r k v; do
case "$k" in
HY_DB_DIR) DB_DIR="$v" ;;
esac
done < "${CFG}"
fi
[ -z "${DB_DIR}" ] && DB_DIR="${TRIM_PKGVAR}/files"
BAK="${DB_DIR}/.huiyuan-密钥备份"
mkdir -p "${BAK}" 2>/dev/null
for f in tls.key tls.crt huiyuan_rsa.pem app.env; do
if [ -f "${TRIM_PKGETC}/${f}" ]; then
cp -f "${TRIM_PKGETC}/${f}" "${BAK}/${f}" 2>/dev/null
fi
done
echo "$(date '+%Y-%m-%d %H:%M:%S') - 卸载前已备份 TLS/RSA 密钥与配置到 ${BAK}" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
exit 0
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
### 升级完成回调:正常升级不覆盖 TRIM_PKGETC,配置自动延续。此处再校验兜底:
### 若 app.env 因升级异常丢失,从 upgrade_init 在数据目录做的备份恢复,
### 保证其他服务器上的端口/数据目录/管理密码/注册开关等配置全部沿用,绝不被重置。
CFG="${TRIM_PKGETC}/app.env"
DB_DIR="${TRIM_DATA_SHARE_PATHS%%:*}"
if [ -f "${CFG}" ]; then
while IFS='=' read -r k v; do
case "$k" in
HY_DB_DIR) DB_DIR="$v" ;;
esac
done < "${CFG}"
fi
[ -z "${DB_DIR}" ] && DB_DIR="${TRIM_PKGVAR}/files"
BAK="${DB_DIR}/.huiyuan-升级备份"
if [ ! -f "${CFG}" ] && [ -f "${BAK}/app.env" ]; then
mkdir -p "${TRIM_PKGETC}" 2>/dev/null
cp -f "${BAK}/app.env" "${CFG}" 2>/dev/null
for f in tls.key tls.crt huiyuan_rsa.pem; do
[ -f "${BAK}/${f}" ] && cp -f "${BAK}/${f}" "${TRIM_PKGETC}/${f}" 2>/dev/null
done
echo "$(date '+%Y-%m-%d %H:%M:%S') - 升级后检测到配置丢失,已从 ${BAK} 恢复 app.env 与密钥" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
# 重读恢复出的数据目录,一并恢复数据库(若升级后库文件异常缺失)
while IFS='=' read -r k v; do
case "$k" in
HY_DB_DIR) DB_DIR="$v" ;;
esac
done < "${CFG}"
if [ -f "${BAK}/huiyuan.json" ] && [ ! -f "${DB_DIR}/huiyuan.json" ]; then
cp -f "${BAK}/huiyuan.json" "${DB_DIR}/huiyuan.json" 2>/dev/null
echo "$(date '+%Y-%m-%d %H:%M:%S') - 已从备份恢复数据库 huiyuan.json 到 ${DB_DIR}" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
fi
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') - upgraded to v${TRIM_APPVER} (from v${TRIM_OLD_APPVER:-unknown}) 配置与数据沿用" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
exit 0
+39
View File
@@ -0,0 +1,39 @@
#!/bin/bash
### 升级初始化:升级覆盖的是 TRIM_APPDEST(target),不动 TRIM_PKGETC(配置)与数据目录。
### 但为防升级流程异常导致配置丢失,升级前把 app.env/TLS/RSA 密钥/数据库打包备份到
### 数据目录(应用目录之外,升级不清除),upgrade_callback 检测到配置丢失时用此备份恢复。
### 备份内容不含任何会员数据和配置本体,只在升级窗口存在,升级正常完成后保留最近一份
### 作为兜底,不参与运行。
DB_DIR="${TRIM_DATA_SHARE_PATHS%%:*}"
# 自定义数据目录优先(与 install_callback/uninstall_init 的解析规则一致)
CFG="${TRIM_PKGETC}/app.env"
if [ -f "${CFG}" ]; then
while IFS='=' read -r k v; do
case "$k" in
HY_DB_DIR) DB_DIR="$v" ;;
esac
done < "${CFG}"
fi
[ -z "${DB_DIR}" ] && DB_DIR="${TRIM_PKGVAR}/files"
BAK="${DB_DIR}/.huiyuan-升级备份"
mkdir -p "${BAK}" 2>/dev/null
# 备份配置目录里的 app.env 与密钥证书(若存在)
for f in app.env tls.key tls.crt huiyuan_rsa.pem; do
if [ -f "${TRIM_PKGETC}/${f}" ]; then
cp -f "${TRIM_PKGETC}/${f}" "${BAK}/${f}" 2>/dev/null
fi
done
# 备份数据库(小 JSON 文件,整库快照)
if [ -f "${DB_DIR}/huiyuan.json" ]; then
cp -f "${DB_DIR}/huiyuan.json" "${BAK}/huiyuan.json" 2>/dev/null
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') - 升级前已备份配置与数据库到 ${BAK} (v${TRIM_OLD_APPVER:-?} → v${TRIM_APPVER})" >> "${TRIM_PKGVAR}/app.log" 2>/dev/null
exit 0