diff --git a/README.md b/README.md index fde1ec1..c8be025 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,33 @@ npm start ## 部署(Docker) +### 一键部署脚本 + +仓库提供一键部署脚本,自动完成「拉取 git 代码 → 校验/生成 `.env` → 准备挂载目录 → `docker compose` 构建并启动」: + +- **Linux / macOS**:`deploy.sh` + ```bash + chmod +x deploy.sh + ./deploy.sh # 默认仓库与 ./kdbx-viewer 目录 + ./deploy.sh /opt/kdbx # 指定部署目录 + REPO=https://... ./deploy.sh # 指定 git 仓库地址 + ``` +- **Windows(cmd)**:`deploy.bat` + ```bat + deploy.bat # 默认仓库与 kdbx-viewer 目录 + deploy.bat D:\kdbx # 指定部署目录 + set REPO=https://... && deploy.bat # 指定 git 仓库地址 + ``` + +脚本行为: +1. 目录已存在 git 仓库则 `git pull`,否则 `git clone`(仓库地址见脚本顶部 `REPO`,可被环境变量覆盖)。 +2. 无 `.env` 时从 `.env.example` 复制,并**中断提示填写敏感项**(`APP_PW_DYNAMIC` / `APP_PW_STATIC` / `SESSION_SECRET`);若仍为占位值也会拒绝启动。 +3. 创建 `vault/`、`logs/`、`ssl/` 挂载目录。 +4. 检查 `docker` 与 `docker compose v2` 是否可用。 +5. `docker compose up -d --build` 启动,并打印访问地址。 + +### 手动部署 + 所有配置通过**同目录的 `.env` 文件注入**,无需在 `docker-compose.yml` 中写死(`compose` 会自动读取 `.env` 并用 `${VAR}` 替换)。 ```bash diff --git a/_check_ip.js b/_check_ip.js deleted file mode 100644 index 47d5ec2..0000000 --- a/_check_ip.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; -// 验证 IP 封锁逻辑修复:必须用字符串 IP 作 key,而非对象 -const ipFails = new Map(); -const IP_WINDOW = 30 * 60 * 1000, IP_MAX_FAIL = 5; -function recordIpFail(ip) { - const now = Date.now(); - let r = ipFails.get(ip); - if (!r || now - r.first > IP_WINDOW) r = { count: 0, first: now }; - r.count++; ipFails.set(ip, r); - return r.count >= IP_MAX_FAIL; -} -function isIpBlocked(ip) { - const r = ipFails.get(ip); - if (!r) return false; - if (Date.now() - r.first > IP_WINDOW) { ipFails.delete(ip); return false; } - return r.count >= IP_MAX_FAIL; -} - -// 旧 bug:传入对象 {ip:'1.2.3.4'} 作 key -const ipObj = { ip: '1.2.3.4' }; -let blockedOld = false; -for (let i = 0; i < 5; i++) recordIpFail(ipObj); -blockedOld = isIpBlocked(ipObj); // 每次都是新对象 -> 永远 false - -// 修复后:传入字符串 '1.2.3.4' -const ipStr = '1.2.3.4'; -let blockedNew = false; -for (let i = 0; i < 5; i++) recordIpFail(ipStr); -blockedNew = isIpBlocked(ipStr); - -console.log('修复前(对象key)是否封锁:', blockedOld, '-> 期望 false(bug 表现)'); -console.log('修复后(字符串key)是否封锁:', blockedNew, '-> 期望 true'); -process.exit(blockedNew === true ? 0 : 1); diff --git a/deploy.bat b/deploy.bat new file mode 100644 index 0000000..3eda73e --- /dev/null +++ b/deploy.bat @@ -0,0 +1,98 @@ +@echo off +REM ============================================================ +REM kdbx-viewer 一键部署脚本(Windows / cmd) +REM 功能:拉取 git 代码 -> 生成/校验 .env -> 准备目录 -> docker compose 构建并启动 +REM +REM 用法: +REM deploy.bat 使用默认仓库与当前目录 +REM deploy.bat D:\kdbx 指定部署目录 +REM set REPO=https://... && deploy.bat 指定 git 仓库地址 +REM ============================================================ +setlocal EnableDelayedExpansion + +REM 默认仓库地址(可被环境变量 REPO 覆盖) +if not defined REPO set "REPO=http://git.iwali.top/wangchuanli/kdbx-viewer.git" +REM 部署目录(第一个参数,默认当前目录下的 kdbx-viewer) +if "%~1"=="" (set "DEPLOY_DIR=kdbx-viewer") else (set "DEPLOY_DIR=%~1") + +echo ============================================ +echo kdbx-viewer 一键部署 +echo 仓库: %REPO% +echo 目录: %DEPLOY_DIR% +echo ============================================ + +REM ---------- 1. 拉取代码 ---------- +if exist "%DEPLOY_DIR%\.git" ( + echo [1/5] 已存在仓库,执行 git pull ... + git -C "%DEPLOY_DIR%" pull --ff-only || git -C "%DEPLOY_DIR%" pull +) else ( + echo [1/5] 克隆仓库 %REPO% ... + git clone "%REPO%" "%DEPLOY_DIR%" +) +cd /d "%DEPLOY_DIR%" + +REM ---------- 2. 校验/生成 .env ---------- +echo [2/5] 检查 .env ... +if not exist ".env" ( + if not exist ".env.example" ( + echo 缺少 .env.example,无法生成 .env,请手动创建。 1>&2 + exit /b 1 + ) + copy .env.example .env >nul + echo 已生成 .env(基于 .env.example)。 + echo !!! 请先编辑 .env 填写敏感项,再重新运行本脚本: + findstr /b "APP_PW_DYNAMIC= APP_PW_STATIC= SESSION_SECRET=" .env + echo (将占位值替换为真实强口令/随机串) + exit /b 1 +) + +REM 校验关键敏感项是否仍为占位/空 +set "NEED_EDIT=0" +for %%K in (APP_PW_DYNAMIC APP_PW_STATIC SESSION_SECRET) do ( + for /f "tokens=2* delims==" %%V in ('findstr /b "%%K=" .env') do ( + set "VAL=%%V" + if "!VAL!"=="" set "NEED_EDIT=1" + if "!VAL!"=="你的固定部分" set "NEED_EDIT=1" + if "!VAL!"=="改成你的强口令" set "NEED_EDIT=1" + if "!VAL!"=="改成随机长字符串" set "NEED_EDIT=1" + ) +) +if "!NEED_EDIT!"=="1" ( + echo .env 中仍存在未设置的占位项,请编辑补全后重新运行 deploy.bat + exit /b 1 +) + +REM ---------- 3. 准备挂载目录 ---------- +echo [3/5] 准备挂载目录 vault/ logs/ ssl/ ... +if not exist vault mkdir vault +if not exist logs mkdir logs +if not exist ssl mkdir ssl + +REM ---------- 4. 环境前置检查 ---------- +echo [4/5] 检查 docker / docker compose ... +where docker >nul 2>nul +if errorlevel 1 ( + echo 未检测到 docker,请先安装 Docker Desktop:https://www.docker.com/products/docker-desktop/ + exit /b 1 +) +docker compose version >nul 2>nul +if errorlevel 1 ( + echo 未检测到 docker compose v2,请升级 Docker Desktop 或安装 compose 插件。 + exit /b 1 +) + +REM ---------- 5. 构建并启动 ---------- +echo [5/5] docker compose 构建并启动(后台)... +docker compose up -d --build + +echo. +echo 部署完成。访问地址(默认 HTTPS,证书缺失时自动生成自签): +for /f "tokens=2* delims==" %%P in ('findstr /b "PORT=" .env') do set "PORT=%%P" +if not defined PORT set "PORT=3000" +echo https://localhost:!PORT! +echo. +echo 常用命令: +echo 查看日志: docker compose logs -f +echo 停止服务: docker compose down +echo 更新代码: deploy.bat +endlocal diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..2b664ee --- /dev/null +++ b/deploy.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# +# kdbx-viewer 一键部署脚本(Linux / macOS) +# 功能:拉取 git 代码 -> 生成/校验 .env -> 准备挂载目录 -> docker compose 构建并启动 +# +# 用法: +# ./deploy.sh # 使用默认仓库与当前目录 +# ./deploy.sh /opt/kdbx # 指定部署目录 +# REPO=https://... ./deploy.sh # 指定 git 仓库地址 +# +set -euo pipefail + +# 默认仓库地址(可被环境变量 REPO 覆盖) +REPO="${REPO:-http://git.iwali.top/wangchuanli/kdbx-viewer.git}" +# 部署目录(第一个参数,默认 ./kdbx-viewer) +DEPLOY_DIR="${1:-kdbx-viewer}" + +echo "============================================" +echo " kdbx-viewer 一键部署" +echo " 仓库: $REPO" +echo " 目录: $DEPLOY_DIR" +echo "============================================" + +# ---------- 1. 拉取代码 ---------- +if [ -d "$DEPLOY_DIR/.git" ]; then + echo "[1/5] 已存在仓库,执行 git pull ..." + git -C "$DEPLOY_DIR" pull --ff-only || git -C "$DEPLOY_DIR" pull +else + echo "[1/5] 克隆仓库 $REPO ..." + git clone "$REPO" "$DEPLOY_DIR" +fi +cd "$DEPLOY_DIR" + +# ---------- 2. 校验/生成 .env ---------- +echo "[2/5] 检查 .env ..." +if [ ! -f .env ]; then + if [ ! -f .env.example ]; then + echo "缺少 .env.example,无法生成 .env,请手动创建。" >&2 + exit 1 + fi + cp .env.example .env + echo "已生成 .env(基于 .env.example)。" + echo "!!! 请先编辑 .env 填写以下敏感项,再重新运行本脚本:" + grep -nE '^APP_PW_DYNAMIC=|^APP_PW_STATIC=|^SESSION_SECRET=' .env || true + echo "(将占位值替换为真实强口令/随机串)" + exit 1 +fi +# 校验关键敏感项是否为空或仍为占位 +need_edit=0 +for key in APP_PW_DYNAMIC APP_PW_STATIC SESSION_SECRET; do + val="$(grep -E "^${key}=" .env | tail -1 | cut -d= -f2-)" + if [ -z "${val:-}" ] || [ "${val}" = "你的固定部分" ] || [ "${val}" = "改成你的强口令" ] || [ "${val}" = "改成随机长字符串" ]; then + echo " [警告] .env 中 $key 未正确设置($val)" + need_edit=1 + fi +done +if [ "$need_edit" -eq 1 ]; then + echo "请编辑 .env 补全敏感项后重新运行 ./deploy.sh" + exit 1 +fi + +# ---------- 3. 准备挂载目录 ---------- +echo "[3/5] 准备挂载目录 vault/ logs/ ssl/ ..." +mkdir -p vault logs ssl + +# ---------- 4. 环境前置检查 ---------- +echo "[4/5] 检查 docker / docker compose ..." +if ! command -v docker >/dev/null 2>&1; then + echo "未检测到 docker,请先安装 Docker:https://docs.docker.com/get-docker/" >&2 + exit 1 +fi +if ! docker compose version >/dev/null 2>&1; then + echo "未检测到 docker compose v2,请升级 Docker Desktop 或安装 compose 插件。" >&2 + exit 1 +fi + +# ---------- 5. 构建并启动 ---------- +echo "[5/5] docker compose 构建并启动(后台)..." +docker compose up -d --build + +echo "" +echo "部署完成。访问地址(默认 HTTPS,证书缺失时自动生成自签):" +PORT="$(grep -E '^PORT=' .env | tail -1 | cut -d= -f2-)" +PORT="${PORT:-3000}" +echo " https://localhost:${PORT}" +echo "" +echo "常用命令:" +echo " 查看日志: docker compose logs -f" +echo " 停止服务: docker compose down" +echo " 更新代码: ./deploy.sh"