diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e72b77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,55 @@ +# ============================================================ +# EchoChat 项目全局 .gitignore +# ============================================================ + +# --- OS --- +.DS_Store +Thumbs.db + +# --- IDE --- +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# --- Go --- +backend/go-service/tmp/ +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out +*.prof + +# --- Node.js --- +node_modules/ +dist/ +.nuxt/ +.output/ + +# --- 环境配置 --- +.env +.env.local +.env.*.local +*.local + +# --- 日志 --- +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# --- Docker 数据卷(本地持久化目录) --- +deploy/data/ + +# --- 编译产物 --- +build/ +*.tmp + +# --- 测试覆盖率 --- +coverage/ +*.coverprofile diff --git a/deploy/docker-compose.dev.yml b/deploy/docker-compose.dev.yml new file mode 100644 index 0000000..ee0e8b8 --- /dev/null +++ b/deploy/docker-compose.dev.yml @@ -0,0 +1,39 @@ +version: '3.8' + +services: + postgres: + image: postgres:17 + container_name: echochat-postgres + environment: + POSTGRES_DB: echochat + POSTGRES_USER: echochat + POSTGRES_PASSWORD: echochat_dev_2026 + ports: + - "5432:5432" + volumes: + - pgdata:/var/lib/postgresql/data + - ./docker/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U echochat"] + interval: 5s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + container_name: echochat-redis + ports: + - "6379:6379" + volumes: + - redisdata:/data + - ./docker/redis/redis.conf:/usr/local/etc/redis/redis.conf + command: redis-server /usr/local/etc/redis/redis.conf + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + pgdata: + redisdata: diff --git a/deploy/docker/postgres/init.sql b/deploy/docker/postgres/init.sql new file mode 100644 index 0000000..3b3e98b --- /dev/null +++ b/deploy/docker/postgres/init.sql @@ -0,0 +1,91 @@ +-- ============================================================ +-- EchoChat 数据库初始化脚本 +-- 第一阶段:auth 模块表结构 +-- ============================================================ + +-- ============================================================ +-- auth_users: 用户主表 +-- 存储系统所有用户(包括普通用户和管理员),通过角色表区分权限 +-- ============================================================ +CREATE TABLE auth_users ( + id BIGSERIAL PRIMARY KEY, + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + nickname VARCHAR(50) NOT NULL DEFAULT '', + avatar VARCHAR(500) NOT NULL DEFAULT '', + gender SMALLINT NOT NULL DEFAULT 0, + phone VARCHAR(20) DEFAULT NULL, + status SMALLINT NOT NULL DEFAULT 1, + last_login_at TIMESTAMP DEFAULT NULL, + last_login_ip VARCHAR(50) DEFAULT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +COMMENT ON TABLE auth_users IS '用户主表,存储所有用户信息(普通用户与管理员共用)'; +COMMENT ON COLUMN auth_users.id IS '用户唯一标识,自增主键'; +COMMENT ON COLUMN auth_users.username IS '用户名,全局唯一,用于登录'; +COMMENT ON COLUMN auth_users.email IS '邮箱地址,全局唯一,用于登录和通知'; +COMMENT ON COLUMN auth_users.password_hash IS '密码哈希值,使用 bcrypt 加密存储'; +COMMENT ON COLUMN auth_users.nickname IS '用户昵称,用于前端显示'; +COMMENT ON COLUMN auth_users.avatar IS '头像 URL 地址'; +COMMENT ON COLUMN auth_users.gender IS '性别:0=未知,1=男,2=女'; +COMMENT ON COLUMN auth_users.phone IS '手机号码,可选'; +COMMENT ON COLUMN auth_users.status IS '账号状态:1=正常,2=禁用(管理员封禁),3=注销(用户主动注销)'; +COMMENT ON COLUMN auth_users.last_login_at IS '最后一次登录时间'; +COMMENT ON COLUMN auth_users.last_login_ip IS '最后一次登录 IP 地址'; +COMMENT ON COLUMN auth_users.created_at IS '账号创建时间'; +COMMENT ON COLUMN auth_users.updated_at IS '信息最后更新时间'; + +-- ============================================================ +-- auth_roles: 角色表 +-- 系统预置角色,用于 RBAC 权限控制 +-- ============================================================ +CREATE TABLE auth_roles ( + id SERIAL PRIMARY KEY, + code VARCHAR(50) UNIQUE NOT NULL, + name VARCHAR(50) NOT NULL, + description VARCHAR(200) DEFAULT '', + created_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +COMMENT ON TABLE auth_roles IS '角色表,定义系统中所有角色类型'; +COMMENT ON COLUMN auth_roles.id IS '角色唯一标识,自增主键'; +COMMENT ON COLUMN auth_roles.code IS '角色代码,唯一标识:user=普通用户,admin=管理员,super_admin=超级管理员'; +COMMENT ON COLUMN auth_roles.name IS '角色显示名称,如"普通用户""管理员""超级管理员"'; +COMMENT ON COLUMN auth_roles.description IS '角色描述说明'; +COMMENT ON COLUMN auth_roles.created_at IS '创建时间'; + +-- 预置角色数据 +INSERT INTO auth_roles (code, name, description) VALUES + ('user', '普通用户', '系统普通用户,可以使用聊天、会议等功能'), + ('admin', '管理员', '后台管理员,可以管理用户、监控会议等'), + ('super_admin', '超级管理员', '最高权限管理员,可以管理角色和系统配置'); + +-- ============================================================ +-- auth_user_roles: 用户角色关联表 +-- 多对多关系,一个用户可拥有多个角色 +-- ============================================================ +CREATE TABLE auth_user_roles ( + user_id BIGINT NOT NULL REFERENCES auth_users(id), + role_id INT NOT NULL REFERENCES auth_roles(id), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_id, role_id) +); + +COMMENT ON TABLE auth_user_roles IS '用户角色关联表,建立用户与角色的多对多关系'; +COMMENT ON COLUMN auth_user_roles.user_id IS '关联的用户 ID'; +COMMENT ON COLUMN auth_user_roles.role_id IS '关联的角色 ID'; +COMMENT ON COLUMN auth_user_roles.created_at IS '角色分配时间'; + +-- ============================================================ +-- 插入默认超级管理员 +-- 密码: admin123456 (bcrypt hash) +-- 实际部署时应更换密码 +-- ============================================================ +INSERT INTO auth_users (username, email, password_hash, nickname, status) +VALUES ('admin', 'admin@echochat.com', '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy', '超级管理员', 1); + +INSERT INTO auth_user_roles (user_id, role_id) +VALUES (1, (SELECT id FROM auth_roles WHERE code = 'super_admin')); diff --git a/deploy/docker/redis/redis.conf b/deploy/docker/redis/redis.conf new file mode 100644 index 0000000..fd466df --- /dev/null +++ b/deploy/docker/redis/redis.conf @@ -0,0 +1,27 @@ +# EchoChat Redis 开发环境配置 + +# 绑定所有接口(开发环境,生产环境应只绑定 127.0.0.1) +bind 0.0.0.0 + +# 关闭保护模式(开发环境) +protected-mode no + +# 端口 +port 6379 + +# 持久化:每 60 秒内至少 1 个 key 变更时触发 RDB 快照 +save 60 1 + +# AOF 持久化 +appendonly yes +appendfsync everysec + +# 最大内存(开发环境 256MB 足够) +maxmemory 256mb +maxmemory-policy allkeys-lru + +# 日志级别 +loglevel notice + +# 数据库数量 +databases 16 diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh new file mode 100755 index 0000000..e145e95 --- /dev/null +++ b/scripts/dev-setup.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# EchoChat 开发环境一键搭建脚本 + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +DEPLOY_DIR="$PROJECT_ROOT/deploy" + +echo "=========================================" +echo " EchoChat 开发环境搭建" +echo "=========================================" + +# 检查 Docker 是否安装 +if ! command -v docker &> /dev/null; then + echo "❌ Docker 未安装,请先安装 Docker Desktop" + exit 1 +fi + +# 检查 Docker Compose 是否可用 +if ! docker compose version &> /dev/null; then + echo "❌ Docker Compose 不可用,请确保 Docker Desktop 已正确安装" + exit 1 +fi + +echo "✅ Docker 和 Docker Compose 已就绪" + +# 启动基础设施服务 +echo "" +echo "🚀 启动 PostgreSQL 和 Redis ..." +cd "$DEPLOY_DIR" +docker compose -f docker-compose.dev.yml up -d + +# 等待服务就绪 +echo "" +echo "⏳ 等待服务健康检查通过 ..." +sleep 3 + +# 检查 PostgreSQL +MAX_RETRIES=30 +RETRY=0 +until docker exec echochat-postgres pg_isready -U echochat > /dev/null 2>&1; do + RETRY=$((RETRY + 1)) + if [ $RETRY -ge $MAX_RETRIES ]; then + echo "❌ PostgreSQL 启动超时" + exit 1 + fi + sleep 1 +done +echo "✅ PostgreSQL 已就绪 (localhost:5432)" + +# 检查 Redis +RETRY=0 +until docker exec echochat-redis redis-cli ping > /dev/null 2>&1; do + RETRY=$((RETRY + 1)) + if [ $RETRY -ge $MAX_RETRIES ]; then + echo "❌ Redis 启动超时" + exit 1 + fi + sleep 1 +done +echo "✅ Redis 已就绪 (localhost:6379)" + +echo "" +echo "=========================================" +echo " ✅ 开发环境搭建完成!" +echo "=========================================" +echo "" +echo " PostgreSQL: localhost:5432" +echo " 数据库: echochat" +echo " 用户名: echochat" +echo " 密码: echochat_dev_2026" +echo "" +echo " Redis: localhost:6379" +echo "" +echo " 管理员账号: admin / admin123456" +echo "========================================="