feat: image lightbox and curl|bash Ubuntu deploy
Add click-to-zoom for uploaded images, move manage.sh to deploy/ for curl|bash bootstrap, and remove Windows docs and PowerShell scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+18
-16
@@ -4,7 +4,21 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v2.1.0(当前)
|
## v2.1.1(当前)
|
||||||
|
|
||||||
|
### 新增 / 调整
|
||||||
|
|
||||||
|
- **图片点击放大**:查询/添加预览支持灯箱全屏查看(Esc / 点击遮罩关闭)
|
||||||
|
- **一键部署改为 curl | bash**(与 crypto_monitor_user 同风格):
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
|
```
|
||||||
|
- 部署脚本迁至 `deploy/manage.sh`(支持管道自举、从 `/dev/tty` 读菜单)
|
||||||
|
- 文档仅保留 **Ubuntu** 说明,移除 Windows 相关内容
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v2.1.0
|
||||||
|
|
||||||
### 新增
|
### 新增
|
||||||
|
|
||||||
@@ -15,34 +29,22 @@
|
|||||||
- **系统设置 Tab**:账户密码 / 自定义类型 / 备份恢复
|
- **系统设置 Tab**:账户密码 / 自定义类型 / 备份恢复
|
||||||
- **前端备份恢复**:下载 `.tar.gz`、立即备份、上传恢复(含 `.env`、`data.json`、`settings.json`、`uploads`)
|
- **前端备份恢复**:下载 `.tar.gz`、立即备份、上传恢复(含 `.env`、`data.json`、`settings.json`、`uploads`)
|
||||||
- **每日自动备份**:服务内线程(03:00)+ 部署脚本 cron,备份到 `/root/crypto_key_backups/`
|
- **每日自动备份**:服务内线程(03:00)+ 部署脚本 cron,备份到 `/root/crypto_key_backups/`
|
||||||
- **交互式管理脚本** `scripts/manage.sh`:
|
- **交互式管理脚本**:
|
||||||
1. 一键部署
|
1. 一键部署
|
||||||
2. 更新(**仅 `git pull`,严禁 scp**)
|
2. 更新(**仅 `git pull`,严禁 scp**)
|
||||||
3. 一键卸载
|
3. 一键卸载
|
||||||
|
|
||||||
### 变更
|
### 变更
|
||||||
|
|
||||||
- `scripts/install-ubuntu.sh` 改为调用 `manage.sh deploy`
|
|
||||||
- 自定义字段类型增加:`textarea`、`image`
|
- 自定义字段类型增加:`textarea`、`image`
|
||||||
|
|
||||||
### 升级注意
|
### 升级注意
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/crypto_key
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
sudo bash scripts/manage.sh
|
|
||||||
# 选择 2) 更新
|
# 选择 2) 更新
|
||||||
```
|
```
|
||||||
|
|
||||||
或:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_key
|
|
||||||
git pull
|
|
||||||
source venv/bin/activate
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pm2 restart api-key-manager
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v2.0.0
|
## v2.0.0
|
||||||
@@ -111,7 +113,7 @@ pm2 restart api-key-manager
|
|||||||
|
|
||||||
- PM2 守护进程支持(`ecosystem.config.cjs`)
|
- PM2 守护进程支持(`ecosystem.config.cjs`)
|
||||||
- Ubuntu `/opt/crypto_key` 部署脚本与文档
|
- Ubuntu `/opt/crypto_key` 部署脚本与文档
|
||||||
- 启动脚本:`pm2-start.sh` / `pm2-start.ps1`
|
- 启动脚本:`pm2-start.sh` / `pm2-stop.sh`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 部署文档 — crypto_key(Ubuntu /opt)
|
# 部署文档 — crypto_key(Ubuntu)
|
||||||
|
|
||||||
> 仓库:[https://git.bz121.com/dekun/crypto_key](https://git.bz121.com/dekun/crypto_key.git)
|
> 仓库:[https://git.bz121.com/dekun/crypto_key](https://git.bz121.com/dekun/crypto_key.git)
|
||||||
> 部署路径:**`/opt/crypto_key`**
|
> 部署路径:**`/opt/crypto_key`**
|
||||||
@@ -14,19 +14,16 @@
|
|||||||
→ http://<IP>:5200
|
→ http://<IP>:5200
|
||||||
→ PM2 (api-key-manager)
|
→ PM2 (api-key-manager)
|
||||||
→ venv/python app.py (Flask)
|
→ venv/python app.py (Flask)
|
||||||
→ .env (登录账号 + 加密主密钥)
|
→ .env / data.json / settings.json / uploads/
|
||||||
→ data.json (加密后的凭证数据)
|
|
||||||
→ settings.json (自定义类型配置)
|
|
||||||
→ uploads/ (图片附件)
|
|
||||||
→ /root/crypto_key_backups/ (每日自动备份)
|
→ /root/crypto_key_backups/ (每日自动备份)
|
||||||
```
|
```
|
||||||
|
|
||||||
| 文件 | 说明 | 是否提交 Git |
|
| 文件 | 说明 | 是否提交 Git |
|
||||||
|------|------|----------------|
|
|------|------|----------------|
|
||||||
| `.env` | `SECRET_KEY`、`AUTH_*`、`DATA_ENCRYPTION_KEY` | 否 |
|
| `.env` | 登录账号 + 加密主密钥 | 否 |
|
||||||
| `data.json` | 加密凭证库 | 否 |
|
| `data.json` | 加密凭证库 | 否 |
|
||||||
| `settings.json` | 自定义类型定义 | 否 |
|
| `settings.json` | 自定义类型 | 否 |
|
||||||
| `uploads/` | 上传的图片 | 否 |
|
| `uploads/` | 上传图片 | 否 |
|
||||||
| `venv/` | Python 虚拟环境 | 否 |
|
| `venv/` | Python 虚拟环境 | 否 |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -35,28 +32,20 @@
|
|||||||
|
|
||||||
| 项目 | 要求 |
|
| 项目 | 要求 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 系统 | Ubuntu 20.04 / 22.04 / 24.04 LTS(Windows 见附录) |
|
| 系统 | Ubuntu 20.04 / 22.04 / 24.04 LTS |
|
||||||
| Python | 3.8+ |
|
| Python | 3.8+ |
|
||||||
| Node.js | 16+(PM2 守护时需要) |
|
| Node.js | 16+(PM2) |
|
||||||
| 权限 | `sudo`(管理脚本) |
|
| 权限 | root |
|
||||||
| 磁盘 | ≥ 200MB |
|
| 磁盘 | ≥ 200MB |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 三、交互式一键部署(推荐)
|
## 三、一键部署(推荐)
|
||||||
|
|
||||||
### 3.1 克隆仓库
|
**无需预先 clone**,在 Ubuntu 服务器上执行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /opt
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
sudo git clone https://git.bz121.com/dekun/crypto_key.git /opt/crypto_key
|
|
||||||
cd /opt/crypto_key
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 运行管理脚本
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo bash scripts/manage.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
交互菜单:
|
交互菜单:
|
||||||
@@ -68,25 +57,28 @@ sudo bash scripts/manage.sh
|
|||||||
0) 退出
|
0) 退出
|
||||||
```
|
```
|
||||||
|
|
||||||
也可直接传参:
|
也可传参直接执行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo bash scripts/manage.sh deploy # 等同选项 1
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash -s -- deploy
|
||||||
sudo bash scripts/manage.sh update # 等同选项 2
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash -s -- update
|
||||||
sudo bash scripts/manage.sh uninstall # 等同选项 3
|
|
||||||
```
|
```
|
||||||
|
|
||||||
兼容旧入口:`sudo bash scripts/install-ubuntu.sh`(内部调用 deploy)。
|
已安装后本地调用:
|
||||||
|
|
||||||
### 3.3 部署脚本会做什么
|
```bash
|
||||||
|
bash /opt/crypto_key/deploy/manage.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 部署脚本会做什么
|
||||||
|
|
||||||
1. 安装 python3 / venv / git / Node / PM2
|
1. 安装 python3 / venv / git / Node / PM2
|
||||||
2. `git clone` 或 **`git pull`** 到 `/opt/crypto_key`
|
2. `git clone` 或 **`git pull`** 到 `/opt/crypto_key`
|
||||||
3. 创建 venv、`pip install -r requirements.txt`
|
3. 创建 venv、安装依赖
|
||||||
4. 配置每日 03:00 cron 备份到 `/root/crypto_key_backups/`
|
4. 配置每日 03:00 cron 备份到 `/root/crypto_key_backups/`
|
||||||
5. PM2 启动 `api-key-manager`
|
5. PM2 启动 `api-key-manager`
|
||||||
|
|
||||||
### 3.4 验证
|
### 验证
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pm2 status
|
pm2 status
|
||||||
@@ -102,97 +94,66 @@ curl -s http://127.0.0.1:5200/api/auth/status
|
|||||||
## 四、更新升级(仅 git pull)
|
## 四、更新升级(仅 git pull)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/crypto_key
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
sudo bash scripts/manage.sh
|
|
||||||
# 选择 2) 更新
|
# 选择 2) 更新
|
||||||
```
|
```
|
||||||
|
|
||||||
脚本流程:更新前自动备份 → `git pull` → 安装依赖 → 刷新 cron → 重启 PM2。
|
或:
|
||||||
|
|
||||||
**禁止** 使用 scp / rsync 覆盖代码目录;数据文件(`.env`、`data.json`)本来就不在 Git 里,不会被 pull 冲掉。
|
|
||||||
|
|
||||||
手动更新:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/crypto_key
|
bash /opt/crypto_key/deploy/manage.sh
|
||||||
git pull
|
# 选择 2
|
||||||
source venv/bin/activate
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pm2 restart api-key-manager
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
流程:更新前自动备份 → `git pull` → 安装依赖 → 刷新 cron → 重启 PM2。
|
||||||
|
|
||||||
|
**禁止** 使用 scp / rsync 覆盖代码目录。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 五、一键卸载
|
## 五、一键卸载
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo bash scripts/manage.sh
|
bash /opt/crypto_key/deploy/manage.sh
|
||||||
# 选择 3) 一键卸载,输入 yes 确认
|
# 选择 3,输入 yes 确认
|
||||||
```
|
```
|
||||||
|
|
||||||
会停止 PM2、移除 cron、删除 `/opt/crypto_key`;卸载前尽量再打一份包到 `/root/crypto_key_backups/`,**保留备份目录**。
|
会停止 PM2、移除 cron、删除 `/opt/crypto_key`;卸载前尽量再打一份包到 `/root/crypto_key_backups/`,**保留备份目录**。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 六、Windows 本地部署
|
## 六、PM2 运维
|
||||||
|
|
||||||
```powershell
|
|
||||||
cd C:\path\to\crypto_key
|
|
||||||
python -m venv venv
|
|
||||||
.\venv\Scripts\activate
|
|
||||||
pip install -r requirements.txt
|
|
||||||
copy .env.example .env
|
|
||||||
python app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
自动备份目录回退为项目下 `backups\`(无 `/root` 写权限时)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、PM2 运维
|
|
||||||
|
|
||||||
| 命令 | 说明 |
|
| 命令 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `pm2 status` | 查看状态 |
|
| `pm2 status` | 查看状态 |
|
||||||
| `pm2 logs api-key-manager` | 查看日志 |
|
| `pm2 logs api-key-manager` | 查看日志 |
|
||||||
| `pm2 restart api-key-manager` | 重启 |
|
| `pm2 restart api-key-manager` | 重启 |
|
||||||
| `./pm2-start.sh` | 启动(Linux) |
|
| `./pm2-start.sh` | 启动 |
|
||||||
|
| `./pm2-stop.sh` | 停止 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 八、备份与恢复
|
## 七、备份与恢复
|
||||||
|
|
||||||
### 8.1 自动备份
|
### 自动备份
|
||||||
|
|
||||||
- 应用内守护线程:每天 **03:00**
|
- 应用内守护线程:每天 **03:00**
|
||||||
- cron(部署脚本安装):每天 **03:00**
|
- cron:每天 **03:00**
|
||||||
- 目录:`/root/crypto_key_backups/crypto_key_backup_YYYYMMDD_HHMMSS.tar.gz`
|
- 目录:`/root/crypto_key_backups/`
|
||||||
- 保留约 30 天,过期自动清理
|
- 约保留 30 天
|
||||||
|
|
||||||
### 8.2 前端备份恢复
|
### 前端备份恢复
|
||||||
|
|
||||||
系统设置 → **备份恢复**:
|
系统设置 → **备份恢复**:下载备份 / 立即备份 / 上传恢复。
|
||||||
|
|
||||||
- 下载备份
|
|
||||||
- 立即备份到服务器
|
|
||||||
- 上传 `.tar.gz` 恢复
|
|
||||||
|
|
||||||
备份内容:`.env`、`data.json`、`settings.json`、`uploads/`。
|
备份内容:`.env`、`data.json`、`settings.json`、`uploads/`。
|
||||||
|
|
||||||
### 8.3 命令行恢复
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 解压到项目目录(示例)
|
|
||||||
cd /opt/crypto_key
|
|
||||||
tar xzvf /root/crypto_key_backups/某备份.tar.gz
|
|
||||||
pm2 restart api-key-manager
|
|
||||||
```
|
|
||||||
|
|
||||||
**`.env` 中 `DATA_ENCRYPTION_KEY` 必须与备份一致。**
|
**`.env` 中 `DATA_ENCRYPTION_KEY` 必须与备份一致。**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 九、防火墙
|
## 八、防火墙
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo ufw allow from 192.168.0.0/16 to any port 5200 proto tcp
|
sudo ufw allow from 192.168.0.0/16 to any port 5200 proto tcp
|
||||||
@@ -202,7 +163,7 @@ sudo ufw allow from 192.168.0.0/16 to any port 5200 proto tcp
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 十、故障排查
|
## 九、故障排查
|
||||||
|
|
||||||
| 现象 | 处理 |
|
| 现象 | 处理 |
|
||||||
|------|------|
|
|------|------|
|
||||||
@@ -210,11 +171,11 @@ sudo ufw allow from 192.168.0.0/16 to any port 5200 proto tcp
|
|||||||
| 无法登录 | 检查 `.env` 中 `AUTH_*` |
|
| 无法登录 | 检查 `.env` 中 `AUTH_*` |
|
||||||
| 数据无法解密 | `DATA_ENCRYPTION_KEY` 是否与备份一致 |
|
| 数据无法解密 | `DATA_ENCRYPTION_KEY` 是否与备份一致 |
|
||||||
| 局域网无法访问 | `ss -tlnp \| grep 5200`;检查 ufw |
|
| 局域网无法访问 | `ss -tlnp \| grep 5200`;检查 ufw |
|
||||||
| 图片 404 | 确认 `uploads/` 存在且备份已含图片文件 |
|
| 图片 404 | 确认 `uploads/` 存在 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 十一、目录结构
|
## 十、目录结构
|
||||||
|
|
||||||
```
|
```
|
||||||
/opt/crypto_key/
|
/opt/crypto_key/
|
||||||
@@ -225,29 +186,26 @@ sudo ufw allow from 192.168.0.0/16 to any port 5200 proto tcp
|
|||||||
├── index.html
|
├── index.html
|
||||||
├── requirements.txt
|
├── requirements.txt
|
||||||
├── ecosystem.config.cjs
|
├── ecosystem.config.cjs
|
||||||
├── scripts/
|
├── deploy/
|
||||||
│ ├── manage.sh # 交互式:部署 / 更新 / 卸载
|
│ └── manage.sh # curl | bash 入口
|
||||||
│ └── install-ubuntu.sh # 兼容入口 → deploy
|
├── scripts/ # 兼容旧入口
|
||||||
├── uploads/ # 图片(运行时)
|
├── uploads/
|
||||||
├── logs/
|
├── logs/
|
||||||
├── venv/
|
└── venv/
|
||||||
└── ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 十二、命令速查
|
## 十一、命令速查
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 首次
|
# 一键(推荐)
|
||||||
sudo git clone https://git.bz121.com/dekun/crypto_key.git /opt/crypto_key
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
cd /opt/crypto_key && sudo bash scripts/manage.sh # 选 1
|
|
||||||
|
|
||||||
# 更新(git pull only)
|
# 日常
|
||||||
sudo bash scripts/manage.sh # 选 2
|
bash /opt/crypto_key/deploy/manage.sh
|
||||||
|
pm2 status
|
||||||
# 卸载
|
pm2 logs api-key-manager --lines 50
|
||||||
sudo bash scripts/manage.sh # 选 3
|
|
||||||
```
|
```
|
||||||
|
|
||||||
使用细节请参阅 **[USAGE.md](./USAGE.md)**。
|
使用细节请参阅 **[USAGE.md](./USAGE.md)**。
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# crypto_key — 凭证保险库
|
# crypto_key — 凭证保险库
|
||||||
|
|
||||||
本地 / 局域网 Web 工具,集中管理交易所 API、钱包助记词/密钥、网站、邮箱、机器人、微信/QQ 及自定义类型凭证。数据 **AES 加密** 存储,**用户名密码登录**,浏览器不缓存明文。
|
Ubuntu / 局域网 Web 工具,集中管理交易所 API、钱包助记词/密钥、网站、邮箱、机器人、微信/QQ 及自定义类型凭证。数据 **AES 加密** 存储,**用户名密码登录**,浏览器不缓存明文。
|
||||||
|
|
||||||
| 项目 | 说明 |
|
| 项目 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 仓库 | [git.bz121.com/dekun/crypto_key](https://git.bz121.com/dekun/crypto_key.git) |
|
| 仓库 | [git.bz121.com/dekun/crypto_key](https://git.bz121.com/dekun/crypto_key.git) |
|
||||||
| 端口 | `5200`(`0.0.0.0`,局域网可访问) |
|
| 端口 | `5200`(`0.0.0.0`,局域网可访问) |
|
||||||
| Ubuntu 部署 | `/opt/crypto_key` + PM2 |
|
| 部署路径 | `/opt/crypto_key` + PM2 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -15,29 +15,47 @@
|
|||||||
| 文档 | 说明 |
|
| 文档 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **[USAGE.md](./USAGE.md)** | 使用说明(登录、添加、查询、系统设置) |
|
| **[USAGE.md](./USAGE.md)** | 使用说明(登录、添加、查询、系统设置) |
|
||||||
| **[DEPLOY.md](./DEPLOY.md)** | 部署文档(Ubuntu/Windows、PM2、备份、排错) |
|
| **[DEPLOY.md](./DEPLOY.md)** | Ubuntu 部署、更新、备份、排错 |
|
||||||
| **[CHANGELOG.md](./CHANGELOG.md)** | 更新说明与版本升级注意 |
|
| **[CHANGELOG.md](./CHANGELOG.md)** | 更新说明与版本升级注意 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 快速开始
|
## 一键部署(Ubuntu)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
交互菜单:
|
||||||
|
|
||||||
|
1. 一键部署
|
||||||
|
2. 更新(**仅 git pull**,严禁 scp)
|
||||||
|
3. 一键卸载
|
||||||
|
|
||||||
|
已安装后也可:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash /opt/crypto_key/deploy/manage.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
详见 [DEPLOY.md](./DEPLOY.md)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 本地开发启动
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.bz121.com/dekun/crypto_key.git
|
git clone https://git.bz121.com/dekun/crypto_key.git
|
||||||
cd crypto_key
|
cd crypto_key
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
source venv/bin/activate # Windows: venv\Scripts\activate
|
source venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
cp .env.example .env # 可选,首次运行也会自动生成
|
cp .env.example .env
|
||||||
python app.py
|
python app.py
|
||||||
```
|
```
|
||||||
|
|
||||||
浏览器打开:
|
浏览器:http://127.0.0.1:5200
|
||||||
|
默认账号(请尽快修改):`admin` / `admin123`
|
||||||
- 本机:http://127.0.0.1:5200
|
|
||||||
- 局域网:`http://<本机IP>:5200`
|
|
||||||
|
|
||||||
默认账号(请尽快在系统设置中修改):`admin` / `admin123`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -45,30 +63,11 @@ python app.py
|
|||||||
|
|
||||||
- 登录鉴权(凭据在 `.env`,Session 在服务端)
|
- 登录鉴权(凭据在 `.env`,Session 在服务端)
|
||||||
- 内置类型:交易所、**钱包助记词**、**钱包密钥**、网站、邮箱、企微/钉钉机器人、微信、QQ
|
- 内置类型:交易所、**钱包助记词**、**钱包密钥**、网站、邮箱、企微/钉钉机器人、微信、QQ
|
||||||
- **图片上传**:助记词/密钥等可附带截图
|
- **图片上传**:助记词/密钥等可附带截图,**点击可放大**
|
||||||
- 自定义类型:系统设置中添加(小红书、抖音等)
|
- 自定义类型:系统设置中添加
|
||||||
- Tab 界面:添加凭证 / 查询凭证;查询类型为下拉筛选
|
- Tab 界面:添加凭证 / 查询凭证;查询类型为下拉筛选
|
||||||
- 系统设置 Tab:账户密码 / 自定义类型 / **备份恢复**
|
- 系统设置 Tab:账户密码 / 自定义类型 / **备份恢复**
|
||||||
- 每日自动备份到 `/root/crypto_key_backups/`(无权限时回退到项目 `backups/`)
|
- 每日自动备份到 `/root/crypto_key_backups/`
|
||||||
- 旧版明文 `data.json` 自动迁移加密
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Ubuntu 一键部署(交互式)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo git clone https://git.bz121.com/dekun/crypto_key.git /opt/crypto_key
|
|
||||||
cd /opt/crypto_key
|
|
||||||
sudo bash scripts/manage.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
菜单:
|
|
||||||
|
|
||||||
1. 一键部署
|
|
||||||
2. 更新(**仅 git pull**,严禁 scp)
|
|
||||||
3. 一键卸载
|
|
||||||
|
|
||||||
详见 [DEPLOY.md](./DEPLOY.md)。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -82,7 +81,7 @@ sudo bash scripts/manage.sh
|
|||||||
| `env_config.py` | `.env` 管理 |
|
| `env_config.py` | `.env` 管理 |
|
||||||
| `index.html` | 前端单页 |
|
| `index.html` | 前端单页 |
|
||||||
| `ecosystem.config.cjs` | PM2 配置 |
|
| `ecosystem.config.cjs` | PM2 配置 |
|
||||||
| `scripts/manage.sh` | 交互式部署/更新/卸载 |
|
| `deploy/manage.sh` | 交互式一键部署 / 更新 / 卸载 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -91,13 +90,3 @@ sudo bash scripts/manage.sh
|
|||||||
- 仅在可信局域网使用,勿将 5200 映射到公网
|
- 仅在可信局域网使用,勿将 5200 映射到公网
|
||||||
- 定期备份 `.env` 与 `data.json`(密钥丢失将无法解密)
|
- 定期备份 `.env` 与 `data.json`(密钥丢失将无法解密)
|
||||||
- 勿将 `.env`、`data.json`、`uploads/` 提交到 Git
|
- 勿将 `.env`、`data.json`、`uploads/` 提交到 Git
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 依赖
|
|
||||||
|
|
||||||
```
|
|
||||||
flask, python-dotenv, cryptography, werkzeug
|
|
||||||
```
|
|
||||||
|
|
||||||
服务器 PM2 部署另需 Node.js 16+。
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
|------|------|
|
|------|------|
|
||||||
| **隐藏敏感字段** | 勾选后以圆点显示;复制仍为明文 |
|
| **隐藏敏感字段** | 勾选后以圆点显示;复制仍为明文 |
|
||||||
| **复制** | 字段右侧「复制」 |
|
| **复制** | 字段右侧「复制」 |
|
||||||
| **图片** | 助记词/密钥等记录可预览已上传图片 |
|
| **图片** | 助记词/密钥等记录可预览已上传图片;**点击图片可放大**,按 Esc 或点遮罩关闭 |
|
||||||
| **删除** | 卡片右上角,需确认 |
|
| **删除** | 卡片右上角,需确认 |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -145,7 +145,13 @@ A:仅支持 jpg/png/gif/webp/bmp,单文件不超过约 16MB。
|
|||||||
A:确认备份包内 `.env` 的 `DATA_ENCRYPTION_KEY` 完整。
|
A:确认备份包内 `.env` 的 `DATA_ENCRYPTION_KEY` 完整。
|
||||||
|
|
||||||
**Q:更新代码怎么做?**
|
**Q:更新代码怎么做?**
|
||||||
A:服务器上 `sudo bash scripts/manage.sh` → 选 **2 更新**(仅 git pull,不要用 scp)。
|
A:服务器执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
选择 **2 更新**(仅 git pull,不要用 scp)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,321 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# crypto_key 部署管理器 — 一键部署 / 更新 / 卸载
|
||||||
|
#
|
||||||
|
# 新服务器(免预先克隆):
|
||||||
|
# curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
|
#
|
||||||
|
# 已安装:
|
||||||
|
# bash /opt/crypto_key/deploy/manage.sh
|
||||||
|
#
|
||||||
|
# 更新一律 git pull,严禁 scp。
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
if [ -n "${BASH_VERSION:-}" ]; then
|
||||||
|
set -o pipefail
|
||||||
|
fi
|
||||||
|
|
||||||
|
INSTALL_ROOT="${INSTALL_ROOT:-/opt/crypto_key}"
|
||||||
|
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/crypto_key.git}"
|
||||||
|
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||||
|
APP_NAME="api-key-manager"
|
||||||
|
BACKUP_ROOT="/root/crypto_key_backups"
|
||||||
|
CRON_MARKER="# crypto_key-auto-backup"
|
||||||
|
|
||||||
|
# curl | bash 时脚本从 stdin 执行,BASH_SOURCE[0] 为空;须先安全探测再 set -u
|
||||||
|
_script_src="${BASH_SOURCE[0]:-}"
|
||||||
|
_from_pipe=0
|
||||||
|
if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then
|
||||||
|
DEPLOY_DIR="$(cd "$(dirname "${_script_src}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
||||||
|
else
|
||||||
|
DEPLOY_DIR=""
|
||||||
|
REPO_ROOT=""
|
||||||
|
_from_pipe=1
|
||||||
|
fi
|
||||||
|
unset _script_src
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
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 "==> $*"; }
|
||||||
|
|
||||||
|
# 从终端读入,兼容 curl | bash(管道 stdin 已 EOF)
|
||||||
|
cm_read() {
|
||||||
|
local __var="$1"
|
||||||
|
local __prompt="${2:-}"
|
||||||
|
local __val=""
|
||||||
|
if [[ -r /dev/tty ]]; then
|
||||||
|
printf "%s" "${__prompt}" > /dev/tty
|
||||||
|
IFS= read -r __val < /dev/tty || true
|
||||||
|
else
|
||||||
|
printf "%s" "${__prompt}"
|
||||||
|
IFS= read -r __val || true
|
||||||
|
fi
|
||||||
|
printf -v "${__var}" '%s' "${__val}"
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [[ "$(id -u)" -ne 0 ]]; then
|
||||||
|
red "错误: 请使用 root 执行"
|
||||||
|
echo " curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
repo_ready() {
|
||||||
|
[[ -f "${1}/deploy/manage.sh" && -f "${1}/app.py" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_repo_if_present() {
|
||||||
|
local root="$1"
|
||||||
|
if [[ -d "${root}/.git" ]] && command -v git >/dev/null 2>&1; then
|
||||||
|
info "git pull(${root})..."
|
||||||
|
git -C "${root}" fetch --all --prune 2>/dev/null || true
|
||||||
|
git -C "${root}" pull --ff-only origin "${GIT_BRANCH}" 2>/dev/null \
|
||||||
|
|| git -C "${root}" pull --ff-only 2>/dev/null \
|
||||||
|
|| true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
reexec_local() {
|
||||||
|
exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" </dev/tty
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap_repo() {
|
||||||
|
if repo_ready "${INSTALL_ROOT}"; then
|
||||||
|
REPO_ROOT="${INSTALL_ROOT}"
|
||||||
|
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
||||||
|
sync_repo_if_present "${REPO_ROOT}"
|
||||||
|
# 管道执行时切到仓库内最新脚本,并从终端读菜单
|
||||||
|
if [[ "${_from_pipe}" -eq 1 ]]; then
|
||||||
|
reexec_local "$@"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||||
|
# 在源码树内直接运行(开发/已 clone 目录)
|
||||||
|
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "crypto_key 部署管理器 — 首次自举"
|
||||||
|
echo "将克隆到: ${INSTALL_ROOT}"
|
||||||
|
require_root
|
||||||
|
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y git ca-certificates curl
|
||||||
|
else
|
||||||
|
red "错误: 未找到 git"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||||
|
red "错误: ${INSTALL_ROOT} 已存在但不是有效仓库"
|
||||||
|
echo "请手动处理后再运行,或设置 INSTALL_ROOT 指向其它路径"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "${INSTALL_ROOT}")"
|
||||||
|
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||||
|
# clone 后用仓库内脚本 + 终端 stdin(管道已 EOF)
|
||||||
|
reexec_local "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
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 20(PM2 需要)..."
|
||||||
|
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_ROOT}"
|
||||||
|
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 deploy/manage.sh 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_env() {
|
||||||
|
cd "${INSTALL_ROOT}"
|
||||||
|
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_ROOT} && ./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() {
|
||||||
|
cd "${INSTALL_ROOT}"
|
||||||
|
pm2 delete "${APP_NAME}" 2>/dev/null || true
|
||||||
|
pm2 start ecosystem.config.cjs
|
||||||
|
pm2 save
|
||||||
|
}
|
||||||
|
|
||||||
|
do_deploy() {
|
||||||
|
require_root
|
||||||
|
info "一键部署到 ${INSTALL_ROOT}"
|
||||||
|
install_deps
|
||||||
|
mkdir -p /opt
|
||||||
|
if [[ -d "${INSTALL_ROOT}/.git" ]]; then
|
||||||
|
info "目录已存在,执行 git pull(不用 scp)..."
|
||||||
|
sync_repo_if_present "${INSTALL_ROOT}"
|
||||||
|
else
|
||||||
|
info "git clone ${GIT_URL} ..."
|
||||||
|
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||||
|
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_ROOT}"
|
||||||
|
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 "日常管理: bash ${INSTALL_ROOT}/deploy/manage.sh"
|
||||||
|
echo "或: curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash"
|
||||||
|
echo "开机自启: pm2 startup (按提示执行后再 pm2 save)"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_update() {
|
||||||
|
require_root
|
||||||
|
if [[ ! -d "${INSTALL_ROOT}/.git" ]]; then
|
||||||
|
red "未找到 ${INSTALL_ROOT},请先选择「1 一键部署」"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
info "更新(仅 git pull,严禁 scp)..."
|
||||||
|
cd "${INSTALL_ROOT}"
|
||||||
|
|
||||||
|
mkdir -p "${BACKUP_ROOT}"
|
||||||
|
if [[ -f "${INSTALL_ROOT}/venv/bin/python" ]]; then
|
||||||
|
"${INSTALL_ROOT}/venv/bin/python" -c "from backup import create_backup_archive; print(create_backup_archive())" \
|
||||||
|
&& grn "更新前已自动备份" || ylw "更新前备份跳过(可手动备份)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sync_repo_if_present "${INSTALL_ROOT}"
|
||||||
|
setup_python
|
||||||
|
setup_backup_cron
|
||||||
|
start_pm2
|
||||||
|
grn "更新完成(git pull + 依赖 + PM2 重启)"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_uninstall() {
|
||||||
|
require_root
|
||||||
|
ylw "即将卸载 crypto_key"
|
||||||
|
echo " - 停止并删除 PM2 进程 ${APP_NAME}"
|
||||||
|
echo " - 移除自动备份 cron"
|
||||||
|
echo " - 删除目录 ${INSTALL_ROOT}"
|
||||||
|
echo " - 保留 ${BACKUP_ROOT}(如存在)"
|
||||||
|
local confirm=""
|
||||||
|
cm_read confirm "确认卸载?输入 yes 继续: "
|
||||||
|
if [[ "${confirm}" != "yes" ]]; then
|
||||||
|
echo "已取消"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
pm2 delete "${APP_NAME}" 2>/dev/null || true
|
||||||
|
pm2 save 2>/dev/null || true
|
||||||
|
remove_backup_cron
|
||||||
|
|
||||||
|
if [[ -d "${INSTALL_ROOT}" ]]; 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_ROOT}" .env data.json settings.json uploads 2>/dev/null || true
|
||||||
|
rm -rf "${INSTALL_ROOT}"
|
||||||
|
grn "已删除 ${INSTALL_ROOT}"
|
||||||
|
fi
|
||||||
|
grn "卸载完成。备份目录保留在: ${BACKUP_ROOT}"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_menu() {
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " crypto_key 管理脚本"
|
||||||
|
echo " 仓库: ${GIT_URL}"
|
||||||
|
echo " 部署: ${INSTALL_ROOT}"
|
||||||
|
echo "========================================"
|
||||||
|
echo " 1) 一键部署"
|
||||||
|
echo " 2) 更新(git pull,严禁 scp)"
|
||||||
|
echo " 3) 一键卸载"
|
||||||
|
echo " 0) 退出"
|
||||||
|
echo "========================================"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
bootstrap_repo "$@"
|
||||||
|
|
||||||
|
# 非交互参数
|
||||||
|
case "${1:-}" in
|
||||||
|
deploy|1) do_deploy; exit 0 ;;
|
||||||
|
update|2) do_update; exit 0 ;;
|
||||||
|
uninstall|3) do_uninstall; exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
require_root
|
||||||
|
while true; do
|
||||||
|
show_menu
|
||||||
|
local choice=""
|
||||||
|
cm_read choice "请选择 [0-3]: "
|
||||||
|
case "${choice}" in
|
||||||
|
1) do_deploy ;;
|
||||||
|
2) do_update ;;
|
||||||
|
3) do_uninstall ;;
|
||||||
|
0) echo "再见"; exit 0 ;;
|
||||||
|
*) red "无效选项" ;;
|
||||||
|
esac
|
||||||
|
echo ""
|
||||||
|
cm_read _ "按 Enter 返回菜单..."
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
+59
-2
@@ -79,8 +79,27 @@
|
|||||||
}
|
}
|
||||||
.tab:hover { color: var(--text); }
|
.tab:hover { color: var(--text); }
|
||||||
.tab.on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
|
.tab.on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
|
||||||
.img-preview { max-width: 220px; max-height: 160px; border-radius: 6px; border: 1px solid var(--border); margin-top: 8px; display: block; }
|
.img-preview {
|
||||||
|
max-width: 220px; max-height: 160px; border-radius: 6px; border: 1px solid var(--border);
|
||||||
|
margin-top: 8px; display: block; cursor: zoom-in;
|
||||||
|
}
|
||||||
.img-preview.lg { max-width: 320px; max-height: 240px; }
|
.img-preview.lg { max-width: 320px; max-height: 240px; }
|
||||||
|
.lightbox {
|
||||||
|
position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.88);
|
||||||
|
display: flex; align-items: center; justify-content: center; padding: 24px;
|
||||||
|
cursor: zoom-out;
|
||||||
|
}
|
||||||
|
.lightbox.hidden { display: none !important; }
|
||||||
|
.lightbox img {
|
||||||
|
max-width: min(96vw, 1200px); max-height: 92vh; object-fit: contain;
|
||||||
|
border-radius: 8px; box-shadow: 0 8px 40px rgba(0,0,0,.5); cursor: default;
|
||||||
|
}
|
||||||
|
.lightbox-close {
|
||||||
|
position: absolute; top: 16px; right: 16px; background: transparent;
|
||||||
|
border: 1px solid var(--border); color: var(--text); border-radius: 6px;
|
||||||
|
padding: 6px 12px; cursor: pointer; font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.lightbox-close:hover { border-color: var(--accent); color: var(--accent); }
|
||||||
.backup-info { font-size: 0.82rem; color: var(--muted); line-height: 1.7; margin: 10px 0 16px; }
|
.backup-info { font-size: 0.82rem; color: var(--muted); line-height: 1.7; margin: 10px 0 16px; }
|
||||||
.backup-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
|
.backup-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
|
||||||
</style>
|
</style>
|
||||||
@@ -232,6 +251,11 @@
|
|||||||
|
|
||||||
<div id="toast" class="toast"></div>
|
<div id="toast" class="toast"></div>
|
||||||
|
|
||||||
|
<div id="lightbox" class="lightbox hidden" role="dialog" aria-modal="true" aria-label="图片预览">
|
||||||
|
<button type="button" class="lightbox-close" id="lightboxClose">关闭</button>
|
||||||
|
<img id="lightboxImg" alt="放大预览">
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const EX_LABEL = { binance: "Binance", okx: "OKX", gate: "Gate" };
|
const EX_LABEL = { binance: "Binance", okx: "OKX", gate: "Gate" };
|
||||||
|
|
||||||
@@ -271,6 +295,37 @@
|
|||||||
toast._t = setTimeout(() => t.classList.remove("on"), 1800);
|
toast._t = setTimeout(() => t.classList.remove("on"), 1800);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openLightbox(src) {
|
||||||
|
if (!src) return;
|
||||||
|
$("lightboxImg").src = src;
|
||||||
|
$("lightbox").classList.remove("hidden");
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLightbox() {
|
||||||
|
$("lightbox").classList.add("hidden");
|
||||||
|
$("lightboxImg").src = "";
|
||||||
|
document.body.style.overflow = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
$("lightboxClose").onclick = (e) => { e.stopPropagation(); closeLightbox(); };
|
||||||
|
$("lightbox").onclick = (e) => {
|
||||||
|
if (e.target === $("lightbox") || e.target === $("lightboxClose")) closeLightbox();
|
||||||
|
};
|
||||||
|
$("lightboxImg").onclick = (e) => e.stopPropagation();
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Escape" && !$("lightbox").classList.contains("hidden")) closeLightbox();
|
||||||
|
});
|
||||||
|
|
||||||
|
function bindZoom(img) {
|
||||||
|
img.title = "点击放大";
|
||||||
|
img.onclick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
openLightbox(img.src);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function isSensitive(fd) {
|
function isSensitive(fd) {
|
||||||
if (!fd) return false;
|
if (!fd) return false;
|
||||||
if (fd.secret || fd.type === "secret") return true;
|
if (fd.secret || fd.type === "secret") return true;
|
||||||
@@ -413,6 +468,7 @@
|
|||||||
file.id = "fld_" + fd.key;
|
file.id = "fld_" + fd.key;
|
||||||
const preview = document.createElement("img");
|
const preview = document.createElement("img");
|
||||||
preview.className = "img-preview hidden";
|
preview.className = "img-preview hidden";
|
||||||
|
bindZoom(preview);
|
||||||
const status = document.createElement("p");
|
const status = document.createElement("p");
|
||||||
status.className = "sub";
|
status.className = "sub";
|
||||||
status.style.marginTop = "6px";
|
status.style.marginTop = "6px";
|
||||||
@@ -424,7 +480,7 @@
|
|||||||
imageDrafts[fd.key] = data.path;
|
imageDrafts[fd.key] = data.path;
|
||||||
preview.src = data.url;
|
preview.src = data.url;
|
||||||
preview.classList.remove("hidden");
|
preview.classList.remove("hidden");
|
||||||
status.textContent = "已上传";
|
status.textContent = "已上传 · 点击图片可放大";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
status.textContent = e.message || "上传失败";
|
status.textContent = e.message || "上传失败";
|
||||||
status.className = "err";
|
status.className = "err";
|
||||||
@@ -530,6 +586,7 @@
|
|||||||
img.className = "img-preview lg";
|
img.className = "img-preview lg";
|
||||||
img.src = `/api/uploads/${encodeURIComponent(val)}`;
|
img.src = `/api/uploads/${encodeURIComponent(val)}`;
|
||||||
img.alt = fd.label;
|
img.alt = fd.label;
|
||||||
|
bindZoom(img);
|
||||||
v.appendChild(img);
|
v.appendChild(img);
|
||||||
row.appendChild(v);
|
row.appendChild(v);
|
||||||
card.appendChild(row);
|
card.appendChild(row);
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
# PM2 重启脚本 (Windows PowerShell)
|
|
||||||
# 用法: .\pm2-restart.ps1
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
if (-not (Get-Command pm2 -ErrorAction SilentlyContinue)) {
|
|
||||||
Write-Host "[错误] 未找到 pm2" -ForegroundColor Red
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pm2 restart api-key-manager
|
|
||||||
Write-Host "已重启 api-key-manager" -ForegroundColor Green
|
|
||||||
$lan = (Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -notmatch '^127\.' } | Select-Object -First 1).IPAddress
|
|
||||||
Write-Host "本机: http://127.0.0.1:5200" -ForegroundColor Cyan
|
|
||||||
if ($lan) { Write-Host "局域网: http://${lan}:5200" -ForegroundColor Cyan }
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# PM2 启动脚本 (Windows PowerShell)
|
|
||||||
# 用法: .\pm2-start.ps1
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
||||||
Set-Location $Root
|
|
||||||
|
|
||||||
if (-not (Get-Command pm2 -ErrorAction SilentlyContinue)) {
|
|
||||||
Write-Host "[错误] 未找到 pm2,请先执行: npm install -g pm2" -ForegroundColor Red
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$venvPy = Join-Path $Root "venv\Scripts\python.exe"
|
|
||||||
if (-not (Test-Path $venvPy)) {
|
|
||||||
Write-Host "[警告] 未检测到 venv,请先运行: python -m venv venv ; .\venv\Scripts\activate ; pip install -r requirements.txt" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Test-Path "logs")) {
|
|
||||||
New-Item -ItemType Directory -Path "logs" | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
pm2 start ecosystem.config.cjs
|
|
||||||
pm2 save 2>$null
|
|
||||||
Write-Host ""
|
|
||||||
$lan = (Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -notmatch '^127\.' -and $_.PrefixOrigin -ne 'WellKnown' } | Select-Object -First 1).IPAddress
|
|
||||||
Write-Host "已启动。本机: http://127.0.0.1:5200" -ForegroundColor Green
|
|
||||||
if ($lan) { Write-Host "局域网: http://${lan}:5200" -ForegroundColor Green }
|
|
||||||
Write-Host "查看状态: pm2 status" -ForegroundColor Cyan
|
|
||||||
Write-Host "查看日志: pm2 logs api-key-manager" -ForegroundColor Cyan
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# PM2 停止脚本 (Windows PowerShell)
|
|
||||||
# 用法: .\pm2-stop.ps1
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
if (-not (Get-Command pm2 -ErrorAction SilentlyContinue)) {
|
|
||||||
Write-Host "[错误] 未找到 pm2" -ForegroundColor Red
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pm2 stop api-key-manager
|
|
||||||
Write-Host "已停止 api-key-manager" -ForegroundColor Green
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 兼容入口:转调交互式管理脚本的一键部署
|
# 兼容入口 → deploy/manage.sh deploy
|
||||||
# 推荐直接使用: sudo bash scripts/manage.sh
|
# 推荐: curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
exec bash "${SCRIPT_DIR}/manage.sh" deploy
|
exec bash "${SCRIPT_DIR}/../deploy/manage.sh" deploy
|
||||||
|
|||||||
+4
-230
@@ -1,232 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# crypto_key 交互式管理脚本
|
# 兼容入口 → deploy/manage.sh
|
||||||
# 用法: sudo bash scripts/manage.sh
|
# 推荐: curl -fsSL https://git.bz121.com/dekun/crypto_key/raw/branch/main/deploy/manage.sh | bash
|
||||||
# 菜单: 1 一键部署 | 2 更新(git pull) | 3 一键卸载
|
|
||||||
# 严禁使用 scp;所有代码更新一律 git pull
|
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
REPO_URL="https://git.bz121.com/dekun/crypto_key.git"
|
exec bash "${SCRIPT_DIR}/../deploy/manage.sh" "$@"
|
||||||
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 20(PM2 需要)..."
|
|
||||||
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 "$@"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user