refactor: 整理项目根目录结构
整理内容: - 删除 60+ 临时测试输出文件 (*.txt) - 移动二进制文件到 bin/ 目录 - 移动 Shell 脚本到 scripts/ 目录 - scripts/dev/: check_gitea.sh, check_sub2api.sh, run_tests.sh - scripts/deploy/: deploy_*.sh, simple_deploy.sh - scripts/ops/: fix_nginx.sh, fix_ssl.sh, install_docker.sh - scripts/test/: test_*.sh, test_*.bat - 移动批处理文件到 scripts/ - 移动 Python 脚本到 tools/ - 清理临时日志文件 保留根目录必要文件: - go.mod, go.sum, go.work - Makefile, docker-compose.yml - .env.example, .gitignore - README.md, AGENTS.md, DEPLOY_GUIDE.md 验证: go build ./... && go test ./... 通过
This commit is contained in:
11
scripts/build.bat
Normal file
11
scripts/build.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
cd /d d:\project
|
||||
set GOWORK=off
|
||||
go build -o server.exe ./cmd/server
|
||||
if exist server.exe (
|
||||
echo Build succeeded!
|
||||
dir server.exe
|
||||
) else (
|
||||
echo Build may have failed or no output
|
||||
go build -v ./cmd/server
|
||||
)
|
||||
124
scripts/chaos/ce-001-database-unavailable.ps1
Normal file
124
scripts/chaos/ce-001-database-unavailable.ps1
Normal file
@@ -0,0 +1,124 @@
|
||||
# CE-001: 数据库不可用韧性验证
|
||||
# 验证:当数据库连接中断时,健康检查正确返回 DOWN,API 返回 503
|
||||
|
||||
param(
|
||||
[string]$BaseURL = "http://localhost:8080",
|
||||
[string]$DBPath = ".\data\user_management.db",
|
||||
[int]$TimeoutSeconds = 30
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$passed = 0
|
||||
$failed = 0
|
||||
|
||||
function Write-Pass { param($msg) Write-Host " ✅ $msg" -ForegroundColor Green; $script:passed++ }
|
||||
function Write-Fail { param($msg) Write-Host " ❌ $msg" -ForegroundColor Red; $script:failed++ }
|
||||
function Write-Step { param($msg) Write-Host "`n[STEP] $msg" -ForegroundColor Cyan }
|
||||
|
||||
Write-Host "=== CE-001: 数据库不可用韧性验证 ===" -ForegroundColor Magenta
|
||||
Write-Host "目标服务: $BaseURL"
|
||||
Write-Host "数据库路径: $DBPath"
|
||||
Write-Host ""
|
||||
|
||||
# 前置检查:服务必须正常运行
|
||||
Write-Step "前置检查:验证服务初始状态"
|
||||
try {
|
||||
$health = Invoke-RestMethod -Uri "$BaseURL/health/ready" -TimeoutSec 5
|
||||
if ($health.status -eq "UP") {
|
||||
Write-Pass "服务初始状态 UP"
|
||||
} else {
|
||||
Write-Fail "服务初始状态不健康 ($($health.status)),请先启动服务"
|
||||
exit 1
|
||||
}
|
||||
} catch {
|
||||
Write-Fail "无法连接到服务: $($_.Exception.Message)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 记录实验前指标
|
||||
Write-Step "记录实验前基线指标"
|
||||
try {
|
||||
$beforeMetrics = Invoke-RestMethod -Uri "$BaseURL/metrics" -TimeoutSec 5
|
||||
Write-Pass "基线指标已记录"
|
||||
} catch {
|
||||
Write-Host " ℹ️ /metrics 端点未就绪(可能是 P0 修复前状态),跳过指标记录"
|
||||
}
|
||||
|
||||
# 注意:此脚本为"观察模式",不实际关闭数据库
|
||||
# 在生产混沌实验中,应使用专用的故障注入工具
|
||||
Write-Step "故障注入模拟(观察模式)"
|
||||
Write-Host " ℹ️ 本实验为观察模式,不实际关闭数据库" -ForegroundColor Yellow
|
||||
Write-Host " ℹ️ 生产环境请使用: chaostoolkit / Gremlin / 手动关闭 DB 进程"
|
||||
|
||||
# 模拟:快速连续请求,观察健康检查行为
|
||||
Write-Step "并发健康检查验证"
|
||||
$jobs = 1..5 | ForEach-Object {
|
||||
Start-Job -ScriptBlock {
|
||||
param($url)
|
||||
try {
|
||||
$resp = Invoke-RestMethod -Uri "$url/health/ready" -TimeoutSec 3
|
||||
return @{ status = $resp.status; ok = $true }
|
||||
} catch {
|
||||
return @{ status = "ERROR"; ok = $false; error = $_.Exception.Message }
|
||||
}
|
||||
} -ArgumentList $BaseURL
|
||||
}
|
||||
|
||||
$results = $jobs | Wait-Job | Receive-Job
|
||||
$allUp = ($results | Where-Object { $_.status -ne "UP" }).Count -eq 0
|
||||
|
||||
if ($allUp) {
|
||||
Write-Pass "5次并发健康检查全部返回 UP"
|
||||
} else {
|
||||
Write-Fail "部分健康检查失败: $($results | ConvertTo-Json -Compress)"
|
||||
}
|
||||
|
||||
# 验证健康检查格式是否符合规范
|
||||
Write-Step "验证健康检查响应格式"
|
||||
try {
|
||||
$health = Invoke-RestMethod -Uri "$BaseURL/health/ready" -TimeoutSec 5
|
||||
|
||||
if ($health.status) { Write-Pass "包含 status 字段: $($health.status)" }
|
||||
else { Write-Fail "缺少 status 字段" }
|
||||
|
||||
if ($health.checks) { Write-Pass "包含 checks 字段" }
|
||||
else { Write-Fail "缺少 checks 字段" }
|
||||
|
||||
if ($health.timestamp) { Write-Pass "包含 timestamp 字段: $($health.timestamp)" }
|
||||
else { Write-Fail "缺少 timestamp 字段(需要升级 health.go)" }
|
||||
|
||||
if ($health.checks.database) {
|
||||
Write-Pass "database 检查存在: $($health.checks.database.status)"
|
||||
} else {
|
||||
Write-Fail "缺少 database 检查"
|
||||
}
|
||||
} catch {
|
||||
Write-Fail "健康检查请求失败: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
# 验证 Liveness 端点(应始终返回成功)
|
||||
Write-Step "验证 Liveness 端点(应始终成功)"
|
||||
try {
|
||||
$resp = Invoke-WebRequest -Uri "$BaseURL/health/live" -TimeoutSec 5
|
||||
if ($resp.StatusCode -eq 200 -or $resp.StatusCode -eq 204) {
|
||||
Write-Pass "Liveness 检查返回 $($resp.StatusCode)"
|
||||
} else {
|
||||
Write-Fail "Liveness 检查返回非成功状态: $($resp.StatusCode)"
|
||||
}
|
||||
} catch {
|
||||
Write-Fail "Liveness 检查失败: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
# 汇总
|
||||
Write-Host "`n=== 实验结果 ===" -ForegroundColor Magenta
|
||||
Write-Host "通过: $passed"
|
||||
Write-Host "失败: $failed"
|
||||
|
||||
if ($failed -eq 0) {
|
||||
Write-Host "`n✅ CE-001 观察阶段通过" -ForegroundColor Green
|
||||
Write-Host "⚠️ 完整实验需要手动关闭数据库并验证 503 响应" -ForegroundColor Yellow
|
||||
exit 0
|
||||
} else {
|
||||
Write-Host "`n❌ CE-001 存在 $failed 个验证失败" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
172
scripts/chaos/ce-005-concurrent-login.ps1
Normal file
172
scripts/chaos/ce-005-concurrent-login.ps1
Normal file
@@ -0,0 +1,172 @@
|
||||
# CE-005: 并发登录压测 & 速率限制验证
|
||||
# 验证:高并发下速率限制(Rate Limiting)是否正常工作
|
||||
|
||||
param(
|
||||
[string]$BaseURL = "http://localhost:8080",
|
||||
[int]$Concurrency = 20,
|
||||
[int]$Duration = 15,
|
||||
[switch]$Verbose
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
Write-Host "=== CE-005: 并发登录压测 & 速率限制验证 ===" -ForegroundColor Magenta
|
||||
Write-Host "目标服务: $BaseURL"
|
||||
Write-Host "并发协程: $Concurrency"
|
||||
Write-Host "持续时间: ${Duration}s"
|
||||
Write-Host ""
|
||||
|
||||
# 前置检查
|
||||
Write-Host "[前置检查] 服务健康状态..." -ForegroundColor Cyan
|
||||
try {
|
||||
$health = Invoke-RestMethod -Uri "$BaseURL/health/ready" -TimeoutSec 5
|
||||
if ($health.status -ne "UP") {
|
||||
Write-Host "❌ 服务不健康,终止实验" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host " ✅ 服务状态: UP" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host " ❌ 无法连接到服务: $($_.Exception.Message)" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 并发压测
|
||||
Write-Host "`n[压测中] 启动 $Concurrency 个并发协程,持续 ${Duration}s..." -ForegroundColor Cyan
|
||||
|
||||
$startTime = Get-Date
|
||||
$jobs = 1..$Concurrency | ForEach-Object {
|
||||
$workerID = $_
|
||||
Start-Job -ScriptBlock {
|
||||
param($BaseURL, $Duration, $workerID)
|
||||
|
||||
$end = (Get-Date).AddSeconds($Duration)
|
||||
$results = @{
|
||||
total = 0
|
||||
http_200 = 0
|
||||
http_400 = 0
|
||||
http_401 = 0
|
||||
http_429 = 0
|
||||
http_500 = 0
|
||||
other = 0
|
||||
errors = 0
|
||||
}
|
||||
|
||||
while ((Get-Date) -lt $end) {
|
||||
try {
|
||||
$body = @{
|
||||
account = "chaos_test_user_$workerID"
|
||||
password = "wrong_password_chaos_test"
|
||||
} | ConvertTo-Json
|
||||
|
||||
$resp = Invoke-WebRequest `
|
||||
-Uri "$BaseURL/api/v1/auth/login" `
|
||||
-Method POST `
|
||||
-Body $body `
|
||||
-ContentType "application/json" `
|
||||
-ErrorAction SilentlyContinue `
|
||||
-TimeoutSec 5
|
||||
|
||||
$results.total++
|
||||
switch ($resp.StatusCode) {
|
||||
200 { $results.http_200++ }
|
||||
400 { $results.http_400++ }
|
||||
401 { $results.http_401++ }
|
||||
429 { $results.http_429++ }
|
||||
500 { $results.http_500++ }
|
||||
default { $results.other++ }
|
||||
}
|
||||
} catch {
|
||||
$results.total++
|
||||
$results.errors++
|
||||
}
|
||||
|
||||
Start-Sleep -Milliseconds 50
|
||||
}
|
||||
|
||||
return $results
|
||||
} -ArgumentList $BaseURL, $Duration, $workerID
|
||||
}
|
||||
|
||||
Write-Host " ⏳ 等待实验完成..." -ForegroundColor Yellow
|
||||
$jobs | Wait-Job | Out-Null
|
||||
|
||||
$elapsed = (Get-Date) - $startTime
|
||||
Write-Host " ✅ 实验完成,耗时: $([math]::Round($elapsed.TotalSeconds, 1))s" -ForegroundColor Green
|
||||
|
||||
# 汇总结果
|
||||
$totals = @{
|
||||
total = 0; http_200 = 0; http_400 = 0; http_401 = 0
|
||||
http_429 = 0; http_500 = 0; other = 0; errors = 0
|
||||
}
|
||||
|
||||
$jobs | Receive-Job | ForEach-Object {
|
||||
$r = $_
|
||||
$totals.total += $r.total
|
||||
$totals.http_200 += $r.http_200
|
||||
$totals.http_400 += $r.http_400
|
||||
$totals.http_401 += $r.http_401
|
||||
$totals.http_429 += $r.http_429
|
||||
$totals.http_500 += $r.http_500
|
||||
$totals.other += $r.other
|
||||
$totals.errors += $r.errors
|
||||
}
|
||||
$jobs | Remove-Job
|
||||
|
||||
# 显示结果
|
||||
$rateTotal = [math]::Max($totals.total, 1)
|
||||
Write-Host "`n=== 压测结果 ===" -ForegroundColor Magenta
|
||||
Write-Host "总请求数: $($totals.total)"
|
||||
Write-Host "吞吐量: $([math]::Round($totals.total / $elapsed.TotalSeconds, 1)) req/s"
|
||||
Write-Host ""
|
||||
Write-Host "HTTP 响应分布:"
|
||||
Write-Host " 200 成功: $($totals.http_200) ($([math]::Round($totals.http_200 / $rateTotal * 100, 1))%)"
|
||||
Write-Host " 400 请求错误: $($totals.http_400) ($([math]::Round($totals.http_400 / $rateTotal * 100, 1))%)"
|
||||
Write-Host " 401 认证失败: $($totals.http_401) ($([math]::Round($totals.http_401 / $rateTotal * 100, 1))%)"
|
||||
Write-Host " 429 速率限制: $($totals.http_429) ($([math]::Round($totals.http_429 / $rateTotal * 100, 1))%)" -ForegroundColor Yellow
|
||||
Write-Host " 500 服务错误: $($totals.http_500) ($([math]::Round($totals.http_500 / $rateTotal * 100, 1))%)" -ForegroundColor $(if ($totals.http_500 -gt 0) {"Red"} else {"White"})
|
||||
Write-Host " 其他/错误: $($totals.other + $totals.errors)"
|
||||
|
||||
# 验证
|
||||
Write-Host "`n=== 验证 ===" -ForegroundColor Cyan
|
||||
|
||||
$passed = 0; $failed = 0
|
||||
|
||||
# 验证1:速率限制触发
|
||||
if ($totals.http_429 -gt 0) {
|
||||
Write-Host " ✅ 速率限制已触发 ($($totals.http_429) 次 429 响应)" -ForegroundColor Green
|
||||
$passed++
|
||||
} else {
|
||||
Write-Host " ❌ 速率限制未触发(0 次 429),请检查 config.yaml ratelimit 配置" -ForegroundColor Red
|
||||
$failed++
|
||||
}
|
||||
|
||||
# 验证2:无服务器错误(500 不应出现)
|
||||
if ($totals.http_500 -eq 0) {
|
||||
Write-Host " ✅ 无 5xx 错误,服务稳定" -ForegroundColor Green
|
||||
$passed++
|
||||
} else {
|
||||
Write-Host " ❌ 出现 $($totals.http_500) 次 5xx 错误,存在系统稳定性问题" -ForegroundColor Red
|
||||
$failed++
|
||||
}
|
||||
|
||||
# 验证3:QPS 合理(服务未被压垮)
|
||||
$targetQPS = $Concurrency * 5 # 理论最大 QPS
|
||||
$actualQPS = $totals.total / $elapsed.TotalSeconds
|
||||
if ($actualQPS -gt 0) {
|
||||
Write-Host " ✅ 服务保持响应,实际 QPS: $([math]::Round($actualQPS, 1))" -ForegroundColor Green
|
||||
$passed++
|
||||
} else {
|
||||
Write-Host " ❌ 服务可能已无响应" -ForegroundColor Red
|
||||
$failed++
|
||||
}
|
||||
|
||||
Write-Host "`n=== 实验总结 ===" -ForegroundColor Magenta
|
||||
Write-Host "通过: $passed 失败: $failed"
|
||||
|
||||
if ($failed -eq 0) {
|
||||
Write-Host "`n✅ CE-005 通过 — 速率限制在高并发下正常工作" -ForegroundColor Green
|
||||
exit 0
|
||||
} else {
|
||||
Write-Host "`n❌ CE-005 失败 — 存在 $failed 个验证问题" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
40
scripts/check_project.bat
Normal file
40
scripts/check_project.bat
Normal file
@@ -0,0 +1,40 @@
|
||||
@echo off
|
||||
echo ====================================
|
||||
echo Project Migration Quick Check
|
||||
echo ====================================
|
||||
echo.
|
||||
|
||||
echo 1. Checking key files...
|
||||
if exist "D:\project\go.mod" echo [OK] go.mod
|
||||
if exist "D:\project\README.md" echo [OK] README.md
|
||||
if exist "D:\project\cmd\server\main.go" echo [OK] main.go
|
||||
if exist "D:\project\configs\config.yaml" echo [OK] config.yaml
|
||||
echo.
|
||||
|
||||
echo 2. Checking Go environment...
|
||||
where go >nul 2>&1
|
||||
if %errorlevel% == 0 (
|
||||
echo [OK] Go is installed
|
||||
go version
|
||||
) else (
|
||||
echo [ERROR] Go is not installed
|
||||
)
|
||||
echo.
|
||||
|
||||
echo 3. Checking directories...
|
||||
if exist "D:\project\cmd" echo [OK] cmd\
|
||||
if exist "D:\project\internal" echo [OK] internal\
|
||||
if exist "D:\project\configs" echo [OK] configs\
|
||||
if exist "D:\project\docs" echo [OK] docs\
|
||||
echo.
|
||||
|
||||
echo ====================================
|
||||
echo Quick check completed!
|
||||
echo ====================================
|
||||
echo.
|
||||
echo Next steps:
|
||||
echo 1. Read docs\migration\MIGRATION_CHECKLIST.md for full checklist
|
||||
echo 2. Read docs\plans\NEXT_STEPS.md for detailed instructions
|
||||
echo 3. If Go is installed, run: go build ./cmd/server
|
||||
echo.
|
||||
pause
|
||||
23
scripts/cleanup.bat
Normal file
23
scripts/cleanup.bat
Normal file
@@ -0,0 +1,23 @@
|
||||
@echo off
|
||||
REM 清理D:\project目录下的临时文件
|
||||
REM 运行方式:把此文件放到D:\project目录,双击运行
|
||||
|
||||
echo 正在清理临时文件...
|
||||
|
||||
REM 删除txt临时文件
|
||||
del /Q *.txt 2>nul
|
||||
|
||||
REM 删除bat脚本
|
||||
del /Q *.bat 2>nul
|
||||
|
||||
REM 删除sh脚本
|
||||
del /Q *.sh 2>nul
|
||||
|
||||
REM 删除ps1脚本
|
||||
del /Q *.ps1 2>nul
|
||||
|
||||
REM 删除py脚本
|
||||
del /Q *.py 2>nul
|
||||
|
||||
echo 清理完成!
|
||||
pause
|
||||
264
scripts/deploy/deploy_full.sh
Normal file
264
scripts/deploy/deploy_full.sh
Normal file
@@ -0,0 +1,264 @@
|
||||
#!/bin/bash
|
||||
# 服务器初始化和部署脚本 - Ubuntu 24.04
|
||||
# 域名: tksea.top
|
||||
# 服务器 IP: 43.155.133.187
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================"
|
||||
echo "服务器初始化和部署脚本"
|
||||
echo "========================================"
|
||||
|
||||
# 0. 检查是否是 root 用户
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "请使用 root 用户运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 1. 更新系统
|
||||
echo "[1/14] 更新系统包..."
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# 2. 安装基础工具
|
||||
echo "[2/14] 安装基础工具..."
|
||||
apt install -y curl wget vim git htop net-tools unzip certbot python3-certbot-nginx gnupg2 ca-certificates lsb-release
|
||||
|
||||
# 3. 安装 Docker
|
||||
echo "[3/14] 安装 Docker..."
|
||||
if ! command -v docker &> /dev/null; then
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
fi
|
||||
|
||||
# 4. 验证 Docker
|
||||
echo "[4/14] 验证 Docker 安装..."
|
||||
docker --version
|
||||
docker compose version
|
||||
|
||||
# 5. 安装 Nginx
|
||||
echo "[5/14] 安装 Nginx..."
|
||||
if ! command -v nginx &> /dev/null; then
|
||||
apt install -y nginx
|
||||
fi
|
||||
|
||||
# 6. 配置防火墙
|
||||
echo "[6/14] 配置防火墙..."
|
||||
ufw allow 22/tcp
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
echo "y" | ufw enable 2>/dev/null || true
|
||||
|
||||
# 7. 创建应用目录
|
||||
echo "[7/14] 创建应用目录..."
|
||||
mkdir -p /opt/gitea
|
||||
mkdir -p /opt/sub2api
|
||||
mkdir -p /opt/nginx/ssl
|
||||
mkdir -p /var/www/html
|
||||
|
||||
# 8. 配置 DNS 验证(用于 Let's Encrypt)
|
||||
echo "[8/14] 配置 Nginx 用于 SSL..."
|
||||
cat > /etc/nginx/sites-available/tksea.top << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name tksea.top www.tksea.top;
|
||||
root /var/www/html;
|
||||
|
||||
location / {
|
||||
return 200 "Sub2API Server";
|
||||
}
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/tksea.top /etc/nginx/sites-enabled/
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
|
||||
# 9. 获取 SSL 证书
|
||||
echo "[9/14] 获取 SSL 证书..."
|
||||
certbot --nginx -d tksea.top -d www.tksea.top --non-interactive --agree-tos --email admin@tksea.top --keep-until-expiring
|
||||
|
||||
# 10. 配置 Nginx 反向代理
|
||||
echo "[10/14] 配置 Nginx 反向代理..."
|
||||
cat > /etc/nginx/sites-available/tksea.top << 'EOF'
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name tksea.top www.tksea.top;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS - Gitea (主域名)
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name tksea.top www.tksea.top;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_tickets off;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
# Gitea 反向代理
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
# sub2api 子域名
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.tksea.top;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_tickets off;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
underscores_in_headers on;
|
||||
|
||||
# Sub2API 反向代理
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/tksea.top /etc/nginx/sites-enabled/
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
|
||||
# 11. 部署 Gitea
|
||||
echo "[11/14] 部署 Gitea..."
|
||||
cat > /opt/gitea/docker-compose.yml << 'EOF'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
- "127.0.0.1:2222:22"
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
- GITEA__server__DOMAIN=tksea.top
|
||||
- GITEA__server__ROOT_URL=https://tksea.top/
|
||||
- GITEA__server__HTTP_PORT=3000
|
||||
- GITEA__ssh__DOMAIN=tksea.top
|
||||
- GITEA__ssh__PORT=2222
|
||||
- GITEA__webhook__ALLOWED_HOSTS=tksea.top
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
gitea-data:
|
||||
name: gitea-data
|
||||
EOF
|
||||
|
||||
cd /opt/gitea
|
||||
docker compose up -d
|
||||
echo "等待 Gitea 启动..."
|
||||
sleep 10
|
||||
|
||||
# 12. 部署 Sub2API
|
||||
echo "[12/14] 部署 Sub2API..."
|
||||
mkdir -p /opt/sub2api/deploy
|
||||
cd /opt/sub2api/deploy
|
||||
|
||||
# 下载部署脚本
|
||||
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh -o docker-deploy.sh
|
||||
chmod +x docker-deploy.sh
|
||||
bash docker-deploy.sh
|
||||
|
||||
# 修改 docker-compose 使用本地存储
|
||||
if [ -f docker-compose.yml ]; then
|
||||
# 替换为本地目录版本
|
||||
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-compose.local.yml -o docker-compose.local.yml
|
||||
docker compose -f docker-compose.local.yml up -d
|
||||
fi
|
||||
|
||||
# 13. 配置 SSL 自动续期
|
||||
echo "[13/14] 配置 SSL 自动续期..."
|
||||
cat > /etc/cron.d/certbot-renew << 'EOF'
|
||||
0 0 * * * root certbot renew --quiet --deploy-hook "systemctl reload nginx"
|
||||
EOF
|
||||
|
||||
# 14. 等待服务启动并显示状态
|
||||
echo "[14/14] 验证服务状态..."
|
||||
sleep 15
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "部署完成!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Gitea 状态:"
|
||||
docker ps | grep gitea || echo "Gitea 容器状态待检查"
|
||||
echo ""
|
||||
echo "Sub2API 状态:"
|
||||
docker ps | grep sub2api || echo "Sub2API 容器状态待检查"
|
||||
echo ""
|
||||
echo "Nginx 状态:"
|
||||
systemctl status nginx --no-pager | head -5
|
||||
echo ""
|
||||
echo "SSL 证书状态:"
|
||||
certbot certificates 2>/dev/null | head -10
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "访问地址:"
|
||||
echo "- Gitea: https://tksea.top"
|
||||
echo "- Sub2API: https://api.tksea.top"
|
||||
echo ""
|
||||
echo "后续步骤:"
|
||||
echo "1. 首次访问 https://tksea.top 完成 Gitea 初始化"
|
||||
echo "2. 访问 https://api.tksea.top 完成 Sub2API 设置向导"
|
||||
echo "3. 在腾讯云控制台添加 DNS 解析: api.tksea.top -> 43.155.133.187"
|
||||
echo "========================================"
|
||||
172
scripts/deploy/deploy_server.sh
Normal file
172
scripts/deploy/deploy_server.sh
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/bin/bash
|
||||
# 服务器初始化和部署脚本 - Ubuntu 24.04
|
||||
# 域名: tksea.top
|
||||
# IP: 43.155.133.187
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================"
|
||||
echo "服务器初始化和部署脚本"
|
||||
echo "========================================"
|
||||
|
||||
# 1. 更新系统
|
||||
echo "[1/12] 更新系统包..."
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# 2. 安装基础工具
|
||||
echo "[2/12] 安装基础工具..."
|
||||
apt install -y curl wget vim git htop net-tools unzipsoftware-properties-common
|
||||
|
||||
# 3. 安装 Docker
|
||||
echo "[3/12] 安装 Docker..."
|
||||
if ! command -v docker &> /dev/null; then
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
fi
|
||||
|
||||
# 4. 安装 Docker Compose
|
||||
echo "[4/12] 安装 Docker Compose..."
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
apt install -y docker-compose-plugin
|
||||
fi
|
||||
|
||||
# 5. 安装 Nginx
|
||||
echo "[5/12] 安装 Nginx..."
|
||||
apt install -y nginx
|
||||
|
||||
# 6. 安装 Certbot
|
||||
echo "[6/12] 安装 Certbot..."
|
||||
snap install --classic certbot
|
||||
ln -sf /snap/bin/certbot /usr/bin/certbot
|
||||
|
||||
# 7. 配置防火墙
|
||||
echo "[7/12] 配置防火墙..."
|
||||
ufw allow 22/tcp
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
ufw enable
|
||||
|
||||
# 8. 创建应用目录
|
||||
echo "[8/12] 创建应用目录..."
|
||||
mkdir -p /opt/gitea
|
||||
mkdir -p /opt/sub2api
|
||||
mkdir -p /opt/nginx/ssl
|
||||
|
||||
# 9. 配置 Nginx
|
||||
echo "[9/12] 配置 Nginx..."
|
||||
cat > /etc/nginx/sites-available/tksea.top << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name tksea.top www.tksea.top;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name tksea.top www.tksea.top;
|
||||
|
||||
# SSL 证书配置 (使用Let's Encrypt)
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_tickets off;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# Gitea 代理
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /git/ {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/tksea.top /etc/nginx/sites-enabled/
|
||||
nginx -t
|
||||
|
||||
# 10. 配置 SSL 证书
|
||||
echo "[10/12] 配置 SSL 证书..."
|
||||
certbot --nginx -d tksea.top -d www.tksea.top --non-interactive --agree-tos --email your-email@example.com
|
||||
|
||||
# 11. 配置 SSL 自动续期
|
||||
echo "[11/12] 配置 SSL 自动续期..."
|
||||
cat > /etc/cron.d/certbot-renew << 'EOF'
|
||||
0 0 * * * root certbot renew --quiet --deploy-hook "nginx -s reload"
|
||||
EOF
|
||||
|
||||
# 12. 创建 Docker Compose 文件
|
||||
echo "[12/12] 创建 Docker Compose 文件..."
|
||||
|
||||
# Gitea
|
||||
cat > /opt/gitea/docker-compose.yml << 'EOF'
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "2222:22"
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
- GITEA__server__DOMAIN=tksea.top
|
||||
- GITEA__server__ROOT_URL=https://tksea.top/
|
||||
- GITEA__server__HTTP_PORT=3000
|
||||
- GITEA__ssh__DOMAIN=tksea.top
|
||||
- GITEA__ssh__PORT=2222
|
||||
networks:
|
||||
- gitea-network
|
||||
|
||||
networks:
|
||||
gitea-network:
|
||||
name: gitea-network
|
||||
|
||||
volumes:
|
||||
gitea-data:
|
||||
name: gitea-data
|
||||
EOF
|
||||
|
||||
# 启动 Gitea
|
||||
cd /opt/gitea && docker compose up -d
|
||||
|
||||
# 安装 Docker (如果还没有)
|
||||
echo "========================================"
|
||||
echo "部署完成!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "服务状态:"
|
||||
docker ps
|
||||
echo ""
|
||||
echo "Nginx 状态:"
|
||||
systemctl status nginx --no-pager
|
||||
echo ""
|
||||
echo "SSL 证书状态:"
|
||||
certbot certificates
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "后续步骤:"
|
||||
echo "1. 访问 https://tksea.top 完成 Gitea 初始化"
|
||||
echo "2. 配置 sub2api 项目部署"
|
||||
echo "========================================"
|
||||
181
scripts/deploy/deploy_single.sh
Normal file
181
scripts/deploy/deploy_single.sh
Normal file
@@ -0,0 +1,181 @@
|
||||
#!/bin/bash
|
||||
# 一键部署脚本 - Ubuntu 24.04
|
||||
# 域名: tksea.top, 服务器: 43.155.133.187
|
||||
# 使用方法: 在服务器上运行此脚本
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================"
|
||||
echo "一键部署服务器初始化脚本"
|
||||
echo "========================================"
|
||||
|
||||
# 检查 root 权限
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "错误: 请使用 root 用户运行此脚本"
|
||||
echo "解决方法: sudo bash deploy.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[1/12] 更新系统..."
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt update && apt upgrade -y
|
||||
|
||||
echo "[2/12] 安装基础工具..."
|
||||
apt install -y curl wget vim git htop net-tools unzip certbot python3-certbot-nginx gnupg2 ca-certificates lsb-release
|
||||
|
||||
echo "[3/12] 安装 Docker..."
|
||||
if ! command -v docker &> /dev/null; then
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl enable docker
|
||||
fi
|
||||
|
||||
echo "[4/12] 安装 Nginx..."
|
||||
apt install -y nginx
|
||||
|
||||
echo "[5/12] 配置防火墙..."
|
||||
ufw allow 22/tcp
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
echo "y" | ufw enable 2>/dev/null || true
|
||||
|
||||
echo "[6/12] 创建目录..."
|
||||
mkdir -p /opt/gitea /opt/sub2api/deploy /var/www/html
|
||||
|
||||
echo "[7/12] 配置 Nginx..."
|
||||
cat > /etc/nginx/sites-available/tksea.top << 'NGINXEOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name tksea.top www.tksea.top api.tksea.top;
|
||||
root /var/www/html;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 200 "Server initializing...";
|
||||
}
|
||||
}
|
||||
NGINXEOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/tksea.top /etc/nginx/sites-enabled/
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
echo "[8/12] 获取 SSL 证书..."
|
||||
certbot --nginx -d tksea.top -d www.tksea.top -d api.tksea.top --non-interactive --agree-tos --email admin@tksea.top --keep-until-expiring || true
|
||||
|
||||
echo "[9/12] 配置完整 Nginx 反向代理..."
|
||||
cat > /etc/nginx/sites-available/tksea.top << 'NGINXEOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name tksea.top www.tksea.top api.tksea.top;
|
||||
location /.well-known/acme-challenge/ { root /var/www/html; }
|
||||
location / { return 301 https://$host$request_uri; }
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name tksea.top www.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
underscores_in_headers on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
NGINXEOF
|
||||
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
echo "[10/12] 部署 Gitea..."
|
||||
cat > /opt/gitea/docker-compose.yml << 'GITEAEOF'
|
||||
version: '3.8'
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
- "127.0.0.1:2222:22"
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
- GITEA__server__DOMAIN=tksea.top
|
||||
- GITEA__server__ROOT_URL=https://tksea.top/
|
||||
- GITEA__server__HTTP_PORT=3000
|
||||
- GITEA__ssh__DOMAIN=tksea.top
|
||||
- GITEA__ssh__PORT=2222
|
||||
volumes:
|
||||
gitea-data:
|
||||
GITEAEOF
|
||||
|
||||
cd /opt/gitea && docker compose up -d
|
||||
|
||||
echo "[11/12] 部署 Sub2API..."
|
||||
cd /opt/sub2api/deploy
|
||||
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh -o docker-deploy.sh
|
||||
chmod +x docker-deploy.sh
|
||||
bash docker-deploy.sh
|
||||
|
||||
echo "[12/12] 配置自动续期..."
|
||||
cat > /etc/cron.d/certbot-renew << 'CRONEOF'
|
||||
0 0 * * * root certbot renew --quiet --deploy-hook "systemctl reload nginx"
|
||||
CRONEOF
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "部署完成!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "请在腾讯云控制台添加 DNS 解析:"
|
||||
echo " - 记录类型: A"
|
||||
echo " - 主机记录: api"
|
||||
echo " - 记录值: 43.155.133.187"
|
||||
echo ""
|
||||
echo "等待 5 分钟后访问:"
|
||||
echo " - Gitea: https://tksea.top"
|
||||
echo " - Sub2API: https://api.tksea.top"
|
||||
echo "========================================"
|
||||
47
scripts/deploy/simple_deploy.sh
Normal file
47
scripts/deploy/simple_deploy.sh
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# 极简一键部署脚本 - Ubuntu 24.04
|
||||
# 服务器: 43.155.133.187 | 域名: tksea.top
|
||||
set -e
|
||||
[ "$EUID" -ne 0 ] && echo "请用 sudo 运行" && exit 1
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
echo "[1/8] 更新..." && apt update -y && apt upgrade -y
|
||||
echo "[2/8] Docker..." && curl -fsSL https://get.docker.com | sh && systemctl enable docker
|
||||
echo "[3/8] Nginx/Certbot..." && apt install -y nginx certbot python3-certbot-nginx
|
||||
echo "[4/8] 目录..." && mkdir -p /opt/gitea /opt/sub2api/deploy /var/www/html
|
||||
echo "[5/8] Nginx配置..." && cat > /etc/nginx/sites-available/tksea << 'N'
|
||||
server { listen 80; server_name tksea.top www.tksea.top api.tksea.top; root /var/www/html; location /.well-known/acme-challenge/ { root /var/www/html; } location / { return 200 "Init..."; } }
|
||||
N
|
||||
ln -sf /etc/nginx/sites-available/tksea /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx
|
||||
echo "[6/8] SSL证书..." && certbot --nginx -d tksea.top -d www.tksea.top -d api.tksea.top --non-interactive --agree-tos --email admin@tksea.top || true
|
||||
echo "[7/8] Nginx反向代理..." && cat > /etc/nginx/sites-available/tksea << 'N'
|
||||
server { listen 80; server_name tksea.top www.tksea.top api.tksea.top; location /.well-known/acme-challenge/ { root /var/www/html; } location / { return 301 https://$host$request_uri; } }
|
||||
server { listen 443 ssl http2; server_name tksea.top; ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_protocols TLSv1.2 TLSv1.3; add_header Strict-Transport-Security "max-age=63072000" always; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
|
||||
server { listen 443 ssl http2; server_name api.tksea.top; ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_protocols TLSv1.2 TLSv1.3; add_header Strict-Transport-Security "max-age=63072000" always; underscores_in_headers on; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
|
||||
N
|
||||
nginx -t && systemctl reload nginx
|
||||
echo "[8/8] Gitea..." && cat > /opt/gitea/docker-compose.yml << 'G'
|
||||
version: "3.8"
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
- "127.0.0.1:2222:22"
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
- GITEA__server__DOMAIN=tksea.top
|
||||
- GITEA__server__ROOT_URL=https://tksea.top/
|
||||
volumes:
|
||||
gitea-data:
|
||||
G
|
||||
cd /opt/gitea && docker compose up -d
|
||||
echo "部署完成! 继续执行 Sub2API 部署..." && cd /opt/sub2api/deploy && curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
|
||||
echo "========================================" && echo "完成! 请添加 DNS: api.tksea.top -> 43.155.133.187" && echo "访问: https://tksea.top 和 https://api.tksea.top" && echo "========================================"
|
||||
18
scripts/dev/check_gitea.sh
Normal file
18
scripts/dev/check_gitea.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# Gitea 上传配置修复
|
||||
|
||||
echo "检查 Gitea 配置..."
|
||||
|
||||
# 1. 检查当前运行配置
|
||||
docker exec gitea gitea admin list-users 2>/dev/null || echo "Gitea 访问正常"
|
||||
|
||||
# 2. 检查 Docker 网络
|
||||
docker network inspect gitea_gitea-network 2>/dev/null || echo "网络正常"
|
||||
|
||||
echo ""
|
||||
echo "需要检查 Gitea 管理后台设置:"
|
||||
echo "1. 管理员设置 → 应用设置 → 最大附件大小"
|
||||
echo "2. 管理员设置 → SSH 密钥"
|
||||
echo ""
|
||||
echo "Docker 容器状态:"
|
||||
docker ps | grep gitea
|
||||
5
scripts/dev/check_sub2api.sh
Normal file
5
scripts/dev/check_sub2api.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# 查看 Sub2API 管理员密码
|
||||
|
||||
cd /opt/sub2api/deploy
|
||||
docker compose logs sub2api | grep -i "password\|admin" | tail -20
|
||||
87
scripts/dev/run_tests.sh
Normal file
87
scripts/dev/run_tests.sh
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# 用户管理系统 - 测试执行脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查Go环境
|
||||
check_go_env() {
|
||||
log_info "检查Go环境..."
|
||||
if ! command -v go &> /dev/null; then
|
||||
log_error "Go未安装"
|
||||
exit 1
|
||||
fi
|
||||
GO_VERSION=$(go version | awk '{print $3}')
|
||||
log_info "Go版本: $GO_VERSION"
|
||||
}
|
||||
|
||||
# 运行单元测试
|
||||
run_unit_tests() {
|
||||
log_info "=========================================="
|
||||
log_info "运行单元测试"
|
||||
log_info "=========================================="
|
||||
|
||||
log_info "测试Domain层..."
|
||||
go test -v ./internal/domain/... -run "Test.*" 2>/dev/null || log_info "Domain层测试(需Go环境)"
|
||||
|
||||
log_info "✅ 单元测试完成"
|
||||
}
|
||||
|
||||
# 运行所有测试
|
||||
run_all_tests() {
|
||||
log_info "=========================================="
|
||||
log_info "运行所有测试"
|
||||
log_info "=========================================="
|
||||
|
||||
check_go_env
|
||||
run_unit_tests
|
||||
|
||||
log_info "集成测试: 需要数据库和Redis环境"
|
||||
log_info "E2E测试: 需要完整服务环境"
|
||||
log_info "鲁棒性测试: 需要并发测试环境"
|
||||
|
||||
log_info "=========================================="
|
||||
log_info "✅ 测试脚本准备完成"
|
||||
log_info "=========================================="
|
||||
}
|
||||
|
||||
# 显示帮助
|
||||
show_help() {
|
||||
echo "用户管理系统 - 测试执行脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " all 运行所有测试"
|
||||
echo " unit 运行单元测试"
|
||||
echo " help 显示帮助"
|
||||
}
|
||||
|
||||
main() {
|
||||
case "$1" in
|
||||
all) run_all_tests ;;
|
||||
unit)
|
||||
check_go_env
|
||||
run_unit_tests
|
||||
;;
|
||||
help|--help|-h) show_help ;;
|
||||
*)
|
||||
run_all_tests
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
61
scripts/ops/fix_nginx.sh
Normal file
61
scripts/ops/fix_nginx.sh
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
# 修复 Nginx 配置
|
||||
|
||||
echo "配置 Nginx..."
|
||||
|
||||
cat > /etc/nginx/sites-available/tksea << 'EOF'
|
||||
# HTTP 重定向
|
||||
server {
|
||||
listen 80;
|
||||
server_name tksea.top www.tksea.top api.tksea.top gitea.tksea.top sub.tksea.top;
|
||||
location /.well-known/acme-challenge/ { root /var/www/html; }
|
||||
location / { return 301 https://$host$request_uri; }
|
||||
}
|
||||
|
||||
# HTTPS - 主域名和 Gitea
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name tksea.top www.tksea.top gitea.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS - API 和 Sub
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.tksea.top sub.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
underscores_in_headers on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
echo "完成! 检查: sudo systemctl status nginx"
|
||||
145
scripts/ops/fix_ssl.sh
Normal file
145
scripts/ops/fix_ssl.sh
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
# SSL 证书修复和 Nginx 配置脚本
|
||||
# 修复 SSL 证书路径问题并配置新的子域名
|
||||
|
||||
echo "========================================"
|
||||
echo "修复 SSL 证书和 Nginx 配置"
|
||||
echo "========================================"
|
||||
|
||||
# 1. 检查 SSL 证书状态
|
||||
echo "[1/5] 检查 SSL 证书状态..."
|
||||
ls -la /etc/letsencrypt/live/ 2>/dev/null || echo "证书目录不存在"
|
||||
|
||||
# 2. 重新获取证书
|
||||
echo "[2/5] 重新获取 SSL 证书..."
|
||||
certbot certonly --webroot -w /var/www/html -d tksea.top -d www.tksea.top -d api.tksea.top -d gitea.tksea.top -d sub.tksea.top --non-interactive --agree-tos --email admin@tksea.top || certbot --nginx -d tksea.top -d www.tksea.top -d api.tksea.top -d gitea.tksea.top -d sub.tksea.top --non-interactive --agree-tos --email admin@tksea.top
|
||||
|
||||
# 3. 验证证书
|
||||
echo "[3/5] 验证证书..."
|
||||
certbot certificates
|
||||
|
||||
# 4. 配置 Nginx
|
||||
echo "[4/5] 配置 Nginx..."
|
||||
cat > /etc/nginx/sites-available/tksea << 'EOF'
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name tksea.top www.tksea.top api.tksea.top gitea.tksea.top sub.tksea.top;
|
||||
location /.well-known/acme-challenge/ { root /var/www/html; }
|
||||
location / { return 301 https://$host$request_uri; }
|
||||
}
|
||||
|
||||
# HTTPS - 主域名 (Gitea)
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name tksea.top www.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS - api 子域名 (Sub2API)
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
underscores_in_headers on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS - gitea 子域名 (Gitea)
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name gitea.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS - sub 子域名 (Sub2API)
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name sub.tksea.top;
|
||||
ssl_certificate /etc/letsencrypt/live/tksea.top/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tksea.top/privkey.pem;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
underscores_in_headers on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# 5. 测试并重载 Nginx
|
||||
echo "[5/5] 测试并重载 Nginx..."
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "修复完成!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "现在可以访问:"
|
||||
echo " - https://tksea.top (Gitea)"
|
||||
echo " - https://gitea.tksea.top (Gitea)"
|
||||
echo " - https://api.tksea.top (Sub2API)"
|
||||
echo " - https://sub.tksea.top (Sub2API)"
|
||||
echo ""
|
||||
echo "检查服务状态:"
|
||||
echo " docker ps"
|
||||
echo " systemctl status nginx"
|
||||
echo "========================================"
|
||||
52
scripts/ops/install_docker.sh
Normal file
52
scripts/ops/install_docker.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# Docker 和服务部署脚本
|
||||
|
||||
echo "[1/4] 安装 Docker..."
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
echo "[2/4] 验证 Docker..."
|
||||
docker --version
|
||||
|
||||
echo "[3/4] 部署 Gitea..."
|
||||
mkdir -p /opt/gitea
|
||||
cd /opt/gitea
|
||||
cat > docker-compose.yml << 'EOF'
|
||||
version: "3.8"
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
- "127.0.0.1:2222:22"
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
- GITEA__server__DOMAIN=tksea.top
|
||||
- GITEA__server__ROOT_URL=https://tksea.top/
|
||||
volumes:
|
||||
gitea-data:
|
||||
EOF
|
||||
docker compose up -d
|
||||
|
||||
echo "[4/4] 部署 Sub2API..."
|
||||
mkdir -p /opt/sub2api/deploy
|
||||
cd /opt/sub2api/deploy
|
||||
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "部署完成!"
|
||||
echo "========================================"
|
||||
echo "检查状态: docker ps"
|
||||
echo "访问: https://tksea.top (Gitea)"
|
||||
echo "访问: https://api.tksea.top (Sub2API)"
|
||||
echo "========================================"
|
||||
184
scripts/ops/sre-daily-healthcheck.ps1
Normal file
184
scripts/ops/sre-daily-healthcheck.ps1
Normal file
@@ -0,0 +1,184 @@
|
||||
# SRE 日常健康巡检脚本
|
||||
# 每日自动运行,输出系统健康状态报告
|
||||
|
||||
param(
|
||||
[string]$BaseURL = "http://localhost:8080",
|
||||
[string]$ReportDir = "docs\evidence\daily-health",
|
||||
[switch]$AlertOnFailure
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
$date = Get-Date -Format "yyyyMMdd-HHmmss"
|
||||
$reportFile = "$ReportDir\HEALTH_CHECK_$date.md"
|
||||
|
||||
# 确保报告目录存在
|
||||
New-Item -ItemType Directory -Force -Path $ReportDir | Out-Null
|
||||
|
||||
$report = @()
|
||||
$totalChecks = 0
|
||||
$passedChecks = 0
|
||||
$criticalFailures = 0
|
||||
|
||||
function Add-Check {
|
||||
param($name, $status, $detail, $isCritical = $false)
|
||||
$script:totalChecks++
|
||||
if ($status -eq "PASS") {
|
||||
$script:passedChecks++
|
||||
$icon = "✅"
|
||||
} elseif ($status -eq "WARN") {
|
||||
$icon = "⚠️"
|
||||
} else {
|
||||
$icon = "❌"
|
||||
if ($isCritical) { $script:criticalFailures++ }
|
||||
}
|
||||
$line = "| $icon | $name | $status | $detail |"
|
||||
$script:report += $line
|
||||
Write-Host " $icon $name : $status — $detail"
|
||||
}
|
||||
|
||||
Write-Host "=== UMS SRE 日常健康巡检 ===" -ForegroundColor Cyan
|
||||
Write-Host "时间: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
||||
Write-Host "目标: $BaseURL"
|
||||
Write-Host ""
|
||||
|
||||
# 1. 健康检查端点
|
||||
Write-Host "[1/6] 健康检查端点" -ForegroundColor Yellow
|
||||
try {
|
||||
$health = Invoke-RestMethod -Uri "$BaseURL/health/ready" -TimeoutSec 10
|
||||
$dbStatus = $health.checks.database.status
|
||||
Add-Check "服务就绪检查 /health/ready" "PASS" "状态: $($health.status)" $true
|
||||
Add-Check "数据库连接" $(if ($dbStatus -eq "UP") {"PASS"} else {"FAIL"}) "状态: $dbStatus" $true
|
||||
if ($health.checks.redis) {
|
||||
Add-Check "Redis 连接" $(if ($health.checks.redis.status -eq "UP") {"PASS"} elseif ($health.checks.redis.status -eq "UNKNOWN") {"WARN"} else {"FAIL"}) "状态: $($health.checks.redis.status)"
|
||||
}
|
||||
if ($health.uptime) {
|
||||
Add-Check "服务运行时间" "PASS" $health.uptime
|
||||
}
|
||||
} catch {
|
||||
Add-Check "服务就绪检查 /health/ready" "FAIL" $_.Exception.Message $true
|
||||
}
|
||||
|
||||
try {
|
||||
$live = Invoke-WebRequest -Uri "$BaseURL/health/live" -TimeoutSec 5
|
||||
Add-Check "存活检查 /health/live" $(if ($live.StatusCode -lt 300) {"PASS"} else {"FAIL"}) "HTTP $($live.StatusCode)" $true
|
||||
} catch {
|
||||
Add-Check "存活检查 /health/live" "FAIL" $_.Exception.Message $true
|
||||
}
|
||||
|
||||
# 2. 关键 API 响应时间
|
||||
Write-Host "`n[2/6] 关键 API 响应时间" -ForegroundColor Yellow
|
||||
$criticalPaths = @(
|
||||
@{path="/api/v1/auth/capabilities"; desc="认证能力接口"; threshold=500},
|
||||
@{path="/health"; desc="健康检查接口"; threshold=100}
|
||||
)
|
||||
foreach ($ep in $criticalPaths) {
|
||||
try {
|
||||
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
Invoke-RestMethod -Uri "$BaseURL$($ep.path)" -TimeoutSec 5 | Out-Null
|
||||
$sw.Stop()
|
||||
$ms = $sw.ElapsedMilliseconds
|
||||
$status = if ($ms -le $ep.threshold) {"PASS"} elseif ($ms -le $ep.threshold * 2) {"WARN"} else {"FAIL"}
|
||||
Add-Check "$($ep.desc) $($ep.path)" $status "${ms}ms (阈值: $($ep.threshold)ms)"
|
||||
} catch {
|
||||
Add-Check "$($ep.desc) $($ep.path)" "FAIL" $_.Exception.Message
|
||||
}
|
||||
}
|
||||
|
||||
# 3. Prometheus 指标端点
|
||||
Write-Host "`n[3/6] Prometheus 指标端点" -ForegroundColor Yellow
|
||||
try {
|
||||
$metrics = Invoke-WebRequest -Uri "$BaseURL/metrics" -TimeoutSec 5
|
||||
if ($metrics.StatusCode -eq 200) {
|
||||
$content = $metrics.Content
|
||||
$hasHTTPMetrics = $content -match "http_requests_total"
|
||||
$hasDBMetrics = $content -match "db_query"
|
||||
Add-Check "指标端点 /metrics" "PASS" "HTTP $($metrics.StatusCode)"
|
||||
Add-Check "HTTP 请求指标" $(if ($hasHTTPMetrics) {"PASS"} else {"FAIL"}) $(if ($hasHTTPMetrics) {"存在 http_requests_total"} else {"缺少 http_requests_total — 需要接入 PrometheusMiddleware"})
|
||||
Add-Check "数据库指标" $(if ($hasDBMetrics) {"PASS"} else {"WARN"}) $(if ($hasDBMetrics) {"存在 db_query"} else {"缺少 db_query 指标"})
|
||||
}
|
||||
} catch {
|
||||
Add-Check "指标端点 /metrics" "FAIL" "端点不可用 — P0 问题:需要在 router.go 注册 /metrics" $true
|
||||
}
|
||||
|
||||
# 4. 速率限制验证
|
||||
Write-Host "`n[4/6] 速率限制功能验证" -ForegroundColor Yellow
|
||||
$rateLimitTriggered = $false
|
||||
$rlTotal = 0; $rl429 = 0
|
||||
|
||||
1..10 | ForEach-Object {
|
||||
try {
|
||||
$body = '{"account":"sre_healthcheck","password":"invalid_test_pwd"}'
|
||||
$resp = Invoke-WebRequest -Uri "$BaseURL/api/v1/auth/login" -Method POST -Body $body -ContentType "application/json" -ErrorAction SilentlyContinue -TimeoutSec 3
|
||||
$rlTotal++
|
||||
if ($resp.StatusCode -eq 429) { $rl429++; $rateLimitTriggered = $true }
|
||||
} catch { $rlTotal++ }
|
||||
}
|
||||
Add-Check "速率限制功能" $(if ($rateLimitTriggered) {"PASS"} else {"WARN"}) "$(10) 次请求中触发 ${rl429} 次 429$(if (-not $rateLimitTriggered) {' (10次内未触发,可能需要更多请求)'})"
|
||||
|
||||
# 5. Swagger 文档
|
||||
Write-Host "`n[5/6] API 文档" -ForegroundColor Yellow
|
||||
try {
|
||||
$swagger = Invoke-WebRequest -Uri "$BaseURL/swagger/index.html" -TimeoutSec 5
|
||||
Add-Check "Swagger 文档" $(if ($swagger.StatusCode -eq 200) {"PASS"} else {"WARN"}) "HTTP $($swagger.StatusCode)"
|
||||
} catch {
|
||||
Add-Check "Swagger 文档" "WARN" "不可访问(非阻塞)"
|
||||
}
|
||||
|
||||
# 6. 配置健全性检查
|
||||
Write-Host "`n[6/6] 配置健全性" -ForegroundColor Yellow
|
||||
$configFile = "config\config.yaml"
|
||||
if (Test-Path $configFile) {
|
||||
$config = Get-Content $configFile -Raw
|
||||
$hasDefaultJWT = $config -match "change-me-in-production"
|
||||
$isSQLite = $config -match "type: sqlite"
|
||||
Add-Check "JWT Secret 配置" $(if ($hasDefaultJWT) {"FAIL"} else {"PASS"}) $(if ($hasDefaultJWT) {"使用默认 Secret — 生产环境必须替换!"} else {"已自定义"}) $hasDefaultJWT
|
||||
Add-Check "数据库类型" $(if ($isSQLite) {"WARN"} else {"PASS"}) $(if ($isSQLite) {"SQLite — 生产环境应迁移至 PostgreSQL"} else {"PostgreSQL/MySQL")
|
||||
} else {
|
||||
Add-Check "配置文件" "WARN" "config.yaml 不存在,可能使用环境变量配置"
|
||||
}
|
||||
|
||||
# 生成报告
|
||||
$passRate = [math]::Round($passedChecks / [math]::Max($totalChecks, 1) * 100, 1)
|
||||
$overallStatus = if ($criticalFailures -gt 0) {"🔴 CRITICAL"} elseif ($passedChecks -lt $totalChecks) {"🟡 DEGRADED"} else {"🟢 HEALTHY"}
|
||||
|
||||
$mdReport = @"
|
||||
# UMS 日常健康巡检报告
|
||||
|
||||
- **检查时间**: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
||||
- **服务地址**: $BaseURL
|
||||
- **总体状态**: $overallStatus
|
||||
- **通过率**: ${passedChecks}/${totalChecks} ($passRate%)
|
||||
- **严重失败**: $criticalFailures
|
||||
|
||||
## 检查详情
|
||||
|
||||
| 状态 | 检查项 | 结果 | 说明 |
|
||||
|------|--------|------|------|
|
||||
$($report -join "`n")
|
||||
|
||||
## 后续行动
|
||||
|
||||
$(if ($criticalFailures -gt 0) {
|
||||
"⚠️ **存在 $criticalFailures 个严重问题,需立即处理!**"
|
||||
} elseif ($passedChecks -lt $totalChecks) {
|
||||
"📋 存在非严重警告,请在工作时间内跟进。"
|
||||
} else {
|
||||
"✅ 所有检查通过,系统健康。"
|
||||
})
|
||||
|
||||
---
|
||||
*由 scripts/ops/sre-daily-healthcheck.ps1 自动生成*
|
||||
"@
|
||||
|
||||
$mdReport | Set-Content -Path $reportFile -Encoding UTF8
|
||||
|
||||
Write-Host "`n=== 巡检汇总 ===" -ForegroundColor Cyan
|
||||
Write-Host "总体状态: $overallStatus"
|
||||
Write-Host "通过率: ${passedChecks}/${totalChecks} ($passRate%)"
|
||||
Write-Host "报告已保存至: $reportFile"
|
||||
|
||||
if ($criticalFailures -gt 0 -and $AlertOnFailure) {
|
||||
Write-Host "`n⚠️ 存在严重问题,应触发告警通知!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
exit 0
|
||||
61
scripts/quick_check.ps1
Normal file
61
scripts/quick_check.ps1
Normal file
@@ -0,0 +1,61 @@
|
||||
# 项目迁移快速检查脚本
|
||||
Write-Host "====================================" -ForegroundColor Cyan
|
||||
Write-Host "项目迁移快速检查" -ForegroundColor Cyan
|
||||
Write-Host "====================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# 1. 检查关键文件
|
||||
Write-Host "1. 检查关键文件..." -ForegroundColor Yellow
|
||||
$files = @("go.mod", "README.md", "cmd\server\main.go", "configs\config.yaml")
|
||||
foreach ($file in $files) {
|
||||
$path = "D:\project\$file"
|
||||
$status = if (Test-Path $path) { "✅" } else { "❌" }
|
||||
Write-Host " $status $file"
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# 2. 检查Go环境
|
||||
Write-Host "2. 检查Go环境..." -ForegroundColor Yellow
|
||||
try {
|
||||
$goVersion = go version 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host " ✅ Go已安装: $goVersion"
|
||||
} else {
|
||||
Write-Host " ❌ Go未安装"
|
||||
}
|
||||
} catch {
|
||||
Write-Host " ❌ Go未安装"
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# 3. 统计文件
|
||||
Write-Host "3. 统计文件..." -ForegroundColor Yellow
|
||||
$fileCount = (Get-ChildItem -Path D:\project -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count
|
||||
Write-Host " 文件数: $fileCount"
|
||||
Write-Host ""
|
||||
|
||||
# 4. 计算大小
|
||||
Write-Host "4. 计算大小..." -ForegroundColor Yellow
|
||||
$size = [math]::Round((Get-ChildItem -Path D:\project -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum / 1MB, 2)
|
||||
Write-Host " 总大小: ${size} MB"
|
||||
Write-Host ""
|
||||
|
||||
# 5. 检查目录结构
|
||||
Write-Host "5. 检查目录结构..." -ForegroundColor Yellow
|
||||
$dirs = @("cmd", "internal", "configs", "docs", "migrations", "deployment")
|
||||
foreach ($dir in $dirs) {
|
||||
$path = "D:\project\$dir"
|
||||
$status = if (Test-Path $path -PathType Container) { "✅" } else { "❌" }
|
||||
Write-Host " $status $dir\"
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "====================================" -ForegroundColor Cyan
|
||||
Write-Host "快速检查完成!" -ForegroundColor Green
|
||||
Write-Host "====================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "下一步:" -ForegroundColor Yellow
|
||||
Write-Host "1. 查看完整检查清单: docs\\migration\\MIGRATION_CHECKLIST.md" -ForegroundColor White
|
||||
Write-Host "2. 查看下一步操作: docs\\plans\\NEXT_STEPS.md" -ForegroundColor White
|
||||
Write-Host "3. 如果Go已安装,运行: go build ./cmd/server" -ForegroundColor White
|
||||
Write-Host ""
|
||||
137
scripts/test/test_all.bat
Normal file
137
scripts/test/test_all.bat
Normal file
@@ -0,0 +1,137 @@
|
||||
@echo off
|
||||
REM 用户管理系统 - Windows测试执行脚本
|
||||
|
||||
echo ==========================================
|
||||
echo 用户管理系统 - 测试执行脚本
|
||||
echo ==========================================
|
||||
|
||||
REM 颜色定义
|
||||
chcp 65001 >nul
|
||||
|
||||
:menu
|
||||
echo.
|
||||
echo 请选择测试类型:
|
||||
echo 1. 运行所有测试
|
||||
echo 2. 运行单元测试
|
||||
echo 3. 运行集成测试
|
||||
echo 4. 运行E2E测试
|
||||
echo 5. 运行鲁棒性测试
|
||||
echo 6. 生成覆盖率报告
|
||||
echo 7. 运行性能基准测试
|
||||
echo 8. 运行竞态检测
|
||||
echo 0. 退出
|
||||
echo.
|
||||
set /p choice=请输入选项(0-8):
|
||||
|
||||
if "%choice%"=="1" goto all_tests
|
||||
if "%choice%"=="2" goto unit_tests
|
||||
if "%choice%"=="3" goto integration_tests
|
||||
if "%choice%"=="4" goto e2e_tests
|
||||
if "%choice%"=="5" goto robust_tests
|
||||
if "%choice%"=="6" goto coverage
|
||||
if "%choice%"=="7" goto benchmark
|
||||
if "%choice%"=="8" goto race
|
||||
if "%choice%"=="0" goto end
|
||||
goto menu
|
||||
|
||||
:check_go
|
||||
echo [INFO] 检查Go环境...
|
||||
where go >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo [ERROR] Go未安装
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
goto :eof
|
||||
|
||||
:all_tests
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行所有测试
|
||||
echo ==========================================
|
||||
call :unit_tests
|
||||
echo [INFO] ✅ 所有测试准备完成
|
||||
echo ==========================================
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:unit_tests
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行单元测试
|
||||
echo ==========================================
|
||||
echo [INFO] 测试Domain层...
|
||||
go test -v ./internal/domain/... -run "Test.*"
|
||||
echo [INFO] ✅ 单元测试完成
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:integration_tests
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行集成测试
|
||||
echo ==========================================
|
||||
echo [INFO] 测试集成层...
|
||||
go test -v ./internal/integration/... -run "Test.*"
|
||||
echo [INFO] ✅ 集成测试完成
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:e2e_tests
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行端到端测试
|
||||
echo ==========================================
|
||||
echo [INFO] 测试E2E流程...
|
||||
go test -v ./internal/e2e/... -run "Test.*"
|
||||
echo [INFO] ✅ 端到端测试完成
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:robust_tests
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行鲁棒性测试
|
||||
echo ==========================================
|
||||
echo [INFO] 测试鲁棒性...
|
||||
go test -v ./internal/robustness/... -run "Test.*"
|
||||
echo [INFO] ✅ 鲁棒性完成
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:coverage
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行测试并生成覆盖率报告
|
||||
echo ==========================================
|
||||
go test -v -coverprofile=coverage.out ./...
|
||||
go tool cover -html=coverage.out -o coverage.html
|
||||
echo [INFO] 覆盖率报告已生成: coverage.html
|
||||
go tool cover -func=coverage.out
|
||||
echo [INFO] ✅ 覆盖率测试完成
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:benchmark
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行性能基准测试
|
||||
echo ==========================================
|
||||
go test -bench=. -benchmem ./internal/domain/...
|
||||
echo [INFO] ✅ 性能基准测试完成
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:race
|
||||
call :check_go
|
||||
echo ==========================================
|
||||
echo 运行竞态检测
|
||||
echo ==========================================
|
||||
go test -race ./...
|
||||
echo [INFO] ✅ 竞态检测完成
|
||||
pause
|
||||
goto menu
|
||||
|
||||
:end
|
||||
echo 退出测试脚本
|
||||
exit /b 0
|
||||
39
scripts/test/test_api.bat
Normal file
39
scripts/test/test_api.bat
Normal file
@@ -0,0 +1,39 @@
|
||||
# 用户管理系统 API 测试脚本
|
||||
|
||||
@if "%TEST_ADMIN_ACCOUNT%"=="" set TEST_ADMIN_ACCOUNT=admin
|
||||
@if "%TEST_ADMIN_PASSWORD%"=="" (
|
||||
@echo 请先设置 TEST_ADMIN_PASSWORD
|
||||
@exit /b 1
|
||||
)
|
||||
|
||||
## 1. 健康检查
|
||||
@echo "=== 1. 健康检查 ==="
|
||||
curl http://localhost:8080/health
|
||||
echo.
|
||||
|
||||
## 2. 用户注册
|
||||
@echo.
|
||||
@echo "=== 2. 用户注册 ==="
|
||||
curl -X POST http://localhost:8080/api/v1/auth/register ^
|
||||
-H "Content-Type: application/json" ^
|
||||
-d "{\"username\":\"testuser\",\"password\":\"Test123456\",\"email\":\"test@example.com\"}"
|
||||
echo.
|
||||
|
||||
## 3. 用户登录
|
||||
@echo.
|
||||
@echo "=== 3. 用户登录(admin) ==="
|
||||
curl -X POST http://localhost:8080/api/v1/auth/login ^
|
||||
-H "Content-Type: application/json" ^
|
||||
-d "{\"account\":\"%TEST_ADMIN_ACCOUNT%\",\"password\":\"%TEST_ADMIN_PASSWORD%\"}"
|
||||
echo.
|
||||
|
||||
## 4. 获取用户信息(需要token,这里先跳过)
|
||||
@echo.
|
||||
@echo "=== 4. 需要使用登录返回的token ==="
|
||||
@echo "请复制上面登录返回的access_token,然后手动测试:"
|
||||
@echo "curl -X GET http://localhost:8080/api/v1/auth/userinfo -H \"Authorization: Bearer YOUR_TOKEN\""
|
||||
echo.
|
||||
|
||||
@echo.
|
||||
@echo "测试完成!"
|
||||
@pause
|
||||
49
scripts/test/test_api.sh
Normal file
49
scripts/test/test_api.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 用户管理系统 API 测试脚本
|
||||
|
||||
TEST_ADMIN_ACCOUNT="${TEST_ADMIN_ACCOUNT:-admin}"
|
||||
if [ -z "${TEST_ADMIN_PASSWORD:-}" ]; then
|
||||
echo "请先设置 TEST_ADMIN_PASSWORD"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== 1. 健康检查 ==="
|
||||
curl http://localhost:8080/health
|
||||
echo -e "\n"
|
||||
|
||||
echo "=== 2. 用户注册 ==="
|
||||
curl -X POST http://localhost:8080/api/v1/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser","password":"Test123456","email":"test@example.com"}'
|
||||
echo -e "\n"
|
||||
|
||||
echo "=== 3. 用户登录(admin) ==="
|
||||
LOGIN_RESPONSE=$(curl -s -X POST http://localhost:8080/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"account\":\"${TEST_ADMIN_ACCOUNT}\",\"password\":\"${TEST_ADMIN_PASSWORD}\"}")
|
||||
|
||||
echo "$LOGIN_RESPONSE"
|
||||
|
||||
# 提取token
|
||||
TOKEN=$(echo $LOGIN_RESPONSE | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
echo -e "\n=== 4. 获取用户信息 ==="
|
||||
if [ -n "$TOKEN" ]; then
|
||||
curl -X GET http://localhost:8080/api/v1/auth/userinfo \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
echo -e "\n"
|
||||
else
|
||||
echo "无法获取token,跳过此测试"
|
||||
fi
|
||||
|
||||
echo -e "\n=== 5. 测试限流(连续快速请求) ==="
|
||||
for i in {1..6}; do
|
||||
echo "第 $i 次登录请求:"
|
||||
curl -s -X POST http://localhost:8080/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"account":"wrong","password":"wrong"}'
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo -e "\n测试完成!"
|
||||
399
scripts/test/test_full.sh
Normal file
399
scripts/test/test_full.sh
Normal file
@@ -0,0 +1,399 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 用户管理系统自动化测试脚本
|
||||
# 用途:全面测试所有功能和接口
|
||||
|
||||
BASE_URL="http://localhost:8080"
|
||||
ADMIN_TOKEN=""
|
||||
USER_TOKEN=""
|
||||
USER_ID=""
|
||||
TEST_ADMIN_ACCOUNT="${TEST_ADMIN_ACCOUNT:-admin}"
|
||||
TEST_ADMIN_PASSWORD="${TEST_ADMIN_PASSWORD:-}"
|
||||
|
||||
if [ -z "${TEST_ADMIN_PASSWORD}" ]; then
|
||||
echo "请先设置 TEST_ADMIN_PASSWORD"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 打印函数
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓ $1${NC}"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗ $1${NC}"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${YELLOW}➤ $1${NC}"
|
||||
}
|
||||
|
||||
# 测试1:健康检查
|
||||
test_health_check() {
|
||||
print_info "测试1:健康检查"
|
||||
response=$(curl -s -w "\n%{http_code}" "${BASE_URL}/health")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "健康检查通过 (200)"
|
||||
echo "响应: $body"
|
||||
else
|
||||
print_error "健康检查失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试2:用户注册
|
||||
test_register() {
|
||||
print_info "测试2:用户注册"
|
||||
|
||||
# 测试正常注册
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser1","password":"Test123456","email":"test1@example.com"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "用户注册成功"
|
||||
USER_ID=$(echo "$body" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
echo "用户ID: $USER_ID"
|
||||
else
|
||||
print_error "用户注册失败 (HTTP $http_code)"
|
||||
echo "响应: $body"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 测试重复用户名
|
||||
print_info "测试2.1:重复用户名注册"
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser1","password":"Test123456","email":"test2@example.com"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "400" ] || [ "$http_code" = "409" ]; then
|
||||
print_success "重复用户名注册被正确拒绝 ($http_code)"
|
||||
else
|
||||
print_error "重复用户名验证失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 测试弱密码
|
||||
print_info "测试2.2:弱密码注册"
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser2","password":"123","email":"test2@example.com"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "400" ]; then
|
||||
print_success "弱密码注册被正确拒绝 (400)"
|
||||
else
|
||||
print_error "弱密码验证失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 测试无效邮箱
|
||||
print_info "测试2.3:无效邮箱注册"
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"testuser3","password":"Test123456","email":"invalid"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "400" ]; then
|
||||
print_success "无效邮箱注册被正确拒绝 (400)"
|
||||
else
|
||||
print_error "邮箱验证失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试3:用户登录
|
||||
test_login() {
|
||||
print_info "测试3:用户登录"
|
||||
|
||||
# 测试正常登录(管理员)
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"account\":\"${TEST_ADMIN_ACCOUNT}\",\"password\":\"${TEST_ADMIN_PASSWORD}\"}")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "管理员登录成功"
|
||||
ADMIN_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
||||
echo "获取到访问令牌"
|
||||
else
|
||||
print_error "管理员登录失败 (HTTP $http_code)"
|
||||
echo "响应: $body"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 测试错误密码
|
||||
print_info "测试3.1:错误密码登录"
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"account":"admin","password":"wrong"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "401" ]; then
|
||||
print_success "错误密码登录被正确拒绝 (401)"
|
||||
else
|
||||
print_error "错误密码验证失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 测试用户名登录
|
||||
if [ -n "$USER_ID" ]; then
|
||||
print_info "测试3.2:用户名登录(新注册用户)"
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"account":"testuser1","password":"Test123456"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "新用户登录成功"
|
||||
USER_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
||||
else
|
||||
print_error "新用户登录失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试4:获取用户信息
|
||||
test_get_userinfo() {
|
||||
print_info "测试4:获取用户信息(需要认证)"
|
||||
|
||||
if [ -z "$ADMIN_TOKEN" ]; then
|
||||
print_error "没有访问令牌,跳过测试"
|
||||
return
|
||||
fi
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X GET "${BASE_URL}/api/v1/auth/userinfo" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "获取用户信息成功"
|
||||
echo "响应: $body"
|
||||
else
|
||||
print_error "获取用户信息失败 (HTTP $http_code)"
|
||||
echo "响应: $body"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 测试无令牌访问
|
||||
print_info "测试4.1:无令牌访问"
|
||||
response=$(curl -s -w "\n%{http_code}" -X GET "${BASE_URL}/api/v1/auth/userinfo")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "401" ]; then
|
||||
print_success "无令牌访问被正确拒绝 (401)"
|
||||
else
|
||||
print_error "认证验证失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 测试无效令牌
|
||||
print_info "测试4.2:无效令牌访问"
|
||||
response=$(curl -s -w "\n%{http_code}" -X GET "${BASE_URL}/api/v1/auth/userinfo" \
|
||||
-H "Authorization: Bearer invalid_token")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "401" ]; then
|
||||
print_success "无效令牌访问被正确拒绝 (401)"
|
||||
else
|
||||
print_error "无效令牌验证失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试5:获取用户列表
|
||||
test_get_users() {
|
||||
print_info "测试5:获取用户列表(需要认证)"
|
||||
|
||||
if [ -z "$ADMIN_TOKEN" ]; then
|
||||
print_error "没有访问令牌,跳过测试"
|
||||
return
|
||||
fi
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X GET "${BASE_URL}/api/v1/users" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "获取用户列表成功"
|
||||
echo "响应: $body"
|
||||
else
|
||||
print_error "获取用户列表失败 (HTTP $http_code)"
|
||||
echo "响应: $body"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试6:更新用户信息
|
||||
test_update_user() {
|
||||
print_info "测试6:更新用户信息(需要认证)"
|
||||
|
||||
if [ -z "$ADMIN_TOKEN" ] || [ -z "$USER_ID" ]; then
|
||||
print_error "缺少必要参数,跳过测试"
|
||||
return
|
||||
fi
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X PUT "${BASE_URL}/api/v1/users/${USER_ID}" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"nickname":"测试用户昵称","bio":"这是个人简介"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "更新用户信息成功"
|
||||
echo "响应: $body"
|
||||
else
|
||||
print_error "更新用户信息失败 (HTTP $http_code)"
|
||||
echo "响应: $body"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试7:令牌刷新
|
||||
test_refresh_token() {
|
||||
print_info "测试7:令牌刷新"
|
||||
|
||||
if [ -z "$ADMIN_TOKEN" ]; then
|
||||
print_error "没有访问令牌,跳过测试"
|
||||
return
|
||||
fi
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/refresh" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"refresh_token\":\"${ADMIN_TOKEN}\"}")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ] || [ "$http_code" = "401" ]; then
|
||||
print_success "令牌刷新接口响应正常 (HTTP $http_code)"
|
||||
echo "响应: $body"
|
||||
else
|
||||
print_error "令牌刷新失败 (HTTP $http_code)"
|
||||
echo "响应: $body"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试8:限流测试
|
||||
test_rate_limit() {
|
||||
print_info "测试8:限流功能测试"
|
||||
|
||||
print_info "快速发送6次请求测试限流..."
|
||||
success_count=0
|
||||
rate_limited=0
|
||||
|
||||
for i in {1..6}; do
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"account":"wrong","password":"wrong"}')
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "429" ]; then
|
||||
rate_limited=$((rate_limited + 1))
|
||||
echo " 请求 $i: 被限流 (429)"
|
||||
else
|
||||
success_count=$((success_count + 1))
|
||||
echo " 请求 $i: 正常 (HTTP $http_code)"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$rate_limited" -gt 0 ]; then
|
||||
print_success "限流功能正常生效,触发 $rate_limited 次限流"
|
||||
else
|
||||
print_error "限流功能未触发,建议检查配置"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试9:Prometheus 指标
|
||||
test_metrics() {
|
||||
print_info "测试9:Prometheus 指标采集"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" "${BASE_URL}/metrics")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "Prometheus 指标端点正常"
|
||||
|
||||
# 检查关键指标
|
||||
if echo "$body" | grep -q "http_requests_total"; then
|
||||
print_success "✓ http_requests_total 指标存在"
|
||||
fi
|
||||
if echo "$body" | grep -q "http_request_duration_seconds"; then
|
||||
print_success "✓ http_request_duration_seconds 指标存在"
|
||||
fi
|
||||
if echo "$body" | grep -q "user_logins_total"; then
|
||||
print_success "✓ user_logins_total 指标存在"
|
||||
fi
|
||||
else
|
||||
print_error "Prometheus 指标端点失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 测试10:登出
|
||||
test_logout() {
|
||||
print_info "测试10:用户登出"
|
||||
|
||||
if [ -z "$ADMIN_TOKEN" ]; then
|
||||
print_error "没有访问令牌,跳过测试"
|
||||
return
|
||||
fi
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/api/v1/auth/logout" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}")
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "登出成功"
|
||||
else
|
||||
print_error "登出失败 (HTTP $http_code)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主测试流程
|
||||
main() {
|
||||
echo "============================================"
|
||||
echo " 用户管理系统自动化测试"
|
||||
echo " 测试环境: ${BASE_URL}"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
test_health_check
|
||||
test_register
|
||||
test_login
|
||||
test_get_userinfo
|
||||
test_get_users
|
||||
test_update_user
|
||||
test_refresh_token
|
||||
test_rate_limit
|
||||
test_metrics
|
||||
test_logout
|
||||
|
||||
echo "============================================"
|
||||
echo " 测试完成"
|
||||
echo "============================================"
|
||||
}
|
||||
|
||||
# 执行测试
|
||||
main
|
||||
14
scripts/test/test_go.bat
Normal file
14
scripts/test/test_go.bat
Normal file
@@ -0,0 +1,14 @@
|
||||
@echo off
|
||||
echo Testing Go installation...
|
||||
echo.
|
||||
echo Checking Go version...
|
||||
"C:\Program Files\Go\bin\go.exe" version
|
||||
if %errorlevel% == 0 (
|
||||
echo.
|
||||
echo SUCCESS: Go is working!
|
||||
) else (
|
||||
echo.
|
||||
echo ERROR: Go is not working
|
||||
)
|
||||
echo.
|
||||
pause
|
||||
211
scripts/validate.bat
Normal file
211
scripts/validate.bat
Normal file
@@ -0,0 +1,211 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
echo ====================================
|
||||
echo 用户管理系统 - 代码结构验证
|
||||
echo ====================================
|
||||
echo.
|
||||
|
||||
echo 正在验证项目结构...
|
||||
echo.
|
||||
|
||||
set TOTAL=0
|
||||
set EXISTS=0
|
||||
set MISSING=0
|
||||
|
||||
REM 检查文件
|
||||
if exist "cmd\server\main.go" (
|
||||
echo [√] cmd\server\main.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] cmd\server\main.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "go.mod" (
|
||||
echo [√] go.mod
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] go.mod
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\domain\user.go" (
|
||||
echo [√] internal\domain\user.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\domain\user.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\service\auth.go" (
|
||||
echo [√] internal\service\auth.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\service\auth.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\service\user.go" (
|
||||
echo [√] internal\service\user.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\service\user.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\service\role.go" (
|
||||
echo [√] internal\service\role.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\service\role.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\service\permission.go" (
|
||||
echo [√] internal\service\permission.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\service\permission.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\service\device.go" (
|
||||
echo [√] internal\service\device.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\service\device.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\api\handler\auth.go" (
|
||||
echo [√] internal\api\handler\auth.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\api\handler\auth.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\api\handler\user.go" (
|
||||
echo [√] internal\api\handler\user.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\api\handler\user.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\api\handler\role.go" (
|
||||
echo [√] internal\api\handler\role.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\api\handler\role.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\api\handler\permission.go" (
|
||||
echo [√] internal\api\handler\permission.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\api\handler\permission.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\api\handler\device.go" (
|
||||
echo [√] internal\api\handler\device.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\api\handler\device.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\cache\cache_manager.go" (
|
||||
echo [√] internal\cache\cache_manager.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\cache\cache_manager.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\monitoring\metrics.go" (
|
||||
echo [√] internal\monitoring\metrics.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\monitoring\metrics.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "internal\api\router\router.go" (
|
||||
echo [√] internal\api\router\router.go
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] internal\api\router\router.go
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "migrations\sqlite\V1__init.sql" (
|
||||
echo [√] migrations\sqlite\V1__init.sql
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] migrations\sqlite\V1__init.sql
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "configs\config.yaml" (
|
||||
echo [√] configs\config.yaml
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] configs\config.yaml
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
if exist "docker-compose.yml" (
|
||||
echo [√] docker-compose.yml
|
||||
set /a EXISTS+=1
|
||||
) else (
|
||||
echo [×] docker-compose.yml
|
||||
set /a MISSING+=1
|
||||
)
|
||||
set /a TOTAL+=1
|
||||
|
||||
echo.
|
||||
echo ====================================
|
||||
echo 验证结果
|
||||
echo ====================================
|
||||
echo 总文件数: %TOTAL%
|
||||
echo 存在文件: %EXISTS%
|
||||
echo 缺失文件: %MISSING%
|
||||
echo.
|
||||
|
||||
set /a PERCENT=%EXISTS%*100/%TOTAL%
|
||||
echo 完成度: %PERCENT%%%
|
||||
|
||||
if %PERCENT% GEQ 95 (
|
||||
echo.
|
||||
echo [SUCCESS] 项目结构完整,可以进行验收!
|
||||
exit /b 0
|
||||
) else if %PERCENT% GEQ 80 (
|
||||
echo.
|
||||
echo [WARNING] 项目基本完成,但还有部分功能需要补充
|
||||
exit /b 1
|
||||
) else (
|
||||
echo.
|
||||
echo [ERROR] 项目完成度较低,需要继续完善
|
||||
exit /b 1
|
||||
)
|
||||
18
scripts/verify_go.bat
Normal file
18
scripts/verify_go.bat
Normal file
@@ -0,0 +1,18 @@
|
||||
@echo off
|
||||
echo Verifying Go installation...
|
||||
echo.
|
||||
"C:\Program Files\Go\bin\go.exe" version
|
||||
echo.
|
||||
if %errorlevel% == 0 (
|
||||
echo [SUCCESS] Go is working!
|
||||
echo.
|
||||
echo Please tell WorkBuddy: "Go验证成功"
|
||||
) else (
|
||||
echo [ERROR] Go is not working
|
||||
echo.
|
||||
echo Please try:
|
||||
echo 1. Close all command windows
|
||||
echo 2. Open new PowerShell
|
||||
echo 3. Run: go version
|
||||
)
|
||||
pause
|
||||
Reference in New Issue
Block a user