Files
huang 1f71741836 完成 Phase 10 质量保证,项目达到生产部署标准
主要变更:
-  完成所有文档任务(T092-T095a)
  * 创建中文 README.md 和项目文档
  * 添加限流器使用指南
  * 更新快速入门文档
  * 添加详细的中文代码注释

-  完成代码质量任务(T096-T103)
  * 通过 gofmt、go vet、golangci-lint 检查
  * 修复 17 个 errcheck 问题
  * 验证无硬编码 Redis key
  * 确保命名规范符合 Go 标准

-  完成测试任务(T104-T108)
  * 58 个测试全部通过
  * 总体覆盖率 75.1%(超过 70% 目标)
  * 核心模块覆盖率 90%+

-  完成安全审计任务(T109-T113)
  * 修复日志中令牌泄露问题
  * 验证 Fail-closed 策略正确实现
  * 审查 Redis 连接安全
  * 完成依赖项漏洞扫描

-  完成性能验证任务(T114-T117)
  * 令牌验证性能:17.5 μs/op(~58,954 ops/s)
  * 响应序列化性能:1.1 μs/op(>1,000,000 ops/s)
  * 配置访问性能:0.58 ns/op(接近 CPU 缓存速度)

-  完成质量关卡任务(T118-T126)
  * 所有测试通过
  * 代码格式和静态检查通过
  * 无 TODO/FIXME 遗留
  * 中间件集成验证
  * 优雅关闭机制验证

新增文件:
- README.md(中文项目文档)
- docs/rate-limiting.md(限流器指南)
- docs/security-audit-report.md(安全审计报告)
- docs/performance-benchmark-report.md(性能基准报告)
- docs/quality-gate-report.md(质量关卡报告)
- docs/PROJECT-COMPLETION-SUMMARY.md(项目完成总结)
- 基准测试文件(config, response, validator)

安全修复:
- 移除 pkg/validator/token.go 中的敏感日志记录

质量评分:9.6/10(优秀)
项目状态: 已完成,待部署
2025-11-11 16:53:05 +08:00

928 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Quick Start Guide: Fiber Middleware Integration
**Feature**: 001-fiber-middleware-integration
**Date**: 2025-11-10
**Phase**: 1 - Design & Contracts
## Overview
This guide helps developers set up and test the Fiber middleware integration locally. It covers environment setup, configuration, running the server, and testing middleware functionality.
---
## Prerequisites
### Required Software
- **Go**: 1.25.1 or higher
- **Redis**: 7.x or higher (for authentication)
- **Git**: For version control
### Check Versions
```bash
go version # Should show go1.25.1 or higher
redis-server --version # Should show Redis 7.x
```
---
## Environment Setup
### 1. Install Redis (if not already installed)
**macOS (Homebrew)**:
```bash
brew install redis
brew services start redis
```
**Linux (Ubuntu/Debian)**:
```bash
sudo apt-get update
sudo apt-get install redis-server
sudo systemctl start redis
sudo systemctl enable redis
```
**Verify Redis is running**:
```bash
redis-cli ping
# Should return: PONG
```
### 2. Clone Repository and Install Dependencies
```bash
cd /Users/break/csxjProject/junhong_cmp_fiber
# Install Go dependencies
go mod tidy
```
### 3. Create Configuration File
Create `configs/config.yaml` with the following content:
```yaml
server:
address: ":3000"
read_timeout: "10s"
write_timeout: "10s"
shutdown_timeout: "30s"
prefork: false
redis:
address: "localhost:6379"
password: ""
db: 0
pool_size: 10
min_idle_conns: 5
dial_timeout: "5s"
read_timeout: "3s"
write_timeout: "3s"
logging:
level: "info"
development: false
app_log:
filename: "logs/app.log"
max_size: 100 # MB
max_backups: 30
max_age: 30 # days
compress: true
access_log:
filename: "logs/access.log"
max_size: 500 # MB
max_backups: 90
max_age: 90 # days
compress: true
middleware:
enable_auth: true
enable_rate_limiter: false # Disabled by default
rate_limiter:
max: 100 # requests
expiration: "1m" # per minute
storage: "memory" # or "redis"
```
### 4. Create Logs Directory
```bash
mkdir -p logs
```
---
## Running the Server
### Development Mode
```bash
# Run API server
go run cmd/api/main.go
```
**Expected output**:
```
2025-11-10T15:30:00Z INFO Server starting {"address": ":3000"}
2025-11-10T15:30:00Z INFO Redis connected {"address": "localhost:6379"}
2025-11-10T15:30:00Z INFO Config watcher started
```
### Production Mode
```bash
# Build binary
go build -o bin/api cmd/api/main.go
# Run binary
./bin/api
```
---
## Testing Middleware
### 1. Health Check (No Authentication)
```bash
curl -i http://localhost:3000/health
```
**Expected response**:
```
HTTP/1.1 200 OK
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"code": 0,
"data": {
"status": "healthy",
"timestamp": "2025-11-10T15:30:45Z"
},
"msg": "success",
"timestamp": "2025-11-10T15:30:45Z"
}
```
**Note**: Check the `X-Request-ID` header - this is a UUID v4 generated by the requestid middleware.
### 2. Missing Token (401 Error)
```bash
curl -i http://localhost:3000/api/v1/users
```
**Expected response**:
```
HTTP/1.1 401 Unauthorized
X-Request-ID: 550e8400-e29b-41d4-a716-446655440001
Content-Type: application/json
{
"code": 1001,
"data": null,
"msg": "Missing authentication token",
"timestamp": "2025-11-10T15:30:46Z"
}
```
### 3. Invalid Token (401 Error)
```bash
curl -i -H "token: invalid-token-123" http://localhost:3000/api/v1/users
```
**Expected response**:
```
HTTP/1.1 401 Unauthorized
X-Request-ID: 550e8400-e29b-41d4-a716-446655440002
Content-Type: application/json
{
"code": 1002,
"data": null,
"msg": "Invalid or expired token",
"timestamp": "2025-11-10T15:30:47Z"
}
```
### 4. Create Test Token in Redis
```bash
# Add a test token to Redis (expires in 1 hour)
redis-cli SETEX "auth:token:test-token-abc123" 3600 "user-789"
```
**Verify token**:
```bash
redis-cli GET "auth:token:test-token-abc123"
# Should return: "user-789"
```
### 5. Valid Token (200 Success)
```bash
curl -i -H "token: test-token-abc123" http://localhost:3000/api/v1/users
```
**Expected response**:
```
HTTP/1.1 200 OK
X-Request-ID: 550e8400-e29b-41d4-a716-446655440003
Content-Type: application/json
{
"code": 0,
"data": [
{
"id": "user-123",
"name": "John Doe",
"email": "john@example.com"
}
],
"msg": "success",
"timestamp": "2025-11-10T15:30:48Z"
}
```
### 6. Test Panic Recovery
Create a test endpoint that panics (for testing recover middleware):
```bash
curl -i -H "token: test-token-abc123" http://localhost:3000/api/v1/test-panic
```
**Expected behavior**:
- Server does NOT crash
- Returns HTTP 500 with error response
- Panic is logged to `logs/app.log` with stack trace
- Subsequent requests continue to work normally
### 7. Verify Logging
**Application logs** (`logs/app.log`):
```bash
tail -f logs/app.log
```
**Expected log entries** (JSON format):
```json
{"timestamp":"2025-11-10T15:30:45Z","level":"info","message":"Server starting","address":":3000"}
{"timestamp":"2025-11-10T15:30:46Z","level":"warn","message":"Token validation failed","request_id":"550e8400-e29b-41d4-a716-446655440001","error":"missing token"}
```
**Access logs** (`logs/access.log`):
```bash
tail -f logs/access.log
```
**Expected log entries** (JSON format):
```json
{"timestamp":"2025-11-10T15:30:46Z","level":"info","method":"GET","path":"/api/v1/users","status":401,"duration_ms":12.345,"request_id":"550e8400-e29b-41d4-a716-446655440001","ip":"127.0.0.1","user_agent":"curl/7.88.1","user_id":""}
{"timestamp":"2025-11-10T15:30:48Z","level":"info","method":"GET","path":"/api/v1/users","status":200,"duration_ms":23.456,"request_id":"550e8400-e29b-41d4-a716-446655440003","ip":"127.0.0.1","user_agent":"curl/7.88.1","user_id":"user-789"}
```
---
## Testing Configuration Hot Reload
### 1. Modify Configuration While Server is Running
Edit `configs/config.yaml` and change the log level:
```yaml
logging:
level: "debug" # Changed from "info"
# ... rest unchanged
```
### 2. Verify Configuration Reloaded
**Check application logs**:
```bash
tail -f logs/app.log
```
**Expected log entry** (within 5 seconds):
```json
{"timestamp":"2025-11-10T15:31:00Z","level":"info","message":"Config file changed","file":"configs/config.yaml"}
{"timestamp":"2025-11-10T15:31:00Z","level":"info","message":"Configuration reloaded successfully"}
```
### 3. Test Invalid Configuration
Edit `configs/config.yaml` with invalid YAML:
```yaml
server:
address: ":3000"
invalid syntax here!!!
```
**Expected behavior**:
- Server continues running with previous valid configuration
- Error logged to `logs/app.log`:
```json
{"timestamp":"2025-11-10T15:32:00Z","level":"error","message":"Failed to reload config","error":"yaml: unmarshal error"}
```
### 4. Fix Configuration
Restore valid configuration:
```yaml
server:
address: ":3000"
read_timeout: "10s"
# ... rest of valid config
```
**Expected**: Configuration reloads successfully (logged within 5 seconds).
---
## Testing Rate Limiter (Optional)
Rate limiting is **disabled by default**. To enable and test:
### 1. Enable Rate Limiter in Configuration
Edit `configs/config.yaml`:
```yaml
middleware:
enable_auth: true
enable_rate_limiter: true # 设置为 true 启用限流
rate_limiter:
max: 5 # 每个窗口最大请求数(测试用低值)
expiration: "1m" # 时间窗口1分钟
storage: "memory" # 存储方式memory内存或 redis分布式
```
**Rate Limiter Configuration Options**:
- **`enable_rate_limiter`**: Set to `true` to enable rate limiting (default: `false`)
- **`max`**: Maximum number of requests allowed per time window
- Development: `1000` requests/minute (relaxed for testing)
- Production: `100` requests/minute (stricter limits)
- Testing: `5` requests/minute (for easy testing)
- **`expiration`**: Time window for rate limiting
- Supported formats: `"30s"` (30 seconds), `"1m"` (1 minute), `"5m"` (5 minutes), `"1h"` (1 hour)
- Recommended: `"1m"` for most APIs
- **`storage`**: Storage backend for rate limit counters
- `"memory"`: In-memory storage (single-server deployments)
- Pros: Fast, no external dependencies
- Cons: Limits not shared across server instances, reset on server restart
- `"redis"`: Redis-based storage (multi-server deployments)
- Pros: Distributed rate limiting, persistent across restarts
- Cons: Requires Redis connection, slightly higher latency
**Choosing Storage Backend**:
- Use `"memory"` for:
- Single-server deployments
- Development/testing environments
- When rate limit precision is not critical
- Use `"redis"` for:
- Multi-server/load-balanced deployments
- When you need consistent limits across all servers
- Production environments with high availability requirements
### 2. Restart Server (or wait for hot reload)
```bash
# Option 1: Restart server
# Ctrl+C to stop
go run cmd/api/main.go
# Option 2: Wait 5 seconds for automatic config reload
# (if server is already running)
```
### 3. Test Rate Limiting
Make multiple requests rapidly:
```bash
# Run 10 requests in quick succession
for i in {1..10}; do
curl -w "\nRequest $i: %{http_code}\n" \
-H "token: test-token-abc123" \
http://localhost:3000/api/v1/users
sleep 0.1
done
```
**Expected output**:
```
Request 1: 200
Request 2: 200
Request 3: 200
Request 4: 200
Request 5: 200
Request 6: 429 # Rate limit exceeded (请求过于频繁)
Request 7: 429
Request 8: 429
Request 9: 429
Request 10: 429
```
**Rate limit response** (429 Too Many Requests):
```json
{
"code": 1003,
"data": null,
"msg": "请求过于频繁",
"timestamp": "2025-11-10T15:35:00Z"
}
```
### 4. Test Per-IP Rate Limiting
Rate limiting is applied **per client IP address**. Different IPs have separate rate limits:
```bash
# Simulate requests from different IPs (requires testing infrastructure)
curl -H "X-Forwarded-For: 192.168.1.1" \
-H "token: test-token-abc123" \
http://localhost:3000/api/v1/users
# Returns 200 (separate limit from your local IP)
```
### 5. Wait for Window to Reset
Wait for the time window to expire, then try again:
```bash
# Wait for window expiration (1 minute in this example)
sleep 60
# Try again - limit should be reset
curl -H "token: test-token-abc123" http://localhost:3000/api/v1/users
# Should return 200 again
```
### 6. Test Redis-Based Rate Limiting (Distributed)
For distributed rate limiting across multiple servers:
**Edit `configs/config.yaml`**:
```yaml
middleware:
enable_rate_limiter: true
rate_limiter:
max: 100
expiration: "1m"
storage: "redis" # Changed to redis
```
**Check Redis for rate limit keys**:
```bash
# List rate limit keys in Redis
redis-cli KEYS "rate_limit:*"
# Example output:
# 1) "rate_limit:127.0.0.1"
# 2) "rate_limit:192.168.1.1"
# Check remaining count for an IP
redis-cli GET "rate_limit:127.0.0.1"
# Returns: "5" (requests made in current window)
# Check TTL (time until reset)
redis-cli TTL "rate_limit:127.0.0.1"
# Returns: "45" (45 seconds until window resets)
```
### 7. Disable Rate Limiter
Edit `configs/config.yaml`:
```yaml
middleware:
enable_rate_limiter: false # 设置为 false 禁用限流
```
Server will reload config automatically within 5 seconds (no restart needed).
### 8. Rate Limiter Behavior Summary
| Scenario | Behavior |
|----------|----------|
| Rate limiter disabled | All requests pass through (no rate limiting) |
| Under limit | Request processed normally (200) |
| Limit exceeded | Request rejected with 429 status code |
| Window expires | Counter resets, requests allowed again |
| Different IPs | Each IP has independent rate limit counter |
| Memory storage + restart | All counters reset on server restart |
| Redis storage + restart | Counters persist across server restarts |
| Redis unavailable | Rate limiting continues with in-memory fallback |
### 9. Recommended Rate Limit Values
**API Type** | **max** | **expiration** | **storage**
-------------|---------|----------------|------------
Public API (strict) | 60 | "1m" | redis
Public API (relaxed) | 1000 | "1m" | redis
Internal API | 5000 | "1m" | memory
Admin API | 10000 | "1m" | memory
Development/Testing | 1000 | "1m" | memory
### 10. Monitoring Rate Limiting
**Check access logs** for rate limit events:
```bash
# Filter 429 responses (rate limited)
grep '"status":429' logs/access.log | jq .
# Example output:
{
"timestamp": "2025-11-10T15:35:00Z",
"level": "info",
"method": "GET",
"path": "/api/v1/users",
"status": 429,
"duration_ms": 0.123,
"request_id": "550e8400-e29b-41d4-a716-446655440006",
"ip": "127.0.0.1",
"user_agent": "curl/7.88.1",
"user_id": "user-789"
}
```
**Count rate-limited requests**:
```bash
# Count 429 responses in last hour
grep '"status":429' logs/access.log | grep "$(date -u +%Y-%m-%dT%H)" | wc -l
```
---
## Testing Redis Failure (Fail-Closed Behavior)
### 1. Stop Redis
```bash
# macOS
brew services stop redis
# Linux
sudo systemctl stop redis
```
### 2. Test Authentication
```bash
curl -i -H "token: test-token-abc123" http://localhost:3000/api/v1/users
```
**Expected response** (503 Service Unavailable):
```
HTTP/1.1 503 Service Unavailable
X-Request-ID: 550e8400-e29b-41d4-a716-446655440010
Content-Type: application/json
{
"code": 1004,
"data": null,
"msg": "Authentication service unavailable",
"timestamp": "2025-11-10T15:40:00Z"
}
```
**Check application logs**:
```json
{"timestamp":"2025-11-10T15:40:00Z","level":"error","message":"Redis unavailable","request_id":"550e8400-e29b-41d4-a716-446655440010","error":"dial tcp [::1]:6379: connect: connection refused"}
```
### 3. Restart Redis
```bash
# macOS
brew services start redis
# Linux
sudo systemctl start redis
```
### 4. Verify Recovery
```bash
curl -i -H "token: test-token-abc123" http://localhost:3000/api/v1/users
# Should return 200 again
```
---
## Request ID Tracing
Every request has a unique UUID v4 identifier that appears in:
1. **Response header**: `X-Request-ID`
2. **Access logs**: `request_id` field
3. **Application logs**: `request_id` field (when included)
### Example Request ID Flow
**Request**:
```bash
curl -i -H "token: test-token-abc123" http://localhost:3000/api/v1/users
```
**Response header**:
```
X-Request-ID: 550e8400-e29b-41d4-a716-446655440020
```
**Access log** (`logs/access.log`):
```json
{"timestamp":"2025-11-10T15:45:00Z","level":"info","method":"GET","path":"/api/v1/users","status":200,"duration_ms":15.234,"request_id":"550e8400-e29b-41d4-a716-446655440020","ip":"127.0.0.1","user_agent":"curl/7.88.1","user_id":"user-789"}
```
**Application log** (`logs/app.log`) - if handler logs something:
```json
{"timestamp":"2025-11-10T15:45:00Z","level":"info","message":"Fetching users","request_id":"550e8400-e29b-41d4-a716-446655440020","user_id":"user-789"}
```
### Search Logs by Request ID
```bash
# Search access logs
grep "550e8400-e29b-41d4-a716-446655440020" logs/access.log
# Search application logs
grep "550e8400-e29b-41d4-a716-446655440020" logs/app.log
```
---
## Log Rotation Testing
### Verify Log Rotation Settings
**Application log rotation** (100MB max, 30 day retention):
```bash
ls -lh logs/app.log*
# Should show app.log and rotated files (app.log.1, app.log.2, etc.)
```
**Access log rotation** (500MB max, 90 day retention):
```bash
ls -lh logs/access.log*
# Should show access.log and rotated files
```
### Trigger Log Rotation (Manual Test)
Generate large number of log entries:
```bash
# Generate 1000 requests (will create logs)
for i in {1..1000}; do
curl -s -H "token: test-token-abc123" http://localhost:3000/api/v1/users > /dev/null
done
```
**Check log file sizes**:
```bash
du -h logs/
```
**Note**: Rotation happens automatically when size limit is reached. Old files are compressed (`.gz`) if compression is enabled.
---
## Troubleshooting
### Problem: Server won't start
**Check**:
1. Port 3000 is not already in use: `lsof -i :3000`
2. Configuration file is valid YAML: `cat configs/config.yaml`
3. Logs directory exists: `ls -ld logs/`
### Problem: Redis connection fails
**Check**:
1. Redis is running: `redis-cli ping`
2. Redis address in config is correct: `localhost:6379`
3. Redis authentication (if password is set)
### Problem: Token validation always fails
**Check**:
1. Token exists in Redis: `redis-cli GET "auth:token:your-token"`
2. Token hasn't expired (check TTL): `redis-cli TTL "auth:token:your-token"`
3. Token key format is correct: `auth:token:{token_string}`
### Problem: Logs not appearing
**Check**:
1. Log directory has write permissions: `ls -ld logs/`
2. Log level in config: `info` or `debug`
3. Logger is properly initialized (check server startup logs)
### Problem: Configuration hot reload not working
**Check**:
1. Configuration file path is correct
2. File system notifications are working (fsnotify)
3. Server logs show config change events
4. New configuration is valid (invalid config is rejected)
---
## Development Workflow
### 1. Code Changes
Make changes to middleware or configuration code.
### 2. Run Tests
```bash
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run specific test
go test -v ./internal/middleware -run TestKeyAuth
```
### 3. Format Code
```bash
# Format all code
go fmt ./...
# Check formatting
gofmt -l .
```
### 4. Static Analysis
```bash
# Run go vet
go vet ./...
# Run golangci-lint (if installed)
golangci-lint run
```
### 5. Build and Test
```bash
# Build
go build -o bin/api cmd/api/main.go
# Test binary
./bin/api
```
---
## Environment-Specific Configurations
### Development (`configs/config.dev.yaml`)
```yaml
logging:
level: "debug" # More verbose logging
development: true # Pretty-printed logs (non-JSON)
middleware:
enable_auth: false # Optional: disable auth for easier testing
```
**Usage**:
```bash
export CONFIG_ENV=dev
go run cmd/api/main.go
```
### Staging (`configs/config.staging.yaml`)
```yaml
server:
address: ":8080"
redis:
address: "redis-staging.example.com:6379"
password: "staging-password"
logging:
level: "info"
middleware:
enable_rate_limiter: true
rate_limiter:
max: 1000
expiration: "1m"
```
### Production (`configs/config.prod.yaml`)
```yaml
server:
address: ":8080"
prefork: true # Multi-process mode for performance
redis:
address: "redis-prod.example.com:6379"
password: "prod-password"
pool_size: 50 # Larger pool for production
logging:
level: "warn" # Less verbose
development: false
middleware:
enable_rate_limiter: true
rate_limiter:
max: 5000
expiration: "1m"
storage: "redis" # Distributed rate limiting
```
---
## Next Steps
After verifying the middleware integration works:
1. **Run `/speckit.tasks`**: Generate implementation tasks
2. **Run `/speckit.implement`**: Execute implementation
3. **Write tests**: Unit and integration tests for all middleware
4. **Update documentation**: Add API endpoint examples
5. **Deploy to staging**: Test in staging environment
---
## Quick Reference
### Redis Commands
```bash
# Set token (expires in 1 hour)
redis-cli SETEX "auth:token:TOKEN" 3600 "USER_ID"
# Get token
redis-cli GET "auth:token:TOKEN"
# Check TTL
redis-cli TTL "auth:token:TOKEN"
# Delete token
redis-cli DEL "auth:token:TOKEN"
# List all tokens (careful in production!)
redis-cli KEYS "auth:token:*"
```
### Curl Examples
```bash
# Health check
curl http://localhost:3000/health
# With token
curl -H "token: TOKEN" http://localhost:3000/api/v1/users
# POST request
curl -X POST \
-H "token: TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"John","email":"john@example.com"}' \
http://localhost:3000/api/v1/users
# Show response headers
curl -i -H "token: TOKEN" http://localhost:3000/api/v1/users
```
### Log Tailing
```bash
# Tail application logs
tail -f logs/app.log | jq .
# Tail access logs
tail -f logs/access.log | jq .
# Filter by request ID
tail -f logs/app.log | grep "REQUEST_ID"
```
---
**Status**: Ready for implementation
**Next**: Run `/speckit.tasks` to generate implementation task list