feat(deployment): Add Docker support and deployment configuration
- Add Dockerfile for multi-stage build with Node.js and Nginx - Create docker-compose.yml for simplified container deployment - Add .dockerignore to optimize Docker build context - Configure nginx.conf with performance and security optimizations - Update README.md and README_EN.md with Docker deployment instructions - Enhance database configuration to support optional Supabase integration - Improve environment configuration handling for local and containerized environments Enables easier deployment and provides a standardized container-based setup for the application.
This commit is contained in:
parent
9b11e47b36
commit
93bac360f1
|
|
@ -0,0 +1,46 @@
|
|||
# 依赖
|
||||
node_modules
|
||||
|
||||
# 构建产物
|
||||
dist
|
||||
.vite
|
||||
|
||||
# 环境变量(保留 .env 用于构建)
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# 日志
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# 编辑器
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# 操作系统
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# 测试
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# 文档
|
||||
*.md
|
||||
docs
|
||||
|
||||
# 其他
|
||||
.history
|
||||
.cache
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# 多阶段构建 - 构建阶段
|
||||
FROM node:18-alpine AS builder
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 禁用代理并安装 pnpm
|
||||
ENV HTTP_PROXY=""
|
||||
ENV HTTPS_PROXY=""
|
||||
ENV http_proxy=""
|
||||
ENV https_proxy=""
|
||||
ENV NO_PROXY="*"
|
||||
ENV no_proxy="*"
|
||||
|
||||
RUN npm config set registry https://registry.npmjs.org/ && \
|
||||
npm config delete proxy 2>/dev/null || true && \
|
||||
npm config delete https-proxy 2>/dev/null || true && \
|
||||
npm config delete http-proxy 2>/dev/null || true && \
|
||||
npm install -g pnpm
|
||||
|
||||
# 复制依赖文件
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# 安装依赖
|
||||
RUN pnpm install --no-frozen-lockfile
|
||||
|
||||
# 复制项目文件(包括 .env)
|
||||
COPY . .
|
||||
|
||||
# 构建应用(环境变量会在构建时被读取)
|
||||
RUN pnpm build
|
||||
|
||||
# 生产阶段 - 使用 nginx 提供静态文件服务
|
||||
FROM nginx:alpine
|
||||
|
||||
# 复制自定义 nginx 配置
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# 从构建阶段复制构建产物
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 80
|
||||
|
||||
# 启动 nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
36
README.md
36
README.md
|
|
@ -49,14 +49,43 @@
|
|||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
### 🐳 Docker 部署(推荐)
|
||||
|
||||
使用 Docker 可以快速部署应用,无需配置 Node.js 环境。
|
||||
|
||||
1. **克隆项目**
|
||||
```bash
|
||||
git clone https://github.com/lintsinghua/XCodeReviewer.git
|
||||
cd XCodeReviewer
|
||||
```
|
||||
|
||||
2. **配置环境变量**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# 编辑 .env 文件,至少需要配置 VITE_GEMINI_API_KEY
|
||||
```
|
||||
|
||||
3. **构建并启动**
|
||||
```bash
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
4. **访问应用**
|
||||
|
||||
在浏览器中打开 `http://localhost:5174`
|
||||
|
||||
### 💻 本地开发部署
|
||||
|
||||
如果需要进行开发或自定义修改,可以使用本地部署方式。
|
||||
|
||||
#### 环境要求
|
||||
|
||||
- **Node.js**: `18+`
|
||||
- **pnpm**: `8+` (推荐) 或 `npm` / `yarn`
|
||||
- **Google Gemini API Key**: 用于 AI 代码分析
|
||||
- **Supabase 项目**: 用于数据存储(可选,支持离线模式)
|
||||
|
||||
### 安装与启动
|
||||
#### 安装与启动
|
||||
|
||||
1. **克隆项目**
|
||||
```bash
|
||||
|
|
@ -256,6 +285,7 @@ XCodeReviewer/
|
|||
5. 查看详细的问题报告
|
||||
|
||||
### 构建和部署
|
||||
|
||||
```bash
|
||||
# 开发模式
|
||||
pnpm dev
|
||||
|
|
|
|||
44
README_EN.md
44
README_EN.md
|
|
@ -49,14 +49,50 @@ In the fast-paced world of software development, ensuring code quality is crucia
|
|||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Requirements
|
||||
### 🐳 Docker Deployment (Recommended)
|
||||
|
||||
Deploy quickly using Docker without Node.js environment setup.
|
||||
|
||||
1. **Clone the project**
|
||||
```bash
|
||||
git clone https://github.com/lintsinghua/XCodeReviewer.git
|
||||
cd XCodeReviewer
|
||||
```
|
||||
|
||||
2. **Configure environment variables**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env file and set at least VITE_GEMINI_API_KEY
|
||||
```
|
||||
|
||||
3. **Build and start**
|
||||
```bash
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
4. **Access the application**
|
||||
|
||||
Open `http://localhost:5174` in your browser
|
||||
|
||||
**Common commands:**
|
||||
```bash
|
||||
docker-compose logs -f # View logs
|
||||
docker-compose restart # Restart service
|
||||
docker-compose down # Stop service
|
||||
```
|
||||
|
||||
### 💻 Local Development Deployment
|
||||
|
||||
For development or custom modifications, use local deployment.
|
||||
|
||||
#### Requirements
|
||||
|
||||
- **Node.js**: `18+`
|
||||
- **pnpm**: `8+` (recommended) or `npm` / `yarn`
|
||||
- **Google Gemini API Key**: For AI code analysis
|
||||
- **Supabase Project**: For data storage (optional, supports offline mode)
|
||||
|
||||
### Installation & Setup
|
||||
#### Installation & Setup
|
||||
|
||||
1. **Clone the project**
|
||||
```bash
|
||||
|
|
@ -111,7 +147,7 @@ In the fast-paced world of software development, ensuring code quality is crucia
|
|||
```
|
||||
|
||||
5. **Access the application**
|
||||
Open `http://localhost:5173` in your browser
|
||||
Open `http://localhost:5174` in your browser
|
||||
|
||||
### 🔑 Getting API Keys
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
services:
|
||||
# XCodeReviewer 前端应用
|
||||
xcodereviewer:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: xcodereviewer-app
|
||||
ports:
|
||||
- "5174:80"
|
||||
environment:
|
||||
# Google Gemini AI 配置 (必需)
|
||||
- VITE_GEMINI_API_KEY=${VITE_GEMINI_API_KEY}
|
||||
- VITE_GEMINI_MODEL=${VITE_GEMINI_MODEL:-gemini-2.5-flash}
|
||||
- VITE_GEMINI_TIMEOUT_MS=${VITE_GEMINI_TIMEOUT_MS:-25000}
|
||||
|
||||
# Supabase 配置 (可选)
|
||||
- VITE_SUPABASE_URL=${VITE_SUPABASE_URL}
|
||||
- VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY}
|
||||
|
||||
# GitHub 集成 (可选)
|
||||
- VITE_GITHUB_TOKEN=${VITE_GITHUB_TOKEN}
|
||||
|
||||
# 应用配置
|
||||
- VITE_APP_ID=${VITE_APP_ID:-xcodereviewer}
|
||||
- VITE_MAX_ANALYZE_FILES=${VITE_MAX_ANALYZE_FILES:-40}
|
||||
- VITE_LLM_CONCURRENCY=${VITE_LLM_CONCURRENCY:-2}
|
||||
- VITE_LLM_GAP_MS=${VITE_LLM_GAP_MS:-500}
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- xcodereviewer-network
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
xcodereviewer-network:
|
||||
driver: bridge
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip 压缩配置
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript;
|
||||
|
||||
# 安全头部
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 处理 React Router 的客户端路由
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# API 代理(如果需要)
|
||||
# location /api {
|
||||
# proxy_pass http://backend:8000;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection 'upgrade';
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_cache_bypass $http_upgrade;
|
||||
# }
|
||||
}
|
||||
|
|
@ -19,19 +19,49 @@ const isValidUuid = (value?: string): boolean => {
|
|||
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
||||
};
|
||||
|
||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
||||
// 检查是否配置了 Supabase
|
||||
const hasSupabaseConfig = supabaseUrl && supabaseAnonKey;
|
||||
|
||||
// 如果没有配置 Supabase,使用虚拟配置避免错误
|
||||
const finalSupabaseUrl = hasSupabaseConfig ? supabaseUrl : 'https://demo.supabase.co';
|
||||
const finalSupabaseKey = hasSupabaseConfig ? supabaseAnonKey : 'demo-key';
|
||||
|
||||
export const supabase = hasSupabaseConfig ? createClient(finalSupabaseUrl, finalSupabaseKey, {
|
||||
global: {
|
||||
fetch: undefined
|
||||
},
|
||||
auth: {
|
||||
storageKey: (import.meta.env.VITE_APP_ID || "sb") + "-auth-token"
|
||||
}
|
||||
});
|
||||
}) : null;
|
||||
|
||||
// 演示模式标识
|
||||
export const isDemoMode = !hasSupabaseConfig;
|
||||
|
||||
// 演示数据
|
||||
const demoProfile: Profile = {
|
||||
id: 'demo-user',
|
||||
phone: null,
|
||||
email: 'demo@xcodereviewer.com',
|
||||
full_name: 'Demo User',
|
||||
avatar_url: null,
|
||||
role: 'admin',
|
||||
github_username: 'demo-user',
|
||||
gitlab_username: null,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 用户相关API
|
||||
export const api = {
|
||||
// Profile相关
|
||||
async getProfilesById(id: string): Promise<Profile | null> {
|
||||
if (isDemoMode) {
|
||||
return demoProfile;
|
||||
}
|
||||
|
||||
if (!supabase) return null;
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('*')
|
||||
|
|
@ -43,6 +73,12 @@ export const api = {
|
|||
},
|
||||
|
||||
async getProfilesCount(): Promise<number> {
|
||||
if (isDemoMode) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!supabase) return 0;
|
||||
|
||||
const { count, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('*', { count: 'exact', head: true });
|
||||
|
|
@ -95,6 +131,25 @@ export const api = {
|
|||
|
||||
// Project相关
|
||||
async getProjects(): Promise<Project[]> {
|
||||
if (isDemoMode) {
|
||||
return [{
|
||||
id: 'demo-project-1',
|
||||
name: 'Demo Project',
|
||||
description: '这是一个演示项目,展示 XCodeReviewer 的功能',
|
||||
repository_url: 'https://github.com/demo/project',
|
||||
repository_type: 'github',
|
||||
default_branch: 'main',
|
||||
programming_languages: JSON.stringify(['TypeScript', 'JavaScript', 'React']),
|
||||
owner_id: 'demo-user',
|
||||
owner: demoProfile,
|
||||
is_active: true,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}];
|
||||
}
|
||||
|
||||
if (!supabase) return [];
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('projects')
|
||||
.select(`
|
||||
|
|
@ -382,6 +437,28 @@ export const api = {
|
|||
|
||||
// 统计相关
|
||||
async getProjectStats(): Promise<any> {
|
||||
if (isDemoMode) {
|
||||
return {
|
||||
total_projects: 1,
|
||||
active_projects: 1,
|
||||
total_tasks: 3,
|
||||
completed_tasks: 2,
|
||||
total_issues: 15,
|
||||
resolved_issues: 12
|
||||
};
|
||||
}
|
||||
|
||||
if (!supabase) {
|
||||
return {
|
||||
total_projects: 0,
|
||||
active_projects: 0,
|
||||
total_tasks: 0,
|
||||
completed_tasks: 0,
|
||||
total_issues: 0,
|
||||
resolved_issues: 0
|
||||
};
|
||||
}
|
||||
|
||||
const [projectsResult, tasksResult, issuesResult] = await Promise.all([
|
||||
supabase.from('projects').select('id, is_active', { count: 'exact' }),
|
||||
supabase.from('audit_tasks').select('id, status', { count: 'exact' }),
|
||||
|
|
|
|||
Loading…
Reference in New Issue