feat(presence-service): 添加完整的部署脚本

- 添加 deploy.sh 主部署脚本 (build/start/stop/logs/migrate/test)
- 添加 scripts/quick-test.sh API 快速测试脚本
- 添加 scripts/rebuild-kafka.sh Kafka 重建脚本
- 更新 scripts/README.md 文档

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Developer 2025-12-02 20:16:33 -08:00
parent 603c1c6c90
commit f132b86899
4 changed files with 490 additions and 1 deletions

View File

@ -0,0 +1,204 @@
#!/bin/bash
# =============================================================================
# Presence Service - Individual Deployment Script
# =============================================================================
set -e
SERVICE_NAME="presence-service"
CONTAINER_NAME="presence-service"
IMAGE_NAME="presence-service"
PORT=3001
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Load environment
if [ -f "$SCRIPT_DIR/.env.production" ]; then
export $(cat "$SCRIPT_DIR/.env.production" | grep -v '^#' | xargs)
fi
case "$1" in
build)
log_info "Building $SERVICE_NAME..."
docker build -t "$IMAGE_NAME" "$SCRIPT_DIR"
log_success "$SERVICE_NAME built successfully"
;;
build-no-cache)
log_info "Building $SERVICE_NAME (no cache)..."
docker build --no-cache -t "$IMAGE_NAME" "$SCRIPT_DIR"
log_success "$SERVICE_NAME built successfully"
;;
start)
log_info "Starting $SERVICE_NAME..."
cd "$SCRIPT_DIR"
docker compose up -d
log_success "$SERVICE_NAME started"
;;
start-deps)
log_info "Starting dependencies only..."
cd "$SCRIPT_DIR"
docker compose -f docker-compose.dev.yml up -d
log_success "Dependencies started"
;;
stop)
log_info "Stopping $SERVICE_NAME..."
cd "$SCRIPT_DIR"
docker compose down
log_success "$SERVICE_NAME stopped"
;;
stop-deps)
log_info "Stopping dependencies..."
cd "$SCRIPT_DIR"
docker compose -f docker-compose.dev.yml down
log_success "Dependencies stopped"
;;
restart)
$0 stop
$0 start
;;
logs)
docker logs -f "$CONTAINER_NAME"
;;
logs-tail)
docker logs --tail 100 "$CONTAINER_NAME"
;;
status)
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
log_success "$SERVICE_NAME is running"
docker ps --filter "name=$CONTAINER_NAME" --format "table {{.Status}}\t{{.Ports}}"
else
log_warn "$SERVICE_NAME is not running"
fi
;;
health)
log_info "Checking health of $SERVICE_NAME..."
if curl -sf "http://localhost:$PORT/api/v1/health" > /dev/null 2>&1; then
HEALTH=$(curl -s "http://localhost:$PORT/api/v1/health")
log_success "$SERVICE_NAME is healthy"
echo "$HEALTH"
else
log_error "$SERVICE_NAME health check failed"
exit 1
fi
;;
migrate)
log_info "Running migrations for $SERVICE_NAME..."
docker exec "$CONTAINER_NAME" npx prisma migrate deploy
log_success "Migrations completed"
;;
migrate-dev)
log_info "Running dev migrations for $SERVICE_NAME..."
docker exec "$CONTAINER_NAME" npx prisma migrate dev
;;
db-push)
log_info "Pushing schema to database..."
docker exec "$CONTAINER_NAME" npx prisma db push
log_success "Schema pushed"
;;
prisma-studio)
log_info "Starting Prisma Studio..."
docker exec -it "$CONTAINER_NAME" npx prisma studio
;;
shell)
log_info "Opening shell in $SERVICE_NAME container..."
docker exec -it "$CONTAINER_NAME" sh
;;
test)
log_info "Running tests for $SERVICE_NAME..."
cd "$SCRIPT_DIR"
npm test
;;
test-unit)
log_info "Running unit tests..."
cd "$SCRIPT_DIR"
npm run test:unit
;;
test-integration)
log_info "Running integration tests..."
cd "$SCRIPT_DIR"
npm run test:integration
;;
test-e2e)
log_info "Running E2E tests..."
cd "$SCRIPT_DIR"
npm run test:e2e
;;
clean)
log_info "Cleaning up..."
cd "$SCRIPT_DIR"
docker compose down -v
docker rmi "$IMAGE_NAME" 2>/dev/null || true
log_success "Cleanup completed"
;;
*)
echo "Usage: $0 {command}"
echo ""
echo "Build Commands:"
echo " build - Build Docker image"
echo " build-no-cache - Build Docker image without cache"
echo ""
echo "Service Commands:"
echo " start - Start the full stack (service + dependencies)"
echo " start-deps - Start dependencies only (for local development)"
echo " stop - Stop all containers"
echo " stop-deps - Stop dependencies only"
echo " restart - Restart the service"
echo ""
echo "Monitoring Commands:"
echo " logs - Follow logs"
echo " logs-tail - Show last 100 log lines"
echo " status - Show service status"
echo " health - Check service health"
echo ""
echo "Database Commands:"
echo " migrate - Run database migrations (production)"
echo " migrate-dev - Run dev migrations"
echo " db-push - Push Prisma schema to database"
echo " prisma-studio - Open Prisma Studio"
echo ""
echo "Development Commands:"
echo " shell - Open shell in container"
echo " test - Run all tests"
echo " test-unit - Run unit tests only"
echo " test-integration - Run integration tests only"
echo " test-e2e - Run E2E tests only"
echo ""
echo "Maintenance Commands:"
echo " clean - Remove containers and images"
exit 1
;;
esac

View File

@ -7,6 +7,14 @@
| `start-all.sh` | 一键启动所有服务 | 开发环境初始化 |
| `stop-service.sh` | 停止 Presence Service | 重启服务、关闭开发环境 |
| `health-check.sh` | 检查所有服务健康状态 | 部署验证、故障排查 |
| `quick-test.sh` | 快速 API 功能测试 | 验证核心功能 |
| `rebuild-kafka.sh` | 重建 Kafka 容器 | Kafka 配置变更后 |
另外,项目根目录还有:
| 脚本 | 用途 | 使用场景 |
|-----|------|---------|
| `deploy.sh` | 完整部署管理脚本 | 生产部署、容器管理 |
---
@ -52,7 +60,15 @@ Failed: 0
✓ All services are healthy!
```
### 3⃣ 停止服务
### 3⃣ 快速测试
```bash
./scripts/quick-test.sh
```
验证核心 API 功能是否正常工作。
### 4⃣ 停止服务
```bash
./scripts/stop-service.sh
@ -60,6 +76,46 @@ Failed: 0
---
## deploy.sh 使用指南
`deploy.sh` 是一个功能完整的部署管理脚本,支持以下命令:
```bash
# 构建 Docker 镜像
./deploy.sh build
./deploy.sh build-no-cache
# 启动/停止服务
./deploy.sh start # 启动完整环境
./deploy.sh start-deps # 只启动依赖(开发模式)
./deploy.sh stop # 停止所有
./deploy.sh restart # 重启
# 监控
./deploy.sh logs # 实时日志
./deploy.sh logs-tail # 最近 100 行日志
./deploy.sh status # 服务状态
./deploy.sh health # 健康检查
# 数据库
./deploy.sh migrate # 生产环境迁移
./deploy.sh migrate-dev # 开发环境迁移
./deploy.sh db-push # 推送 Schema
./deploy.sh prisma-studio # 打开 Prisma Studio
# 开发
./deploy.sh shell # 进入容器 Shell
./deploy.sh test # 运行所有测试
./deploy.sh test-unit # 单元测试
./deploy.sh test-integration # 集成测试
./deploy.sh test-e2e # E2E 测试
# 清理
./deploy.sh clean # 删除容器和镜像
```
---
## 服务端口
| 服务 | 开发端口 | 说明 |

View File

@ -0,0 +1,140 @@
#!/bin/bash
# =============================================================================
# Presence Service - Quick Test Script
# =============================================================================
# Quickly verify core API functionality in local environment.
# =============================================================================
set -e
echo "🚀 Starting quick API tests..."
echo ""
# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
BASE_URL="http://localhost:3001/api/v1"
# Test counters
PASS=0
FAIL=0
# Test function
test_api() {
local test_name=$1
local method=$2
local endpoint=$3
local data=$4
local expected_status=$5
local token=$6
echo -n "Testing: $test_name ... "
if [ -n "$token" ]; then
response=$(curl -s -w "\n%{http_code}" -X $method "$BASE_URL$endpoint" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $token" \
-d "$data" 2>/dev/null)
elif [ -n "$data" ]; then
response=$(curl -s -w "\n%{http_code}" -X $method "$BASE_URL$endpoint" \
-H "Content-Type: application/json" \
-d "$data" 2>/dev/null)
else
response=$(curl -s -w "\n%{http_code}" -X $method "$BASE_URL$endpoint" \
-H "Content-Type: application/json" 2>/dev/null)
fi
status=$(echo "$response" | tail -n1)
body=$(echo "$response" | head -n-1)
if [ "$status" -eq "$expected_status" ]; then
echo -e "${GREEN}✓ PASS${NC}"
PASS=$((PASS + 1))
if [ -n "$body" ]; then
echo "$body" | head -c 200
echo ""
fi
else
echo -e "${RED}✗ FAIL${NC} (Expected: $expected_status, Got: $status)"
FAIL=$((FAIL + 1))
echo "$body"
fi
echo ""
}
# Generate test data
INSTALL_ID="test-install-$(date +%s)"
USER_ID="test-user-$(date +%s)"
# =============================================================================
# 1. Health Check
# =============================================================================
echo -e "${YELLOW}=== 1. Health Check ===${NC}"
test_api "Health endpoint" "GET" "/health" "" 200 ""
# =============================================================================
# 2. Presence API (requires auth - should return 401)
# =============================================================================
echo -e "${YELLOW}=== 2. Presence API (Auth Required) ===${NC}"
test_api "Heartbeat without auth" "POST" "/presence/heartbeat" \
"{\"installId\": \"$INSTALL_ID\"}" \
401 ""
test_api "Online count without auth" "GET" "/presence/online-count" "" 401 ""
test_api "Online history without auth" "GET" "/presence/online-history?startDate=2024-01-01&endDate=2024-01-31" "" 401 ""
# =============================================================================
# 3. Analytics API (requires auth - should return 401)
# =============================================================================
echo -e "${YELLOW}=== 3. Analytics API (Auth Required) ===${NC}"
test_api "Track event without auth" "POST" "/analytics/events" \
"{\"installId\": \"$INSTALL_ID\", \"eventName\": \"app_open\", \"eventData\": {}}" \
401 ""
test_api "DAU stats without auth" "GET" "/analytics/dau?date=2024-01-01" "" 401 ""
# =============================================================================
# 4. API with Mock Token (will fail auth validation but test routing)
# =============================================================================
echo -e "${YELLOW}=== 4. API Routing Test (Invalid Token) ===${NC}"
FAKE_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRlc3QiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
test_api "Heartbeat with invalid token" "POST" "/presence/heartbeat" \
"{\"installId\": \"$INSTALL_ID\"}" \
401 "$FAKE_TOKEN"
# =============================================================================
# 5. Error Handling
# =============================================================================
echo -e "${YELLOW}=== 5. Error Handling ===${NC}"
test_api "Non-existent endpoint" "GET" "/non-existent" "" 404 ""
test_api "Invalid JSON body" "POST" "/analytics/events" \
"invalid-json" \
400 ""
# =============================================================================
# Summary
# =============================================================================
echo ""
echo -e "${YELLOW}======================================${NC}"
echo -e "${YELLOW}Test Summary${NC}"
echo -e "${GREEN}Passed: $PASS${NC}"
echo -e "${RED}Failed: $FAIL${NC}"
echo -e "${YELLOW}======================================${NC}"
if [ $FAIL -eq 0 ]; then
echo -e "${GREEN}✓ All tests passed!${NC}"
echo ""
echo -e "${BLUE}Note:${NC} Most endpoints require authentication."
echo "For full testing, use: npm run test:e2e"
exit 0
else
echo -e "${RED}✗ Some tests failed!${NC}"
exit 1
fi

View File

@ -0,0 +1,89 @@
#!/bin/bash
# =============================================================================
# Presence Service - Rebuild Kafka Script
# =============================================================================
# Rebuilds Kafka container to apply new configuration.
# =============================================================================
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
echo -e "${YELLOW}🔄 Rebuilding Kafka containers...${NC}"
echo ""
cd "$PROJECT_DIR"
# 1. Stop Presence Service (if running)
echo -e "${BLUE}Step 1: Stop Presence Service${NC}"
PID=$(lsof -ti :3001 2>/dev/null)
if [ ! -z "$PID" ]; then
echo "Stopping Presence Service (PID: $PID)..."
kill $PID 2>/dev/null || true
sleep 2
echo -e "${GREEN}✓ Presence Service stopped${NC}"
else
echo -e "${YELLOW}⚠️ Presence Service is not running${NC}"
fi
echo ""
# 2. Stop and remove Kafka and Zookeeper containers
echo -e "${BLUE}Step 2: Stop and remove old containers${NC}"
docker compose -f docker-compose.dev.yml stop kafka zookeeper 2>/dev/null || true
docker compose -f docker-compose.dev.yml rm -f kafka zookeeper 2>/dev/null || true
echo -e "${GREEN}✓ Old containers removed${NC}"
echo ""
# 3. Recreate containers
echo -e "${BLUE}Step 3: Create new containers${NC}"
docker compose -f docker-compose.dev.yml up -d zookeeper
echo "Waiting for Zookeeper to start..."
sleep 5
docker compose -f docker-compose.dev.yml up -d kafka
echo "Waiting for Kafka to start..."
sleep 20
echo -e "${GREEN}✓ Kafka containers created${NC}"
echo ""
# 4. Verify configuration
echo -e "${BLUE}Step 4: Verify configuration${NC}"
CONTAINER_NAME=$(docker ps --filter "name=presence-kafka" --format "{{.Names}}" | head -1)
if [ -z "$CONTAINER_NAME" ]; then
echo -e "${RED}✗ Kafka container not found${NC}"
exit 1
fi
echo "Container: $CONTAINER_NAME"
# Check if Kafka is responding
if docker exec "$CONTAINER_NAME" nc -z localhost 29092 2>/dev/null; then
echo -e "${GREEN}✓ Kafka internal port (29092) is accessible${NC}"
else
echo -e "${YELLOW}⚠️ Kafka internal port not yet available${NC}"
fi
echo ""
# 5. Test external connection
echo -e "${BLUE}Step 5: Test Kafka connection${NC}"
if nc -zv localhost 9092 2>&1 | grep -q "succeeded\|Connected"; then
echo -e "${GREEN}✓ Kafka external port (9092) is accessible${NC}"
else
echo -e "${YELLOW}⚠️ Kafka external port may need more time to start${NC}"
fi
echo ""
echo -e "${YELLOW}======================================${NC}"
echo -e "${GREEN}✓ Kafka rebuild complete!${NC}"
echo -e "${YELLOW}======================================${NC}"
echo ""
echo -e "${BLUE}Next steps:${NC}"
echo "1. Start Presence Service: npm run start:dev"
echo "2. Run health check: ./scripts/health-check.sh"
echo "3. Run quick tests: ./scripts/quick-test.sh"