From 0203a65973d139dbe77d0d95359144d295812524 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 19 May 2026 00:54:27 +0800 Subject: [PATCH] Initial commit: crypto_key API key manager with PM2 and Ubuntu deploy docs Co-authored-by: Cursor --- .gitignore | 4 + DEPLOY.md | 287 +++++++++++++++++++++++ README.md | 106 +++++++++ app.py | 83 +++++++ ecosystem.config.cjs | 38 +++ index.html | 473 ++++++++++++++++++++++++++++++++++++++ logs/.gitkeep | 0 pm2-restart.ps1 | 13 ++ pm2-start.ps1 | 27 +++ pm2-start.sh | 24 ++ pm2-stop.ps1 | 12 + pm2-stop.sh | 6 + requirements.txt | 1 + scripts/install-ubuntu.sh | 66 ++++++ 14 files changed, 1140 insertions(+) create mode 100644 .gitignore create mode 100644 DEPLOY.md create mode 100644 README.md create mode 100644 app.py create mode 100644 ecosystem.config.cjs create mode 100644 index.html create mode 100644 logs/.gitkeep create mode 100644 pm2-restart.ps1 create mode 100644 pm2-start.ps1 create mode 100644 pm2-start.sh create mode 100644 pm2-stop.ps1 create mode 100644 pm2-stop.sh create mode 100644 requirements.txt create mode 100644 scripts/install-ubuntu.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cb197f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +venv/ +logs/*.log +data.json +__pycache__/ diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..f4857e0 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,287 @@ +# 部署文档 — crypto_key(Ubuntu /opt) + +> 仓库地址:[https://git.bz121.com/dekun/crypto_key](https://git.bz121.com/dekun/crypto_key.git) +> 部署路径:**`/opt/crypto_key`** +> 进程守护:**PM2** · 服务端口:**5200** + +币圈 API 密钥本地管理工具。数据保存在服务器本地 `data.json`,无登录,默认仅监听 `127.0.0.1`。 + +--- + +## 一、架构 + +``` +浏览器 / SSH 隧道 → http://127.0.0.1:5200 + ↓ + PM2 (api-key-manager) + ↓ + /opt/crypto_key/venv/python app.py + ↓ + /opt/crypto_key/data.json +``` + +| 路径 | 说明 | +|------|------| +| `/opt/crypto_key` | 项目根目录(从 Git 克隆) | +| `/opt/crypto_key/data.json` | 账户数据(不纳入 Git) | +| `/opt/crypto_key/logs/` | PM2 日志 | +| `/opt/crypto_key/venv/` | Python 虚拟环境 | + +--- + +## 二、服务器要求 + +| 项目 | 要求 | +|------|------| +| 系统 | Ubuntu 20.04 / 22.04 / 24.04 LTS | +| 权限 | 具备 `sudo` 的普通用户或 root | +| Python | 3.8+(`python3`) | +| Node.js | 16+(用于 PM2) | +| 磁盘 | ≥ 100MB | + +--- + +## 三、首次部署(推荐:一键脚本) + +### 3.1 克隆仓库并执行安装 + +```bash +# 若仓库尚未克隆,可先克隆再安装 +sudo mkdir -p /opt +sudo git clone https://git.bz121.com/dekun/crypto_key.git /opt/crypto_key +cd /opt/crypto_key +sudo bash scripts/install-ubuntu.sh +``` + +脚本将自动完成:安装 `python3` / `git` / `Node.js` / `PM2` → 创建 `venv` → 安装 Flask → PM2 启动服务。 + +### 3.2 验证 + +```bash +pm2 status +curl -s http://127.0.0.1:5200/api/accounts +``` + +浏览器或 SSH 端口转发访问:**http://127.0.0.1:5200** + +```bash +# 本机 SSH 转发示例(在你自己的电脑上执行) +ssh -L 5200:127.0.0.1:5200 user@your-server-ip +# 然后浏览器打开 http://127.0.0.1:5200 +``` + +--- + +## 四、手动部署(逐步) + +适合需要自定义权限或不用一键脚本的环境。 + +### 4.1 安装系统依赖 + +```bash +sudo apt update +sudo apt install -y python3 python3-venv python3-pip git curl + +# Node.js + PM2 +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt install -y nodejs +sudo npm install -g pm2 +``` + +### 4.2 克隆到 /opt + +```bash +sudo mkdir -p /opt +sudo git clone https://git.bz121.com/dekun/crypto_key.git /opt/crypto_key +cd /opt/crypto_key +``` + +若目录已存在,更新代码: + +```bash +cd /opt/crypto_key +sudo git pull +``` + +### 4.3 Python 环境 + +```bash +cd /opt/crypto_key +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +### 4.4 PM2 启动 + +```bash +cd /opt/crypto_key +mkdir -p logs +chmod +x pm2-start.sh pm2-stop.sh +./pm2-start.sh +``` + +或: + +```bash +pm2 start ecosystem.config.cjs +pm2 save +``` + +### 4.5 目录权限(可选) + +若用普通用户运行 PM2,建议将项目目录属主改为该用户: + +```bash +sudo chown -R $USER:$USER /opt/crypto_key +``` + +--- + +## 五、开机自启 + +```bash +pm2 startup +# 终端会输出一行 sudo 命令,复制并执行 +pm2 save +``` + +重启后检查: + +```bash +pm2 status +# api-key-manager 应为 online +``` + +--- + +## 六、日常运维 + +### 查看状态与日志 + +```bash +pm2 status +pm2 logs api-key-manager +pm2 logs api-key-manager --lines 100 +``` + +### 重启 / 停止 + +```bash +cd /opt/crypto_key +./pm2-start.sh # 启动 +# 或 +pm2 restart api-key-manager + +./pm2-stop.sh # 停止 +# 或 +pm2 stop api-key-manager +``` + +### 更新代码 + +```bash +cd /opt/crypto_key +git pull +source venv/bin/activate +pip install -r requirements.txt +pm2 restart api-key-manager +``` + +### 备份数据 + +```bash +sudo cp /opt/crypto_key/data.json /opt/crypto_key/data.json.bak.$(date +%F) +``` + +`data.json` 含明文 API 密钥,请加密存储或限制文件权限: + +```bash +chmod 600 /opt/crypto_key/data.json +``` + +--- + +## 七、PM2 配置说明 + +文件:`/opt/crypto_key/ecosystem.config.cjs` + +| 配置 | 值 | +|------|-----| +| 进程名 | `api-key-manager` | +| 工作目录 | 项目根目录(自动) | +| Python | `venv/bin/python` | +| 自动重启 | 是 | +| 日志 | `logs/pm2-out.log` / `logs/pm2-error.log` | + +--- + +## 八、项目文件结构 + +``` +/opt/crypto_key/ +├── app.py +├── index.html +├── requirements.txt +├── ecosystem.config.cjs +├── pm2-start.sh +├── pm2-stop.sh +├── scripts/ +│ └── install-ubuntu.sh +├── logs/ +├── venv/ +├── data.json # 运行时生成,已 .gitignore +├── README.md +└── DEPLOY.md +``` + +--- + +## 九、故障排查 + +| 现象 | 处理 | +|------|------| +| `pm2 status` 为 errored | `pm2 logs api-key-manager --err`;确认 `venv` 已创建 | +| 端口 5200 占用 | `sudo ss -tlnp \| grep 5200`;修改 `app.py` 中 `port` 后 `pm2 restart` | +| `git clone` 需认证 | 配置 SSH Key 或 `git config credential.helper store` | +| 无法访问页面 | 服务仅监听 127.0.0.1,需 SSH 隧道或在本机 curl | +| 更新后异常 | `pip install -r requirements.txt` 后 `pm2 restart api-key-manager` | + +--- + +## 十、安全说明 + +- 默认绑定 **127.0.0.1**,不对外网直接暴露。 +- **无登录鉴权**,切勿将 `0.0.0.0:5200` 开放到公网。 +- `data.json` 已加入 `.gitignore`,不会推送到仓库。 +- 建议在防火墙仅允许 SSH,通过隧道访问管理页。 + +--- + +## 十一、Windows 本地开发(附录) + +```powershell +cd C:\path\to\crypto_key +python -m venv venv +.\venv\Scripts\activate +pip install -r requirements.txt +npm install -g pm2 +.\pm2-start.ps1 +``` + +访问:http://127.0.0.1:5200 + +--- + +## 十二、命令速查 + +```bash +# 首次部署 +sudo git clone https://git.bz121.com/dekun/crypto_key.git /opt/crypto_key +cd /opt/crypto_key && sudo bash scripts/install-ubuntu.sh + +# 日常 +pm2 status +pm2 restart api-key-manager +cd /opt/crypto_key && git pull && pm2 restart api-key-manager +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9a9960 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# crypto_key — 币圈 API 密钥管理工具 + +本地 Web 工具,管理多个交易所/平台 API 账户(账户名称、API Key、API Secret)。数据保存在本地 `data.json`,无登录。 + +| 项目 | 说明 | +|------|------| +| 仓库 | [git.bz121.com/dekun/crypto_key](https://git.bz121.com/dekun/crypto_key.git) | +| Ubuntu 部署路径 | `/opt/crypto_key` | +| 服务端口 | `5200` | +| 进程守护 | PM2 | + +--- + +## 功能 + +- 账户数量不限,每项含 `username` / `api_key` / `api_secret` +- 黑色专业界面,列表展示 + 每项 3 个复制按钮(**复制始终为明文**) +- 可选界面打码显示,不影响复制内容 +- 数据持久化至 `data.json` + +--- + +## Ubuntu 服务器部署(/opt) + +完整步骤见 **[DEPLOY.md](./DEPLOY.md)**。 + +```bash +sudo git clone https://git.bz121.com/dekun/crypto_key.git /opt/crypto_key +cd /opt/crypto_key +sudo bash scripts/install-ubuntu.sh +``` + +访问(需在本机或通过 SSH 隧道):**http://127.0.0.1:5200** + +```bash +pm2 status +pm2 logs api-key-manager +``` + +--- + +## 本地开发 + +```bash +python3 -m venv venv +source venv/bin/activate # Windows: venv\Scripts\activate +pip install -r requirements.txt +python app.py +``` + +浏览器打开 http://127.0.0.1:5200 + +PM2 守护:`./pm2-start.sh`(Linux)或 `.\pm2-start.ps1`(Windows) + +--- + +## 文件说明 + +| 文件 | 说明 | +|------|------| +| `app.py` | Flask 后端 | +| `index.html` | 前端页面 | +| `ecosystem.config.cjs` | PM2 配置 | +| `scripts/install-ubuntu.sh` | Ubuntu 一键安装 | +| `DEPLOY.md` | 完整部署文档 | + +--- + +## 数据格式 + +```json +{ + "id": "uuid", + "username": "账户名称", + "api_key": "API Key", + "api_secret": "API Secret" +} +``` + +--- + +## API + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/` | 前端页面 | +| GET | `/api/accounts` | 获取全部账户 | +| POST | `/api/accounts` | 新增账户 | +| DELETE | `/api/accounts/` | 删除账户 | + +--- + +## 安全提示 + +- 无认证,仅绑定 `127.0.0.1`,请勿暴露公网 +- `data.json` 为明文密钥,已 `.gitignore`,请定期备份 + +--- + +## 依赖 + +``` +flask>=3.0.0,<4.0.0 +``` + +服务器另需:Python 3.8+、Node.js(PM2) diff --git a/app.py b/app.py new file mode 100644 index 0000000..0e39189 --- /dev/null +++ b/app.py @@ -0,0 +1,83 @@ +""" +多账户 API 密钥管理工具 — Flask 后端 +端口: 5200 | 数据: data.json +""" +import json +import uuid +from pathlib import Path + +from flask import Flask, jsonify, request, send_from_directory + +app = Flask(__name__) + +BASE_DIR = Path(__file__).resolve().parent +DATA_FILE = BASE_DIR / "data.json" + + +def load_accounts(): + if not DATA_FILE.exists(): + return [] + try: + with open(DATA_FILE, "r", encoding="utf-8") as f: + data = json.load(f) + if isinstance(data, list): + return data + return [] + except (json.JSONDecodeError, OSError): + return [] + + +def save_accounts(accounts): + with open(DATA_FILE, "w", encoding="utf-8") as f: + json.dump(accounts, f, ensure_ascii=False, indent=2) + + +def validate_account_payload(payload): + username = (payload.get("username") or "").strip() + api_key = (payload.get("api_key") or "").strip() + api_secret = (payload.get("api_secret") or "").strip() + if not username: + return None, "账户名称不能为空" + if not api_key: + return None, "API Key 不能为空" + if not api_secret: + return None, "API Secret 不能为空" + return {"username": username, "api_key": api_key, "api_secret": api_secret}, None + + +@app.route("/") +def index(): + return send_from_directory(BASE_DIR, "index.html") + + +@app.route("/api/accounts", methods=["GET"]) +def list_accounts(): + return jsonify(load_accounts()) + + +@app.route("/api/accounts", methods=["POST"]) +def create_account(): + body = request.get_json(silent=True) or {} + account, err = validate_account_payload(body) + if err: + return jsonify({"error": err}), 400 + + accounts = load_accounts() + account["id"] = str(uuid.uuid4()) + accounts.append(account) + save_accounts(accounts) + return jsonify(account), 201 + + +@app.route("/api/accounts/", methods=["DELETE"]) +def delete_account(account_id): + accounts = load_accounts() + new_accounts = [a for a in accounts if a.get("id") != account_id] + if len(new_accounts) == len(accounts): + return jsonify({"error": "账户不存在"}), 404 + save_accounts(new_accounts) + return jsonify({"ok": True}) + + +if __name__ == "__main__": + app.run(host="127.0.0.1", port=5200, debug=False) diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs new file mode 100644 index 0000000..4f829c5 --- /dev/null +++ b/ecosystem.config.cjs @@ -0,0 +1,38 @@ +/** + * PM2 进程配置 — API 密钥管理工具 + * 用法: pm2 start ecosystem.config.cjs + */ +const path = require("path"); + +const ROOT = __dirname; +const isWin = process.platform === "win32"; + +// 优先使用项目内 venv;若无 venv,改为 "python" 或系统 Python 绝对路径 +const venvPython = isWin + ? path.join(ROOT, "venv", "Scripts", "python.exe") + : path.join(ROOT, "venv", "bin", "python"); + +module.exports = { + apps: [ + { + name: "api-key-manager", + script: "app.py", + cwd: ROOT, + interpreter: venvPython, + instances: 1, + exec_mode: "fork", + autorestart: true, + watch: false, + max_restarts: 10, + min_uptime: "5s", + max_memory_restart: "200M", + error_file: path.join(ROOT, "logs", "pm2-error.log"), + out_file: path.join(ROOT, "logs", "pm2-out.log"), + log_date_format: "YYYY-MM-DD HH:mm:ss", + merge_logs: true, + env: { + PYTHONUNBUFFERED: "1", + }, + }, + ], +}; diff --git a/index.html b/index.html new file mode 100644 index 0000000..ea53424 --- /dev/null +++ b/index.html @@ -0,0 +1,473 @@ + + + + + + API 密钥管理 + + + +
+
+

API 密钥管理

+

本地多账户存储 · 数据保存在 data.json

+
+ +
+

添加账户

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+ 共 0 个账户 + +
+ +
+
+ +
已复制到剪贴板
+ + + + diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pm2-restart.ps1 b/pm2-restart.ps1 new file mode 100644 index 0000000..283b6b3 --- /dev/null +++ b/pm2-restart.ps1 @@ -0,0 +1,13 @@ +# 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 +Write-Host "访问: http://127.0.0.1:5200" -ForegroundColor Cyan diff --git a/pm2-start.ps1 b/pm2-start.ps1 new file mode 100644 index 0000000..7f89471 --- /dev/null +++ b/pm2-start.ps1 @@ -0,0 +1,27 @@ +# 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 "" +Write-Host "已启动。访问: http://127.0.0.1:5200" -ForegroundColor Green +Write-Host "查看状态: pm2 status" -ForegroundColor Cyan +Write-Host "查看日志: pm2 logs api-key-manager" -ForegroundColor Cyan diff --git a/pm2-start.sh b/pm2-start.sh new file mode 100644 index 0000000..577d7aa --- /dev/null +++ b/pm2-start.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# PM2 启动脚本 (Linux / macOS) +# 用法: chmod +x pm2-start.sh && ./pm2-start.sh + +set -e +cd "$(dirname "$0")" + +if ! command -v pm2 >/dev/null 2>&1; then + echo "[错误] 未找到 pm2,请先执行: npm install -g pm2" + exit 1 +fi + +if [ ! -f "venv/bin/python" ]; then + echo "[警告] 未检测到 venv,请先: python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt" +fi + +mkdir -p logs +pm2 start ecosystem.config.cjs +pm2 save 2>/dev/null || true + +echo "" +echo "已启动。访问: http://127.0.0.1:5200" +echo "查看状态: pm2 status" +echo "查看日志: pm2 logs api-key-manager" diff --git a/pm2-stop.ps1 b/pm2-stop.ps1 new file mode 100644 index 0000000..6ce1dc6 --- /dev/null +++ b/pm2-stop.ps1 @@ -0,0 +1,12 @@ +# 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 diff --git a/pm2-stop.sh b/pm2-stop.sh new file mode 100644 index 0000000..d3074ff --- /dev/null +++ b/pm2-stop.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# PM2 停止脚本 (Linux / macOS) + +set -e +pm2 stop api-key-manager +echo "已停止 api-key-manager" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..757cc5b --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +flask>=3.0.0,<4.0.0 diff --git a/scripts/install-ubuntu.sh b/scripts/install-ubuntu.sh new file mode 100644 index 0000000..bbfbc55 --- /dev/null +++ b/scripts/install-ubuntu.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Ubuntu 一键安装 — 部署到 /opt/crypto_key +# 用法: sudo bash scripts/install-ubuntu.sh + +set -e + +REPO_URL="https://git.bz121.com/dekun/crypto_key.git" +INSTALL_DIR="/opt/crypto_key" +APP_USER="${SUDO_USER:-$USER}" + +echo "==> 安装系统依赖..." +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 + echo "==> 安装 Node.js (用于 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 + echo "==> 安装 PM2..." + npm install -g pm2 +fi + +echo "==> 拉取代码到 ${INSTALL_DIR} ..." +mkdir -p /opt +if [ -d "${INSTALL_DIR}/.git" ]; then + cd "${INSTALL_DIR}" + git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || git pull +else + git clone "${REPO_URL}" "${INSTALL_DIR}" + cd "${INSTALL_DIR}" +fi + +echo "==> 创建 Python 虚拟环境..." +python3 -m venv venv +source venv/bin/activate +pip install -q --upgrade pip +pip install -q -r requirements.txt + +mkdir -p logs +chmod +x pm2-start.sh pm2-stop.sh 2>/dev/null || true + +if [ -n "${APP_USER}" ] && [ "${APP_USER}" != "root" ]; then + chown -R "${APP_USER}:${APP_USER}" "${INSTALL_DIR}" +fi + +echo "==> 启动 PM2 守护..." +cd "${INSTALL_DIR}" +pm2 delete api-key-manager 2>/dev/null || true +pm2 start ecosystem.config.cjs +pm2 save + +echo "" +echo "==========================================" +echo " 安装完成" +echo " 目录: ${INSTALL_DIR}" +echo " 访问: http://127.0.0.1:5200" +echo " 状态: pm2 status" +echo " 日志: pm2 logs api-key-manager" +echo "==========================================" +echo "" +echo "开机自启请执行: pm2 startup" +echo "按提示复制 sudo 命令执行后,再运行: pm2 save"