新增 nginx 整站 Basic Auth(deploy.sh auth 子命令)

This commit is contained in:
2026-09-14 16:39:21 +08:00
parent fe65d1b653
commit 80a310d928
3 changed files with 89 additions and 5 deletions

View File

@@ -20,6 +20,7 @@ pnpm build # vue-tsc 类型检查 + vite build产出 dist/
- 界面文案统一 `Token`(首字母大写),表头样式禁用 `text-transform: uppercase` - 界面文案统一 `Token`(首字母大写),表头样式禁用 `text-transform: uppercase`
- 页面名分保持中立(「用量排行」「用量榜」),不加评价性徽章/高亮等引导元素。 - 页面名分保持中立(「用量排行」「用量榜」),不加评价性徽章/高亮等引导元素。
- 面向用户的品牌名是「行业应用」(页眉 eyebrow代码注释里指代后端系统仍可写 Sub2API不要全局替换。 - 面向用户的品牌名是「行业应用」(页眉 eyebrow代码注释里指代后端系统仍可写 Sub2API不要全局替换。
- 线上整站有 nginx HTTP Basic Auth`/api/``/data/``/bailian-admin/`),账号密码只在服务器 htpasswdapr1不进 git对线上做 curl/探测要么预期 401要么带 `-u`。开关与换人用 `deploy.sh auth`,不要手改 nginx 配置里的 auth_basic。
- 颜色走 `src/style.css` 的 CSS 变量:成本 `--accent` 橙、Token `--token` 钢蓝、金 `--gold`;图表内因 ECharts 读不到 CSS 变量而硬编码同值,改色时两处同步。 - 颜色走 `src/style.css` 的 CSS 变量:成本 `--accent` 橙、Token `--token` 钢蓝、金 `--gold`;图表内因 ECharts 读不到 CSS 变量而硬编码同值,改色时两处同步。
- 百炼 Token Plan 额度:数据来自服务器探针写出的静态快照 `/data/bailian.json`(契约见 `types.ts``BailianSnapshot``schema: 1`,含只读的 `reset_cards` 重置卡列表)。**Cookie/Key 只留在服务器 `/etc/bailian-probe.env`,绝不进前端产物、快照文件或 git探针bailian-probe.py只调 reset-card/list 等只读接口,绝不接 reset-card/use**。用卡写操作只有 `deploy/reset-card-server.py`(本机 127.0.0.1:18082nginx `/bailian-admin/`)在 Admin Key + 卡归属校验后才允许代发;分组页入口默认 CSS 隐藏,需 `localStorage.bailian-reset-card='1'` 开启。Sub2API 的智谱额度通道(账号 `extra.zhipu_*`)复用不到百炼:平台是 openai/anthropic、`account_mode` 为空、域名不在识别表内,别指望接进 `cn_provider_quota_service` - 百炼 Token Plan 额度:数据来自服务器探针写出的静态快照 `/data/bailian.json`(契约见 `types.ts``BailianSnapshot``schema: 1`,含只读的 `reset_cards` 重置卡列表)。**Cookie/Key 只留在服务器 `/etc/bailian-probe.env`,绝不进前端产物、快照文件或 git探针bailian-probe.py只调 reset-card/list 等只读接口,绝不接 reset-card/use**。用卡写操作只有 `deploy/reset-card-server.py`(本机 127.0.0.1:18082nginx `/bailian-admin/`)在 Admin Key + 卡归属校验后才允许代发;分组页入口默认 CSS 隐藏,需 `localStorage.bailian-reset-card='1'` 开启。Sub2API 的智谱额度通道(账号 `extra.zhipu_*`)复用不到百炼:平台是 openai/anthropic、`account_mode` 为空、域名不在识别表内,别指望接进 `cn_provider_quota_service`
@@ -28,4 +29,4 @@ pnpm build # vue-tsc 类型检查 + vite build产出 dist/
- `src/App.vue` 排行主页;`src/groups/GroupsApp.vue` 分组页(分组配 `src/groups.config.ts`,含百炼额度卡)。 - `src/App.vue` 排行主页;`src/groups/GroupsApp.vue` 分组页(分组配 `src/groups.config.ts`,含百炼额度卡)。
- `src/api.ts` 数据层Sub2API 接口 + 百炼快照读取,快照失败返回 null 不阻断页面);`src/format.ts` 格式化与时区Asia/Shanghai - `src/api.ts` 数据层Sub2API 接口 + 百炼快照读取,快照失败返回 null 不阻断页面);`src/format.ts` 格式化与时区Asia/Shanghai
- `deploy/bailian-probe.py` 百炼额度探针Python 3 标准库cookie 主路 / apikey 验证路);部署见 `deploy/DEPLOY.md` 第 5 节。 - `deploy/bailian-probe.py` 百炼额度探针Python 3 标准库cookie 主路 / apikey 验证路);部署见 `deploy/DEPLOY.md` 第 5 节。
- 生产部署为同域反代,见 `deploy/DEPLOY.md`现行环境10.1.192.225:18081 → `/usr/dcits/token-list-share`)的发布/换 Cookie 用根目录 `deploy.sh`web / cookie / probe / nginx / status / all - 生产部署为同域反代,见 `deploy/DEPLOY.md`现行环境10.1.192.225:18081 → `/usr/dcits/token-list-share`)的发布/换 Cookie 用根目录 `deploy.sh`web / cookie / probe / nginx / auth / status / all

View File

@@ -8,6 +8,9 @@
# bash deploy.sh cookie 'cna=…' # 直接以参数提供 Cookie会出现在 shell 历史,优先用交互方式) # bash deploy.sh cookie 'cna=…' # 直接以参数提供 Cookie会出现在 shell 历史,优先用交互方式)
# bash deploy.sh probe # 更新探针脚本与 systemd 单元,立即试跑 # bash deploy.sh probe # 更新探针脚本与 systemd 单元,立即试跑
# bash deploy.sh nginx # 安装/刷新 nginx 站点配置并 reload # bash deploy.sh nginx # 安装/刷新 nginx 站点配置并 reload
# bash deploy.sh auth # 交互式设置 Basic Auth 用户名/密码(不回显)并开启整站认证
# bash deploy.sh auth user pwd # 非交互设置(密码会进 shell 历史,优先用交互方式)
# bash deploy.sh auth off # 关闭 Basic Auth删除 htpasswd 并 reload
# bash deploy.sh status # 查看定时器、最新快照与 HTTP 自检 # bash deploy.sh status # 查看定时器、最新快照与 HTTP 自检
# bash deploy.sh all # nginx + web + probe不动 Cookie # bash deploy.sh all # nginx + web + probe不动 Cookie
# #
@@ -27,6 +30,7 @@ PROBE_BIN="$PROBE_DIR/bailian-probe.py"
RESET_CARD_BIN="$PROBE_DIR/reset-card-server.py" RESET_CARD_BIN="$PROBE_DIR/reset-card-server.py"
RESET_CARD_PORT="${RESET_CARD_PORT:-18082}" RESET_CARD_PORT="${RESET_CARD_PORT:-18082}"
SNAPSHOT="$REMOTE_DIR/www/data/bailian.json" SNAPSHOT="$REMOTE_DIR/www/data/bailian.json"
HTPASSWD=/etc/nginx/token-list.htpasswd
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -216,17 +220,87 @@ REMOTE
ok "探针已更新,定时器每 5 分钟跑一次" ok "探针已更新,定时器每 5 分钟跑一次"
} }
# ---------- auth ----------
# 在远端用 openssl apr1 生成 htpasswd 行Debian/Ubumtu 的 openssl 带 -apr1
cmd_auth() {
if [ "${2:-}" = "off" ]; then
log "关闭 Basic Auth"
ssh_r "rm -f '$HTPASSWD'"
cmd_nginx
ok "Basic Auth 已关闭"
return
fi
local user password
if [ "$#" -ge 3 ]; then
user="$2"; password="$3"
else
printf 'Basic Auth 用户名:' >&2
IFS= read -r user </dev/tty || true
printf '密码(输入不回显):' >&2
IFS= read -r -s password </dev/tty || true
printf '\n' >&2
fi
[ -n "$user" ] || die "用户名不能为空"
[ -n "$password" ] || die "密码不能为空"
# 密码经 600 临时文件透传,不出现在远端命令行/进程列表
local tmp; tmp="$(mktemp -t token-htpasswd-XXXXXX)"
chmod 600 "$tmp"
printf '%s' "$password" >"$tmp"
scp_r -q "$tmp" "$REMOTE_HOST:/run/.token-htpasswd.in"
rm -f "$tmp"
log "写入 $HTPASSWD 并刷新 nginx"
# stdout 输出除本次用户外仍保留的账号,用于提示旧账号仍可登录
local others
others="$(ssh_r 'bash -s' "$HTPASSWD" "$user" <<'REMOTE'
set -euo pipefail
HTPASSWD="$1"; USER_NAME="$2"
command -v openssl >/dev/null || { echo "✗ 远端没有 openssl无法生成 apr1 哈希" >&2; exit 1; }
[ -s /run/.token-htpasswd.in ] || { echo "✗ 密码临时文件缺失" >&2; exit 1; }
hash=$(openssl passwd -apr1 -in /run/.token-htpasswd.in)
rm -f /run/.token-htpasswd.in
mkdir -p "$(dirname "$HTPASSWD")"
# 同名用户覆盖更新,其余用户保留
if [ -f "$HTPASSWD" ]; then
grep -v "^${USER_NAME}:" "$HTPASSWD" > "$HTPASSWD.new" || true
else
: > "$HTPASSWD.new"
fi
printf '%s:%s\n' "$USER_NAME" "$hash" >> "$HTPASSWD.new"
install -m 640 "$HTPASSWD.new" "$HTPASSWD"
rm -f "$HTPASSWD.new"
chown root:www-data "$HTPASSWD" 2>/dev/null || chown root:nginx "$HTPASSWD" 2>/dev/null || true
cut -d: -f1 "$HTPASSWD" | grep -v "^${USER_NAME}$" || true
REMOTE
)"
cmd_nginx >/dev/null
ok "Basic Auth 已开启:用户 $user(浏览器访问整站时输入)"
if [ -n "$others" ]; then
printf '\033[1;33m! htpasswd 里还有其他账号仍可登录:%s\n' "$(printf '%s' "$others" | tr '\n' ' ')" >&2
printf ' 如要停用旧账号ssh %s "sed -i \x27/^<用户名>:/d\x27 %s"\033[0m\n' "$REMOTE_HOST" "$HTPASSWD" >&2
fi
}
# ---------- nginx ---------- # ---------- nginx ----------
cmd_nginx() { cmd_nginx() {
log "安装 nginx 站点(端口 $SITE_PORT,反代 $SUB2API_UPSTREAM" log "安装 nginx 站点(端口 $SITE_PORT,反代 $SUB2API_UPSTREAM"
# 先看远端 htpasswd 是否存在:存在就在整站 server 块开 Basic Auth
local auth_block=""
if ssh_r "test -s '$HTPASSWD'"; then
auth_block="$(printf ' auth_basic "token-list-share";\n auth_basic_user_file %s;\n' "$HTPASSWD")"
log "检测到 $HTPASSWD,开启 Basic Auth"
fi
local tmp; tmp="$(mktemp -t token-nginx-XXXXXX.conf)" local tmp; tmp="$(mktemp -t token-nginx-XXXXXX.conf)"
cat >"$tmp" <<EOF cat >"$tmp" <<EOF
server { server {
listen $SITE_PORT; listen $SITE_PORT;
listen [::]:$SITE_PORT; listen [::]:$SITE_PORT;
server_name _; server_name _;
$auth_block
location /api/ { location /api/ {
proxy_pass $SUB2API_UPSTREAM; proxy_pass $SUB2API_UPSTREAM;
proxy_http_version 1.1; proxy_http_version 1.1;
@@ -270,9 +344,9 @@ REMOTE
# ---------- status ---------- # ---------- status ----------
cmd_status() { cmd_status() {
ssh_r 'bash -s' "$SITE_PORT" "$SNAPSHOT" <<'REMOTE' ssh_r 'bash -s' "$SITE_PORT" "$SNAPSHOT" "$HTPASSWD" <<'REMOTE'
set -euo pipefail set -euo pipefail
PORT="$1"; SNAPSHOT="$2" PORT="$1"; SNAPSHOT="$2"; HTPASSWD="$3"
echo "== timer ==" echo "== timer =="
systemctl list-timers bailian-probe.timer --no-pager | head -3 systemctl list-timers bailian-probe.timer --no-pager | head -3
echo "== snapshot ==" echo "== snapshot =="
@@ -287,6 +361,10 @@ else
echo "(快照不存在)" echo "(快照不存在)"
fi fi
echo "== http ==" echo "== http =="
# 若开了 Basic Auth无凭证应统一 401哈希无法回放密码带凭证 200 需手工 curl -u 验证)
if [ -s "$HTPASSWD" ]; then
echo "Basic Auth 已开启:下面无凭证请求预期均为 401"
fi
for path in / /list.html /groups.html /data/bailian.json /api/v1/admin/groups/all; do for path in / /list.html /groups.html /data/bailian.json /api/v1/admin/groups/all; do
code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT$path") code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT$path")
printf ' %s %s\n' "$code" "$path" printf ' %s %s\n' "$code" "$path"
@@ -301,7 +379,8 @@ case "${1:-web}" in
cookie) cmd_cookie "$@" ;; cookie) cmd_cookie "$@" ;;
probe) cmd_probe ;; probe) cmd_probe ;;
nginx) cmd_nginx ;; nginx) cmd_nginx ;;
auth) cmd_auth "$@" ;;
status) cmd_status ;; status) cmd_status ;;
all) cmd_nginx; cmd_web; cmd_probe ;; all) cmd_nginx; cmd_web; cmd_probe ;;
*) die "未知子命令:$1(支持 web | cookie | probe | nginx | status | all" ;; *) die "未知子命令:$1(支持 web | cookie | probe | nginx | auth | status | all" ;;
esac esac

View File

@@ -110,10 +110,14 @@ bash deploy.sh # 构建并发布前端(= web自动保留 data/
bash deploy.sh cookie # 交互粘贴新百炼 Cookie写入后立即试跑并回显结果 bash deploy.sh cookie # 交互粘贴新百炼 Cookie写入后立即试跑并回显结果
bash deploy.sh probe # 更新探针脚本/systemd 单元并试跑timer 每 5 分钟) bash deploy.sh probe # 更新探针脚本/systemd 单元并试跑timer 每 5 分钟)
bash deploy.sh nginx # 刷新 nginx 站点配置并 reload bash deploy.sh nginx # 刷新 nginx 站点配置并 reload
bash deploy.sh auth # 交互式设置 Basic Auth 用户名/密码(不回显),整站开启认证
bash deploy.sh auth off # 关闭 Basic Auth
bash deploy.sh status # 定时器、快照、各 URL HTTP 自检 bash deploy.sh status # 定时器、快照、各 URL HTTP 自检
bash deploy.sh all # nginx + web + probe bash deploy.sh all # nginx + web + probe
``` ```
Basic Auth 在 nginx 层整站生效(页面、`/api/``/bailian-admin/``/data/` 都要登录),浏览器输入一次后同域请求自动带凭证,前端无需改动;账号密码以 apr1 哈希存在服务器 `/etc/nginx/token-list.htpasswd`640密码经 600 临时文件透传,不进命令行/进程列表)。`nginx` 子命令每次按该文件是否存在自动决定开/关认证,所以换机器或重刷配置不会丢开关。
- Cookie 经 600 权限临时文件透传,不出现在远端命令行/进程列表;`.env` 同步到站点根上一级(`www/` 之外nginx 发不出去)。 - Cookie 经 600 权限临时文件透传,不出现在远端命令行/进程列表;`.env` 同步到站点根上一级(`www/` 之外nginx 发不出去)。
- 可用 `DEPLOY_HOST` / `REMOTE_DIR` / `SITE_PORT` / `SUB2API_UPSTREAM` 环境变量覆盖默认目标。 - 可用 `DEPLOY_HOST` / `REMOTE_DIR` / `SITE_PORT` / `SUB2API_UPSTREAM` 环境变量覆盖默认目标。