Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Dependencies
node_modules/
.pnpm-store/

# Build output
dist/
build/
target/

# IDE files
.idea/
.vscode/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*
pnpm-debug.log*

# Git files
.git/
.gitignore

# Docker files (避免递归,但保留需要的配置文件)
docker/backend/
docker/frontend/Dockerfile
docker/.env
docker/.env.example
docker/docker-compose.yml
docker/docker-*.sh
docker/docker-*.bat

# Test files
coverage/
.nyc_output/
.playwright-cli/

# Documentation
*.md
docs/

# Root config files (不需要复制到镜像)
.env
.gitattributes
.github/
AGENTS.md
LICENSE

# SQL files (通过 volume 挂载,不需要复制)
sql/
28 changes: 28 additions & 0 deletions data-agent-backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Build artifacts
target/
!.mvn/wrapper/maven-wrapper.jar

# IDE files
.idea/
*.iml
*.iws
*.ipr

# OS files
.DS_Store
Thumbs.db

# Logs
*.log

# Git files
.git/
.gitignore

# Docker files
Dockerfile
docker/

# Documentation
README.md
docs/
40 changes: 40 additions & 0 deletions data-agent-frontend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Dependencies
node_modules/
.pnpm-store/

# Build output
dist/
build/

# IDE files
.idea/
.vscode/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*
pnpm-debug.log*

# Git files
.git/
.gitignore

# Docker files
Dockerfile
docker/

# Test files
coverage/
.nyc_output/
.playwright-cli/

# Documentation
README.md
docs/
CODE_STYLE.md
18 changes: 18 additions & 0 deletions docker/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Data Agent 环境变量配置
# 此文件不应提交到 Git(已在 .gitignore 中)

# AI 模型配置 (必填)
IO_GITHUB_MALONETALK_MODEL_API_KEY=
IO_GITHUB_MALONETALK_MODEL_PROVIDER=openai
IO_GITHUB_MALONETALK_MODEL_NAME=deepseek-v4-flash
IO_GITHUB_MALONETALK_MODEL_BASE_URL=https://api.deepseek.com
IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED=true

# 数据库配置
DB_PASSWORD=root

# JWT 配置(生产环境必须 >= 32 字节)
JWT_SECRET=

# 管理员配置(首次启动时使用)
ADMIN_INIT_PASSWORD=admin
38 changes: 38 additions & 0 deletions docker/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Stage 1: Build (使用 maven 镜像,自带 Maven)
FROM maven:3.9-eclipse-temurin-17 AS builder

WORKDIR /app

# Copy pom.xml and download dependencies
COPY data-agent-backend/pom.xml .
RUN mvn dependency:go-offline -B

# Copy source code and build
COPY data-agent-backend/src ./src
RUN mvn clean package -DskipTests -B

# Stage 2: Runtime
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

# 创建 skills 目录(skill.properties 需要)
RUN mkdir -p /app/skills

# Add non-root user
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser

# Copy the built jar
COPY --from=builder /app/target/*.jar app.jar

# Create log directory
RUN mkdir -p /app/logs && chown -R appuser:appgroup /app

USER appuser

# Health check
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", "-jar", "app.jar"]
110 changes: 110 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Data Agent Docker Compose Configuration
#
# 所有配置项都有合理的默认值,可以直接启动。
# 如需自定义配置,在 docker/ 目录创建 .env.example 文件覆盖默认值即可。
#
# 示例 .env.example 文件:
# IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-your-api-key
# DB_PASSWORD=your-password
# JWT_SECRET=your-jwt-secret-at-least-32-bytes-long
# ADMIN_INIT_PASSWORD=your-admin-password
#

services:
# MySQL Database
mysql:
image: mysql:8.0
container_name: data-agent-mysql
restart: unless-stopped
environment:
# 可通过 .env.example 覆盖:DB_PASSWORD=xxx
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root}
MYSQL_DATABASE: data_agent
MYSQL_CHARACTER_SET_SERVER: utf8mb4
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
TZ: Asia/Shanghai
ports:
# 可通过 .env.example 覆盖:MYSQL_PORT=3307
- "${MYSQL_PORT:-3306}:3306"
volumes:
- data-agent-mysql:/var/lib/mysql
# 自动挂载项目根目录的 sql/ 文件夹进行初始化
- ../sql:/docker-entrypoint-initdb.d:ro
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --character-set-client-handshake=FALSE
- --init-connect='SET NAMES utf8mb4'
- --default-authentication-plugin=mysql_native_password
networks:
- data-agent-network
healthcheck:
# 使用 mysql 命令实际测试数据库连接和查询
test: ["CMD", "mysql", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-root}", "-e", "SELECT 1 FROM data_agent.sys_user LIMIT 1;"]
interval: 10s
timeout: 10s
retries: 10
start_period: 60s

# Backend Service (Spring Boot)
backend:
build:
context: ..
dockerfile: docker/backend/Dockerfile
container_name: data-agent-backend
restart: unless-stopped
environment:
# 数据库配置(通常不需要修改)
DB_URL: ${DB_URL:-jdbc:mysql://mysql:3306/data_agent?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true}
DB_USERNAME: ${DB_USERNAME:-root}
DB_PASSWORD: ${DB_PASSWORD:-root}

# AI 模型配置(重要:需要配置真实的 API Key)
# 可通过 .env.example 覆盖:IO_GITHUB_MALONETALK_MODEL_API_KEY=sk-xxx
IO_GITHUB_MALONETALK_MODEL_PROVIDER: ${IO_GITHUB_MALONETALK_MODEL_PROVIDER:-dashscope}
IO_GITHUB_MALONETALK_MODEL_NAME: ${IO_GITHUB_MALONETALK_MODEL_NAME:-qwen3-max}
IO_GITHUB_MALONETALK_MODEL_BASE_URL: ${IO_GITHUB_MALONETALK_MODEL_BASE_URL:-}
IO_GITHUB_MALONETALK_MODEL_API_KEY: ${IO_GITHUB_MALONETALK_MODEL_API_KEY:-}
IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED: ${IO_GITHUB_MALONETALK_MODEL_THINKING_ENABLED:-false}

# JWT 配置(可通过 .env.example 覆盖)
# JWT_SECRET 必须 >= 32 字节
JWT_SECRET: ${JWT_SECRET:-}
JWT_EXPIRATION_HOURS: ${JWT_EXPIRATION_HOURS:-24}

# 初始管理员密码(首次启动时使用)
ADMIN_INIT_PASSWORD: ${ADMIN_INIT_PASSWORD:-admin}

TZ: Asia/Shanghai
ports:
# 可通过 .env.example 覆盖:BACKEND_PORT=8081
- "${BACKEND_PORT:-8080}:8080"
depends_on:
mysql:
condition: service_healthy
networks:
- data-agent-network

# Frontend Service (Vue + Nginx)
frontend:
build:
context: ..
dockerfile: docker/frontend/Dockerfile
container_name: data-agent-frontend
restart: unless-stopped
ports:
# 可通过 .env.example 覆盖:FRONTEND_PORT=3001
- "${FRONTEND_PORT:-3000}:80"
depends_on:
backend:
condition: service_healthy
networks:
- data-agent-network

volumes:
data-agent-mysql:
name: data-agent-mysql

networks:
data-agent-network:
name: data-agent-network
31 changes: 31 additions & 0 deletions docker/frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Stage 1: Build
FROM node:20-alpine AS builder

WORKDIR /app

# 直接安装 pnpm(不使用 corepack)
RUN npm install -g pnpm

# 先复制所有源代码(.dockerignore 会排除不需要的文件)
COPY data-agent-frontend/ .

# 再安装依赖
RUN pnpm install --frozen-lockfile

# Build the application (跳过类型检查,直接打包)
RUN pnpm vite build

# Stage 2: Production with Nginx
FROM nginx:alpine

# Copy custom nginx config
COPY docker/frontend/nginx.conf /etc/nginx/nginx.conf

# Copy built files
COPY --from=builder /app/dist /usr/share/nginx/html

# Add healthcheck
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1/health || exit 1

CMD ["nginx", "-g", "daemon off;"]
68 changes: 68 additions & 0 deletions docker/frontend/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;

# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss image/svg+xml;

# Docker DNS resolver
resolver 127.0.0.11 valid=10s;

server {
listen 80;
listen [::]:80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;

# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

# Vue Router history mode support
location / {
try_files $uri $uri/ /index.html;
}

# API proxy to backend
location /api/ {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}

# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
Loading
Loading