Files
crypto_key/scripts/manage.sh
T
dekun 3e52b8eb87 feat: add wallet types, image upload, backup tabs, and manage.sh
Add mnemonic/key credentials with image upload, tabbed UI for add/query
and settings (auth/types/backup), daily backup to /root, and interactive
deploy script that updates via git pull only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 20:35:24 +08:00

233 lines
7.1 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# crypto_key 交互式管理脚本
# 用法: sudo bash scripts/manage.sh
# 菜单: 1 一键部署 | 2 更新(git pull) | 3 一键卸载
# 严禁使用 scp;所有代码更新一律 git pull
set -euo pipefail
REPO_URL="https://git.bz121.com/dekun/crypto_key.git"
INSTALL_DIR="/opt/crypto_key"
APP_NAME="api-key-manager"
BACKUP_ROOT="/root/crypto_key_backups"
CRON_MARKER="# crypto_key-auto-backup"
red() { printf '\033[31m%s\033[0m\n' "$*"; }
grn() { printf '\033[32m%s\033[0m\n' "$*"; }
ylw() { printf '\033[33m%s\033[0m\n' "$*"; }
info() { echo "==> $*"; }
require_root() {
if [ "$(id -u)" -ne 0 ]; then
red "请使用 root 或 sudo 运行: sudo bash scripts/manage.sh"
exit 1
fi
}
detect_app_user() {
echo "${SUDO_USER:-${USER:-root}}"
}
install_deps() {
info "安装系统依赖..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq python3 python3-venv python3-pip git curl ca-certificates
if ! command -v node >/dev/null 2>&1; then
info "安装 Node.js 20PM2 需要)..."
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y -qq nodejs
fi
if ! command -v pm2 >/dev/null 2>&1; then
info "安装 PM2..."
npm install -g pm2
fi
}
setup_python() {
info "创建/更新 Python 虚拟环境..."
cd "${INSTALL_DIR}"
python3 -m venv venv
# shellcheck disable=SC1091
source venv/bin/activate
pip install -q --upgrade pip
pip install -q -r requirements.txt
mkdir -p logs uploads
chmod +x pm2-start.sh pm2-stop.sh scripts/manage.sh scripts/install-ubuntu.sh 2>/dev/null || true
}
setup_env() {
cd "${INSTALL_DIR}"
if [ ! -f .env ]; then
if [ -f .env.example ]; then
cp .env.example .env
ylw "已从 .env.example 生成 .env(首次启动会自动补全密钥)"
else
touch .env
fi
fi
}
setup_backup_cron() {
mkdir -p "${BACKUP_ROOT}"
local cron_line="0 3 * * * cd ${INSTALL_DIR} && ./venv/bin/python -c \"from backup import create_backup_archive; create_backup_archive()\" >> ${BACKUP_ROOT}/cron.log 2>&1 ${CRON_MARKER}"
# 先去掉旧标记行,再写入
(crontab -l 2>/dev/null | grep -v "${CRON_MARKER}" || true; echo "${cron_line}") | crontab -
info "已配置每日 03:00 自动备份到 ${BACKUP_ROOT}"
}
remove_backup_cron() {
if crontab -l 2>/dev/null | grep -q "${CRON_MARKER}"; then
crontab -l 2>/dev/null | grep -v "${CRON_MARKER}" | crontab - || true
info "已移除自动备份 cron"
fi
}
start_pm2() {
local app_user
app_user="$(detect_app_user)"
cd "${INSTALL_DIR}"
if [ -n "${app_user}" ] && [ "${app_user}" != "root" ]; then
chown -R "${app_user}:${app_user}" "${INSTALL_DIR}" || true
fi
# 以调用用户身份跑 PM2(若可能)
if [ -n "${app_user}" ] && [ "${app_user}" != "root" ] && id "${app_user}" >/dev/null 2>&1; then
sudo -u "${app_user}" bash -c "cd '${INSTALL_DIR}' && pm2 delete '${APP_NAME}' 2>/dev/null || true; pm2 start ecosystem.config.cjs && pm2 save"
else
pm2 delete "${APP_NAME}" 2>/dev/null || true
pm2 start ecosystem.config.cjs
pm2 save
fi
}
do_deploy() {
info "一键部署到 ${INSTALL_DIR}"
install_deps
mkdir -p /opt
if [ -d "${INSTALL_DIR}/.git" ]; then
info "目录已存在,执行 git pull 更新代码(不用 scp..."
cd "${INSTALL_DIR}"
git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || git pull
else
info "git clone ${REPO_URL} ..."
git clone "${REPO_URL}" "${INSTALL_DIR}"
fi
setup_env
setup_python
setup_backup_cron
start_pm2
local lan_ip
lan_ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
echo ""
grn "========== 部署完成 =========="
echo " 目录: ${INSTALL_DIR}"
echo " 本机: http://127.0.0.1:5200"
echo " 局域网: http://${lan_ip:-<IP>}:5200"
echo " 自动备份: ${BACKUP_ROOT}"
echo " 状态: pm2 status"
echo " 日志: pm2 logs ${APP_NAME}"
echo "=============================="
echo ""
echo "开机自启: pm2 startup (按提示执行 sudo 命令后再 pm2 save"
}
do_update() {
if [ ! -d "${INSTALL_DIR}/.git" ]; then
red "未找到 ${INSTALL_DIR},请先选择「1 一键部署」"
exit 1
fi
info "更新(仅 git pull,严禁 scp..."
cd "${INSTALL_DIR}"
# 更新前备份关键数据
mkdir -p "${BACKUP_ROOT}"
if [ -f "${INSTALL_DIR}/venv/bin/python" ]; then
"${INSTALL_DIR}/venv/bin/python" -c "from backup import create_backup_archive; print(create_backup_archive())" \
&& grn "更新前已自动备份" || ylw "更新前备份跳过(可手动备份)"
fi
git fetch --all --prune
git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || git pull
setup_python
setup_backup_cron
start_pm2
grn "更新完成(git pull + 依赖 + PM2 重启)"
}
do_uninstall() {
ylw "即将卸载 crypto_key"
echo " - 停止并删除 PM2 进程 ${APP_NAME}"
echo " - 移除自动备份 cron"
echo " - 删除目录 ${INSTALL_DIR}"
echo " - 保留 ${BACKUP_ROOT}(如存在)"
read -r -p "确认卸载?输入 yes 继续: " confirm
if [ "${confirm}" != "yes" ]; then
echo "已取消"
return 0
fi
local app_user
app_user="$(detect_app_user)"
if [ -n "${app_user}" ] && [ "${app_user}" != "root" ] && id "${app_user}" >/dev/null 2>&1; then
sudo -u "${app_user}" pm2 delete "${APP_NAME}" 2>/dev/null || true
sudo -u "${app_user}" pm2 save 2>/dev/null || true
else
pm2 delete "${APP_NAME}" 2>/dev/null || true
pm2 save 2>/dev/null || true
fi
remove_backup_cron
if [ -d "${INSTALL_DIR}" ]; then
# 卸载前再备份一次
mkdir -p "${BACKUP_ROOT}"
local stamp
stamp="$(date +%Y%m%d_%H%M%S)"
tar czf "${BACKUP_ROOT}/uninstall_backup_${stamp}.tar.gz" \
-C "${INSTALL_DIR}" .env data.json settings.json uploads 2>/dev/null || true
rm -rf "${INSTALL_DIR}"
grn "已删除 ${INSTALL_DIR}"
fi
grn "卸载完成。备份目录保留在: ${BACKUP_ROOT}"
}
show_menu() {
echo ""
echo "========================================"
echo " crypto_key 管理脚本"
echo " 仓库: ${REPO_URL}"
echo " 部署: ${INSTALL_DIR}"
echo "========================================"
echo " 1) 一键部署"
echo " 2) 更新(git pull,严禁 scp"
echo " 3) 一键卸载"
echo " 0) 退出"
echo "========================================"
}
main() {
require_root
# 非交互:支持参数直接执行
if [ "${1:-}" = "deploy" ] || [ "${1:-}" = "1" ]; then do_deploy; exit 0; fi
if [ "${1:-}" = "update" ] || [ "${1:-}" = "2" ]; then do_update; exit 0; fi
if [ "${1:-}" = "uninstall" ] || [ "${1:-}" = "3" ]; then do_uninstall; exit 0; fi
while true; do
show_menu
read -r -p "请选择 [0-3]: " choice
case "${choice}" in
1) do_deploy ;;
2) do_update ;;
3) do_uninstall ;;
0) echo "再见"; exit 0 ;;
*) red "无效选项" ;;
esac
done
}
main "$@"