diff --git a/backend/services/presence-service/deploy.sh b/backend/services/presence-service/deploy.sh new file mode 100644 index 00000000..8296dc2d --- /dev/null +++ b/backend/services/presence-service/deploy.sh @@ -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 diff --git a/backend/services/presence-service/scripts/README.md b/backend/services/presence-service/scripts/README.md index a5083fec..4f51e028 100644 --- a/backend/services/presence-service/scripts/README.md +++ b/backend/services/presence-service/scripts/README.md @@ -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 # 删除容器和镜像 +``` + +--- + ## 服务端口 | 服务 | 开发端口 | 说明 | diff --git a/backend/services/presence-service/scripts/quick-test.sh b/backend/services/presence-service/scripts/quick-test.sh new file mode 100644 index 00000000..84a10f64 --- /dev/null +++ b/backend/services/presence-service/scripts/quick-test.sh @@ -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 diff --git a/backend/services/presence-service/scripts/rebuild-kafka.sh b/backend/services/presence-service/scripts/rebuild-kafka.sh new file mode 100644 index 00000000..eefc3e83 --- /dev/null +++ b/backend/services/presence-service/scripts/rebuild-kafka.sh @@ -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"