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:
2026-04-07 18:10:36 +08:00
parent 5dbb530b76
commit 5b6bd93179
152 changed files with 8775 additions and 4084 deletions

11
scripts/build.bat Normal file
View 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
)

View File

@@ -0,0 +1,124 @@
# CE-001: 数据库不可用韧性验证
# 验证:当数据库连接中断时,健康检查正确返回 DOWNAPI 返回 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
}

View 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++
}
# 验证3QPS 合理(服务未被压垮)
$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
View 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
View 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

View 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 "========================================"

View 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 "========================================"

View 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 "========================================"

View 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 "========================================"

View 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

View 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
View 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
View 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
View 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 "========================================"

View 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 "========================================"

View 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
View 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
View 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
View 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
View 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
View 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 ""
}
# 测试9Prometheus 指标
test_metrics() {
print_info "测试9Prometheus 指标采集"
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
View 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
View 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
View 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