diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..f2ff35c77 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,65 @@ +# syntax=docker/dockerfile:1.4 + +ARG NODE_VERSION=22 + +FROM node:${NODE_VERSION}-slim + +# Install necessary system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + curl \ + gnupg \ + openssh-client \ + bash-completion \ + # Playwright browser dependencies + libnss3 \ + libnspr4 \ + libatk1.0-0 \ + libatk-bridge2.0-0 \ + libcups2 \ + libdrm2 \ + libxkbcommon0 \ + libxcomposite1 \ + libxdamage1 \ + libxfixes3 \ + libxrandr2 \ + libgbm1 \ + libasound2 \ + libpango-1.0-0 \ + libcairo2 \ + # PostgreSQL client for CLI debugging + postgresql-client \ + && rm -rf /var/lib/apt/lists/* + +# Install pnpm via npm (with fallback to Taobao mirror for network issues) +ARG PNPM_VERSION=10.17.1 +RUN npm install -g pnpm@${PNPM_VERSION} || \ + npm install -g pnpm@${PNPM_VERSION} --registry=https://registry.npmmirror.com + +# Configure pnpm +ENV PNPM_HOME=/pnpm +ENV PATH=$PNPM_HOME:$PATH +RUN mkdir -p /pnpm/store && \ + pnpm config set store-dir /pnpm/store + +# Set working directory +WORKDIR /workspace + +# Configure Git safe directory to avoid permission warnings +RUN git config --global --add safe.directory /workspace + +# Pre-create directories for volumes and set correct permissions +# This ensures the node user can write to these directories +RUN mkdir -p /workspace/node_modules /workspace/.turbo && \ + chown -R node:node /workspace /pnpm + +# Set up shell configuration for node user +USER node +RUN echo 'alias ll="ls -alh"' >> ~/.bashrc && \ + echo 'alias pn="pnpm"' >> ~/.bashrc && \ + echo 'export PS1="\[\e[32m\]\u@nomad-dev\[\e[m\]:\[\e[34m\]\w\[\e[m\]\$ "' >> ~/.bashrc + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD node --version && pnpm --version || exit 1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..9a81754b5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,130 @@ +{ + "name": "Nomad OTA Platform", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspace", + + "features": { + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "biomejs.biome", + "editorconfig.editorconfig", + "dsznajder.es7-react-js-snippets", + "bradlc.vscode-tailwindcss", + "mtxr.sqltools", + "mtxr.sqltools-driver-pg", + "Playwright.playwright", + "ZixuanChen.vitest-explorer", + "eamodio.gitlens", + "usernamehw.errorlens", + "streetsidesoftware.code-spell-checker", + "DavidAnson.vscode-markdownlint", + "ms-azuretools.vscode-docker" + ], + + "settings": { + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true, + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/node_modules/**": true, + "**/.next/**": true, + "**/.turbo/**": true, + "**/dist/**": true + }, + "search.exclude": { + "**/node_modules": true, + "**/.next": true, + "**/.turbo": true, + "**/dist": true, + "pnpm-lock.yaml": true + }, + "sqltools.connections": [ + { + "name": "Nomad Development DB", + "driver": "PostgreSQL", + "previewLimit": 50, + "server": "postgres", + "port": 5432, + "database": "${containerEnv:POSTGRES_DB:nomad_dev}", + "username": "${containerEnv:POSTGRES_USER:nomad}", + "password": "${containerEnv:POSTGRES_PASSWORD:nomad_dev_password}" + }, + { + "name": "Nomad Test DB", + "driver": "PostgreSQL", + "previewLimit": 50, + "server": "postgres", + "port": 5432, + "database": "nomad_test", + "username": "${containerEnv:POSTGRES_USER:nomad}", + "password": "${containerEnv:POSTGRES_PASSWORD:nomad_dev_password}" + } + ] + } + } + }, + + "forwardPorts": [3000, 3001, 3002, 5432, 6006, 6007], + + "portsAttributes": { + "3000": { + "label": "Web App", + "onAutoForward": "notify" + }, + "3001": { + "label": "Documentation", + "onAutoForward": "silent" + }, + "3002": { + "label": "Demo (Remotion)", + "onAutoForward": "silent" + }, + "5432": { + "label": "PostgreSQL", + "onAutoForward": "silent" + }, + "6006": { + "label": "Storybook", + "onAutoForward": "silent" + }, + "6007": { + "label": "React Email", + "onAutoForward": "silent" + } + }, + + "postCreateCommand": "bash .devcontainer/init-scripts/postCreateCommand.sh", + "postStartCommand": "bash .devcontainer/init-scripts/postStartCommand.sh", + + "remoteUser": "node", + + "containerEnv": { + "NODE_ENV": "development" + }, + + "mounts": [ + "source=nomad-pnpm-store,target=/pnpm/store,type=volume", + "source=nomad-node-modules,target=${containerWorkspaceFolder}/node_modules,type=volume" + ] +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 000000000..7e48de04b --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,80 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + NODE_VERSION: 22 + PNPM_VERSION: 10.17.1 + + volumes: + - ..:/workspace:cached + - nomad-pnpm-store:/pnpm/store + - nomad-node-modules:/workspace/node_modules + - nomad-turbo-cache:/workspace/.turbo + + command: sleep infinity + + environment: + - NODE_ENV=development + - DATABASE_URL=postgresql://${POSTGRES_USER:-nomad}:${POSTGRES_PASSWORD:-nomad_dev_password}@postgres:5432/${POSTGRES_DB:-nomad_dev} + - DATABASE_URL_TEST=postgresql://${POSTGRES_USER:-nomad}:${POSTGRES_PASSWORD:-nomad_dev_password}@postgres:5432/nomad_test + - BETTER_AUTH_URL=http://localhost:3000 + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-dev_secret_key_change_in_production_32_chars_minimum} + - LOG_LEVEL=${LOG_LEVEL:-debug} + - ENABLE_ALIYUN_SMS=${ENABLE_ALIYUN_SMS:-disabled} + - ENABLE_RESEND=${ENABLE_RESEND:-disabled} + - NEXT_PUBLIC_TURNSTILE_SITE_KEY=1x00000000000000000000AA + - TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA + - RESEND_FROM_EMAIL=onboarding@resend.dev + - DATABASE_SSL=false + + depends_on: + postgres: + condition: service_healthy + + networks: + - nomad-network + + user: node + + postgres: + image: postgres:15-alpine + restart: unless-stopped + + volumes: + - nomad-postgres-data:/var/lib/postgresql/data + - ./init-scripts/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro + + environment: + POSTGRES_USER: ${POSTGRES_USER:-nomad} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-nomad_dev_password} + POSTGRES_DB: ${POSTGRES_DB:-nomad_dev} + POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C" + + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-nomad} -d ${POSTGRES_DB:-nomad_dev}"] + interval: 10s + timeout: 5s + retries: 5 + + networks: + - nomad-network + + ports: + - "5432:5432" + +volumes: + nomad-pnpm-store: + name: nomad-pnpm-store + nomad-node-modules: + name: nomad-node-modules + nomad-turbo-cache: + name: nomad-turbo-cache + nomad-postgres-data: + name: nomad-postgres-data + +networks: + nomad-network: + name: nomad-network + driver: bridge diff --git a/.devcontainer/init-scripts/init-db.sh b/.devcontainer/init-scripts/init-db.sh new file mode 100644 index 000000000..73f66e469 --- /dev/null +++ b/.devcontainer/init-scripts/init-db.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +# Create test database +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE DATABASE nomad_test; + GRANT ALL PRIVILEGES ON DATABASE nomad_test TO $POSTGRES_USER; +EOSQL + +echo "Test database 'nomad_test' created successfully" diff --git a/.devcontainer/init-scripts/postCreateCommand.sh b/.devcontainer/init-scripts/postCreateCommand.sh new file mode 100644 index 000000000..1be9ceb9a --- /dev/null +++ b/.devcontainer/init-scripts/postCreateCommand.sh @@ -0,0 +1,106 @@ +#!/bin/bash +set -e + +echo "Initializing Nomad development environment..." + +# Display environment information +echo "Node.js version: $(node --version)" +echo "pnpm version: $(pnpm --version)" + +# ============================================================================ +# CI Environment Detection +# ============================================================================ +# In CI environments (like GitHub Actions), we completely skip postCreateCommand +# to avoid timeout issues during container startup. The devcontainers/ci tool +# expects postCreateCommand to complete quickly, but even a basic "pnpm install" +# can take several minutes on first run. +# +# Solution: +# - In CI: postCreateCommand does nothing (instant return) +# - All setup steps (including dependency installation) are handled explicitly +# in the GitHub workflow's runCmd section for better control and visibility +# +# Benefits: +# - Reliable container startup in CI (no timeouts) +# - Complete visibility of all validation steps in workflow logs +# - Fine-grained control over each step's execution +# ============================================================================ +if [ "$CI" = "true" ]; then + echo "CI environment detected - Skipping postCreateCommand" + echo "All setup steps will be handled by the CI workflow's runCmd" + exit 0 +fi + +# ============================================================================ +# Full Development Environment Setup (Local Development Only) +# ============================================================================ +# The following steps are only executed in local development environments +# to provide a complete, ready-to-use setup. +# ============================================================================ + +# Install dependencies +echo "Installing dependencies..." +pnpm install --frozen-lockfile + +# Wait for database to be ready +echo "Waiting for PostgreSQL to be ready..." +until pg_isready -h postgres -U nomad -d nomad_dev > /dev/null 2>&1; do + echo "Waiting for database..." + sleep 2 +done +echo "Database is ready!" + +# Initialize development database +echo "Initializing development database..." +cd /workspace/apps/web +pnpm db:push + +# Seed development database (optional) +echo "Seeding development database..." +pnpm db:seed || echo "Warning: Seeding failed or skipped" + +# Initialize test database +echo "Initializing test database..." +export NODE_ENV=test +export DATABASE_URL=postgresql://nomad:nomad_dev_password@postgres:5432/nomad_test +pnpm db:push || echo "Warning: Test DB initialization failed" + +# Install Playwright browsers +echo "Installing Playwright browsers..." +cd /workspace/apps/web +pnpm exec playwright install chromium --with-deps || echo "Warning: Playwright installation failed" + +# Create local environment variable file if it doesn't exist +cd /workspace/apps/web +if [ ! -f .env.local ]; then + echo "Creating .env.local for development..." + cat > .env.local << 'EOF' +# Local development overrides +# This file is automatically created by devcontainer + +# Database (managed by Docker Compose) +DATABASE_URL=postgresql://nomad:nomad_dev_password@postgres:5432/nomad_dev + +# Auth (development defaults) +BETTER_AUTH_SECRET=dev_secret_key_change_in_production_32_chars_minimum +BETTER_AUTH_URL=http://localhost:3000 + +# Features (disabled by default in dev) +ENABLE_ALIYUN_SMS=disabled +ENABLE_RESEND=disabled + +# Logging +LOG_LEVEL=debug +EOF +fi + +# Return to workspace root +cd /workspace + +echo "" +echo "Setup complete! You can now:" +echo " - Run 'pnpm dev' to start all applications" +echo " - Run 'pnpm web:dev' to start only the web app" +echo " - Run 'pnpm web:test' to run tests" +echo " - Open http://localhost:3000 to view the app" +echo "" diff --git a/.devcontainer/init-scripts/postStartCommand.sh b/.devcontainer/init-scripts/postStartCommand.sh new file mode 100644 index 000000000..77fdd00bd --- /dev/null +++ b/.devcontainer/init-scripts/postStartCommand.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +echo "Container started, running post-start checks..." + +# Check database connection +echo "Checking database connection..." +if pg_isready -h postgres -U nomad -d nomad_dev > /dev/null 2>&1; then + echo "Database connection OK" +else + echo "Warning: Database not ready, some features may not work" +fi + +# Display quick start guide +echo "" +echo "Quick Start Commands:" +echo " pnpm dev - Start all apps" +echo " pnpm web:dev - Start web app only" +echo " pnpm web:db:studio - Open Drizzle Studio" +echo " pnpm web:test - Run tests" +echo "" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..bdc780ac2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,18 @@ +# Git Attributes Configuration for Nomad Project + +# Ensure shell scripts use Unix line endings (LF) for Docker containers +# This is critical for DevContainer scripts which run in Linux containers +*.sh text eol=lf + +# Ensure consistent line endings for common text files +*.ts text +*.tsx text +*.js text +*.jsx text +*.json text +*.md text +*.yml text +*.yaml text + +# Ensure package manager lockfiles are not modified +pnpm-lock.yaml text eol=lf diff --git a/.nvmrc b/.nvmrc index f5b3ef39f..8fdd954df 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.21.0 \ No newline at end of file +22 \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..851c3e4f7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,19 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "biomejs.biome", + "editorconfig.editorconfig", + "dsznajder.es7-react-js-snippets", + "bradlc.vscode-tailwindcss", + "mtxr.sqltools", + "mtxr.sqltools-driver-pg", + "Playwright.playwright", + "ZixuanChen.vitest-explorer", + "eamodio.gitlens", + "usernamehw.errorlens", + "streetsidesoftware.code-spell-checker", + "DavidAnson.vscode-markdownlint", + "ms-azuretools.vscode-docker" + ] +} diff --git a/DEVCONTAINER.md b/DEVCONTAINER.md new file mode 100644 index 000000000..7976a47fb --- /dev/null +++ b/DEVCONTAINER.md @@ -0,0 +1,485 @@ +# Nomad 项目 DevContainer 使用指南 + +本文档介绍如何使用 VSCode DevContainer 快速搭建 Nomad 项目的开发环境。 + +## 什么是 DevContainer? + +DevContainer(开发容器)是一种基于 Docker 的开发环境配置方案,它可以: + +- 统一团队的开发环境,避免"在我机器上可以运行"的问题 +- 自动安装所需的 Node.js、pnpm、PostgreSQL 等依赖 +- 隔离开发环境,不影响本地系统 +- 支持 Windows、macOS、Linux 跨平台使用 + +## 前置要求 + +### 必需软件 + +1. **Docker Desktop** 或 **Docker Engine** + - Windows/macOS: [Docker Desktop](https://www.docker.com/products/docker-desktop) + - Linux: Docker Engine + Docker Compose + +2. **Visual Studio Code** + - [下载 VSCode](https://code.visualstudio.com/) + +3. **Dev Containers 扩展** + - 在 VSCode 中搜索并安装 "Dev Containers" 扩展 + - 或运行命令: `code --install-extension ms-vscode-remote.remote-containers` + +### 系统要求 + +- **磁盘空间**: 至少 10GB 可用空间(用于 Docker 镜像和依赖) +- **内存**: 建议 8GB 及以上 +- **CPU**: 支持虚拟化(Windows 需启用 WSL2) + +## 快速开始 + +### 首次使用 + +1. **克隆仓库** + + ```bash + git clone https://github.com/ukeSJTU/nomad.git + cd nomad + ``` + +2. **(可选)配置环境变量** + + 如果需要测试外部服务(短信、邮件、OAuth 等),可以配置自定义环境变量: + + ```bash + cp .devcontainer/.env.devcontainer.example .devcontainer/.env.devcontainer + # 编辑 .env.devcontainer,添加你的 API 密钥 + ``` + +3. **在容器中打开项目** + + 在 VSCode 中打开项目文件夹,然后: + - 按 `F1` 或 `Cmd/Ctrl+Shift+P` 打开命令面板 + - 输入并选择: **Dev Containers: Reopen in Container** + - 或点击左下角的绿色图标,选择 "Reopen in Container" + +4. **等待初始化完成** + + 首次构建和初始化需要 10-15 分钟,过程包括: + - 构建 Docker 镜像(Node.js + 工具链) + - 启动 PostgreSQL 数据库 + - 安装项目依赖(pnpm install) + - 初始化数据库 schema + - 填充测试数据 + - 安装 Playwright 浏览器 + + 你可以在 VSCode 终端中查看进度。 + +5. **开始开发** + + 初始化完成后,运行: + + ```bash + pnpm dev + ``` + + 然后在浏览器中访问 [http://localhost:3000](http://localhost:3000) + +### 后续使用 + +容器配置完成后,每次使用只需: + +1. 打开 VSCode +2. 点击左下角绿色图标 → "Reopen in Container" +3. 等待约 30-60 秒容器启动 +4. 运行 `pnpm dev` 开始开发 + +## 常用命令 + +### 开发服务器 + +```bash +# 启动所有应用(web + docs + demo) +pnpm dev + +# 只启动 Web 应用(推荐) +pnpm web:dev + +# 启动文档站点 +pnpm docs:dev + +# 启动 Storybook(UI 组件库) +pnpm web:storybook +``` + +### 数据库管理 + +```bash +# 打开 Drizzle Studio(可视化数据库管理) +pnpm web:db:studio + +# 推送 schema 更改到数据库 +pnpm web:db:push + +# 重新填充测试数据 +pnpm web:db:seed + +# 使用 psql 连接数据库 +psql -h postgres -U nomad -d nomad_dev +``` + +### 测试 + +```bash +# 运行所有测试 +pnpm web:test + +# 运行单元测试 +pnpm web:test:unit + +# 运行集成测试 +pnpm web:test:integration + +# E2E 测试 +pnpm web:e2e + +# E2E 测试(带 UI) +pnpm web:e2e:ui +``` + +### 代码质量 + +```bash +# 代码检查 +pnpm lint + +# 自动修复代码问题 +pnpm lint:fix + +# 格式化代码 +pnpm format + +# TypeScript 类型检查 +pnpm type-check +``` + +## 容器配置说明 + +### 端口转发 + +以下端口会自动转发到本地: + +- **3000**: Web 应用(主应用) +- **3001**: 文档站点 +- **3002**: Demo 应用(Remotion) +- **5432**: PostgreSQL 数据库 +- **6006**: Storybook +- **6007**: React Email 预览 + +### 环境变量 + +环境变量的优先级(从高到低): + +1. `.devcontainer/.env.devcontainer`(你的自定义配置,不会提交到 Git) +2. `docker-compose.yml` 中的默认值 +3. `apps/web/.env.local`(容器自动创建) +4. `apps/web/.env`(项目默认配置) + +### 默认配置 + +开箱即用的默认配置: + +- **数据库**: PostgreSQL 15,用户名 `nomad`,密码 `nomad_dev_password` +- **外部服务**: 阿里云短信和 Resend 邮件服务默认**禁用** +- **日志级别**: `debug`(开发模式) +- **认证密钥**: 开发环境默认密钥(生产环境需更换) + +### 启用外部服务 + +如果需要测试短信或邮件功能: + +1. 创建 `.devcontainer/.env.devcontainer` 文件: + + ```bash + cp .devcontainer/.env.devcontainer.example .devcontainer/.env.devcontainer + ``` + +2. 编辑文件,添加你的 API 密钥: + + ```bash + # 启用阿里云短信 + ENABLE_ALIYUN_SMS=enabled + ALIBABA_CLOUD_ACCESS_KEY_ID=your_key_id + ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_secret + ALIBABA_CLOUD_SMS_SIGN_NAME=YourAppName + ALIBABA_CLOUD_SMS_TEMPLATE_CODE=SMS_123456789 + + # 启用 Resend 邮件 + ENABLE_RESEND=enabled + RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx + RESEND_FROM_EMAIL=noreply@yourdomain.com + ``` + +3. 重建容器: + - 按 `F1` → **Dev Containers: Rebuild Container** + +## VSCode 扩展 + +容器会自动安装以下推荐扩展: + +- **Biome**: 代码格式化和检查(替代 ESLint + Prettier) +- **Playwright**: E2E 测试支持 +- **Vitest Explorer**: 单元测试运行器 +- **SQLTools**: 数据库管理工具 +- **Tailwind CSS IntelliSense**: Tailwind 自动补全 +- **GitLens**: Git 增强工具 +- **Error Lens**: 行内错误提示 + +### 使用 SQLTools 连接数据库 + +SQLTools 已预配置好数据库连接: + +1. 点击左侧活动栏的 SQLTools 图标 +2. 选择 "Nomad Development DB" 或 "Nomad Test DB" +3. 点击连接 +4. 即可查询和管理数据库 + +## 常见问题 + +### 容器构建失败 + +**症状**: Docker 构建时报错 + +**解决方案**: + +```bash +# 清理 Docker 缓存 +docker system prune -a + +# 重新构建容器 +# 在 VSCode 中: F1 → Dev Containers: Rebuild Container (Without Cache) +``` + +### 端口被占用 + +**症状**: "Port 3000 is already in use" + +**解决方案**: + +```bash +# macOS/Linux +lsof -ti:3000 | xargs kill + +# Windows +netstat -ano | findstr :3000 +taskkill /PID /F +``` + +### 数据库连接失败 + +**症状**: "ECONNREFUSED postgres:5432" + +**解决方案**: + +```bash +# 检查 PostgreSQL 容器状态 +docker ps + +# 查看数据库日志 +docker logs nomad-postgres + +# 重启数据库容器 +docker-compose -f .devcontainer/docker-compose.yml restart postgres +``` + +### pnpm install 失败 + +**症状**: 依赖安装失败或超时 + +**解决方案**: + +```bash +# 清理缓存并重新安装 +pnpm clean +pnpm install --frozen-lockfile + +# 如果仍然失败,尝试不使用 frozen-lockfile +pnpm install +``` + +### Playwright 浏览器缺失 + +**症状**: E2E 测试失败,提示浏览器未安装 + +**解决方案**: + +```bash +cd apps/web +pnpm exec playwright install chromium --with-deps +``` + +### Windows 行尾符问题 + +**症状**: 脚本执行失败,提示 `/bin/bash^M: bad interpreter` + +**解决方案**: + +项目已通过 `.gitattributes` 配置自动处理,但如果仍遇到问题: + +```bash +# 重新克隆仓库,确保 Git 自动转换行尾符 +git clone https://github.com/ukeSJTU/nomad.git +``` + +### 容器性能慢(Windows/macOS) + +**症状**: 文件读写和命令执行明显变慢 + +**优化方案**: + +1. 确保 Docker Desktop 分配了足够的资源(设置 → Resources) +2. 项目已使用命名卷缓存 `node_modules` 和 `pnpm store` +3. 避免在容器内编辑大文件(推荐在宿主机编辑,容器内执行) + +## 重建和清理 + +### 重建容器 + +如果遇到配置问题或需要更新容器: + +```bash +# 在 VSCode 中 +F1 → Dev Containers: Rebuild Container + +# 完全清理后重建(不保留缓存) +F1 → Dev Containers: Rebuild Container (Without Cache) +``` + +### 清理数据卷 + +如果需要完全重置环境(会删除所有数据): + +```bash +# 停止并删除容器 +docker-compose -f .devcontainer/docker-compose.yml down + +# 删除命名卷 +docker volume rm nomad-pnpm-store +docker volume rm nomad-node-modules +docker volume rm nomad-turbo-cache +docker volume rm nomad-postgres-data + +# 重新打开容器 +# VSCode: F1 → Dev Containers: Reopen in Container +``` + +## 性能优化建议 + +### 1. 限制文件监视范围 + +VSCode 设置已默认排除以下目录: + +- `node_modules` +- `.next` +- `.turbo` +- `dist` + +### 2. 使用 Turbo 缓存 + +项目已配置 Turbo 缓存卷,重复构建速度更快: + +```bash +# 查看缓存使用情况 +pnpm turbo:graph +``` + +### 3. 按需启动应用 + +不需要所有应用时,只启动 Web 应用: + +```bash +pnpm web:dev # 而不是 pnpm dev +``` + +### 4. 定期清理 Docker + +```bash +# 清理未使用的镜像和容器 +docker system prune + +# 查看磁盘占用 +docker system df +``` + +## 架构说明 + +### 为什么使用 Docker Compose? + +- **服务分离**: 应用容器和数据库容器独立管理 +- **健康检查**: 确保数据库就绪后才启动应用 +- **数据持久化**: PostgreSQL 数据不会随容器删除 +- **易于扩展**: 未来可添加 Redis、MinIO 等服务 + +### 为什么使用命名卷? + +- **pnpm-store**: 避免重复下载依赖包 +- **node_modules**: 跨容器重建保留已安装模块 +- **turbo-cache**: 持久化 Turborepo 构建缓存 +- **postgres-data**: 数据库数据持久化 + +**性能提升**: + +- 首次构建: 10-15 分钟 +- 重建容器: 1-2 分钟(依赖已缓存) + +### 环境变量分层设计 + +1. **开发默认值** (docker-compose.yml): 适合大多数开发场景,无需配置即可使用 +2. **个人覆盖** (.env.devcontainer): 开发者个人配置,不提交到 Git +3. **应用层配置** (apps/web/.env.local): 自动创建,支持 Next.js 约定 + +**好处**: 开箱即用 + 灵活定制 + 安全(敏感信息不提交) + +## 与现有工具集成 + +### Husky Git Hooks + +容器内 Git hooks 自动工作: + +- Pre-commit: Biome 检查和 lint-staged +- Commit-msg: Commitlint 验证提交信息 + +### Turborepo + +完全兼容: + +- 缓存卷 `nomad-turbo-cache` 持久化构建结果 +- 所有 workspace 可见 +- 守护进程正常运行 + +### Drizzle ORM + +自动配置: + +- `DATABASE_URL` 环境变量预设 +- 支持开发和测试数据库 +- `drizzle.config.ts` 无需修改 + +### Playwright + +浏览器预装: + +- Chromium 在初始化时自动安装 +- E2E 测试可立即运行 +- 支持 headed 和 UI 模式 + +## 技术支持 + +如果遇到问题: + +1. 查看本文档的"常见问题"部分 +2. 查看容器日志: VSCode 终端中的输出 +3. 在项目 GitHub 仓库提交 Issue +4. 联系项目维护者 + +## 参考资源 + +- [VSCode Dev Containers 官方文档](https://code.visualstudio.com/docs/devcontainers/containers) +- [Docker 官方文档](https://docs.docker.com/) +- [项目贡献指南](./CONTRIBUTING.md) diff --git a/package.json b/package.json index 306d0e1d9..f6680207e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "engines": { - "node": ">=22.21.0", + "node": ">=22.0.0", "pnpm": ">=10.0.0" }, "packageManager": "pnpm@10.17.1",