diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9c87fe2 --- /dev/null +++ b/.env.example @@ -0,0 +1,25 @@ +# EZOps AIOps - 环境变量模板 +# 复制为 .env 并根据需要修改 + +# 服务配置 +SERVER_PORT=8080 +SERVER_MODE=development + +# 前端端口 +FRONTEND_PORT=5173 + +# 数据库配置 - 默认 SQLite 零配置 +DB_DRIVER=sqlite +DB_NAME=data/aiops.db + +# MySQL 配置(可选) +# DB_DRIVER=mysql +# DB_HOST=localhost +# DB_PORT=3306 +# DB_USER=root +# DB_PASSWORD= +# DB_NAME=aiops + +# JWT 配置 +JWT_SECRET=aiops-secret-key-change-in-production +JWT_EXPIRE_HOURS=24 diff --git a/.gitignore b/.gitignore index 6cae04a..482413c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ # Dependencies node_modules/ -app/node_modules/ +frontend/node_modules/ # Build output -app/dist/ -app/dist-ssr/ +frontend/dist/ +frontend/dist-ssr/ backend/bin/ *.exe *.exe~ @@ -17,9 +17,14 @@ backend/vendor/ *.test *.out -# Config -config.yaml +# Compiled binary +backend/aiops-server +backend/aiops + +# Config (keep backend/config.yaml as default) +config.local.yaml *.local +.env # IDE .idea/ @@ -39,6 +44,10 @@ logs/ # Database *.db *.sqlite +backend/data/ # Docker docker-data/ + +# PID files +*.pid diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09ef793 --- /dev/null +++ b/Makefile @@ -0,0 +1,62 @@ +# EZOps AIOps - Makefile +.PHONY: help install dev build clean status stop restart lint + +help: + @echo "EZOps AIOps - 可用命令:" + @echo "" + @echo " make install - 安装前后端依赖" + @echo " make dev - 启动开发环境(前后端)" + @echo " make build - 构建生产环境(前后端)" + @echo " make build-ui - 仅构建前端" + @echo " make build-api - 仅构建后端" + @echo " make status - 查看服务状态" + @echo " make stop - 停止所有服务" + @echo " make restart - 重启所有服务" + @echo " make logs - 查看实时日志" + @echo " make lint - 代码检查" + @echo " make clean - 清理构建产物" + @echo "" + +install: + @echo "安装后端依赖..." + cd backend && go mod download + @echo "安装前端依赖..." + cd frontend && npm install + +dev: + ./run.sh start + +build-ui: + @echo "构建前端..." + cd frontend && npm run build + +build-api: + @echo "构建后端..." + cd backend && CGO_ENABLED=1 go build -o aiops-server cmd/main.go + +build: build-api build-ui + +status: + ./run.sh status + +stop: + ./run.sh stop + +restart: + ./run.sh restart + +logs: + @tail -f backend/logs/server.log frontend/logs/dev.log 2>/dev/null || echo "日志文件不存在" + +lint: + @echo "检查后端代码..." + cd backend && go vet ./... + @echo "检查前端代码..." + cd frontend && npm run lint 2>/dev/null || echo "前端 lint 未配置,跳过" + +clean: + @echo "清理构建产物..." + rm -f backend/aiops-server + rm -rf frontend/dist + rm -f backend/logs/*.log frontend/logs/*.log + @echo "清理完成" diff --git a/README.md b/README.md index c178216..def2c4b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ``` aiops/ -├── app/ # 前端项目 (React + TypeScript) +├── frontend/ # 前端项目 (React + TypeScript) │ ├── src/ │ │ ├── components/ # 组件 (dashboard/server/k8s/monitor/deploy/aiops/...) │ │ ├── lib/ # 工具函数 diff --git a/app/vite.config.ts b/app/vite.config.ts deleted file mode 100644 index 32692d1..0000000 --- a/app/vite.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import path from "path" -import react from "@vitejs/plugin-react" -import { defineConfig } from "vite" -import { inspectAttr } from 'kimi-plugin-inspect-react' - -// https://vite.dev/config/ -export default defineConfig({ - base: './', - plugins: [inspectAttr(), react()], - resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - }, - }, -}); diff --git a/backend/go.mod b/backend/go.mod index 691363a..54a8579 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -10,7 +10,8 @@ require ( golang.org/x/crypto v0.18.0 gorm.io/driver/mysql v1.5.2 gorm.io/driver/postgres v1.5.4 - gorm.io/gorm v1.25.5 + gorm.io/driver/sqlite v1.6.0 + gorm.io/gorm v1.30.0 ) require ( @@ -35,6 +36,7 @@ require ( github.com/leodido/go-urn v1.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -53,7 +55,7 @@ require ( golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.20.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/backend/go.sum b/backend/go.sum index 0bfddc8..7f59e9c 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -64,6 +64,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -128,8 +130,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -146,7 +148,9 @@ gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo= gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= -gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= +gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 9ba8ba6..b2bc5d2 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -1,6 +1,8 @@ package config import ( + "fmt" + "github.com/spf13/viper" "go.uber.org/zap" ) @@ -60,7 +62,8 @@ func Load() *Config { viper.SetDefault("mode", "development") viper.SetDefault("port", "8080") viper.SetDefault("log_level", "info") - viper.SetDefault("database.driver", "mysql") + viper.SetDefault("database.driver", "sqlite") + viper.SetDefault("database.dbname", "data/aiops.db") viper.SetDefault("database.host", "localhost") viper.SetDefault("database.port", 3306) viper.SetDefault("database.charset", "utf8mb4") @@ -90,7 +93,11 @@ func Load() *Config { // DSN 返回数据库连接字符串 func (d *DatabaseConfig) DSN() string { if d.Driver == "mysql" { - return d.User + ":" + d.Password + "@tcp(" + d.Host + ":" + string(rune(d.Port)) + ")/" + d.DBName + "?charset=" + d.Charset + "&parseTime=True&loc=Local" + return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local", + d.User, d.Password, d.Host, d.Port, d.DBName, d.Charset) + } + if d.Driver == "sqlite" { + return d.DBName } return "" } diff --git a/backend/internal/models/approval.go b/backend/internal/models/approval.go index d1a2bf8..8d8aa70 100644 --- a/backend/internal/models/approval.go +++ b/backend/internal/models/approval.go @@ -44,9 +44,9 @@ type ApprovalProcess struct { DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 关联 - Template *ApprovalTemplate `json:"template,omitempty"` - ApplicantUser *User `json:"applicant_user,omitempty"` - Steps []ApprovalStep `json:"steps,omitempty"` + Template *ApprovalTemplate `gorm:"foreignKey:TemplateID" json:"template,omitempty"` + ApplicantUser *User `gorm:"foreignKey:Applicant" json:"applicant_user,omitempty"` + Steps []ApprovalStep `gorm:"foreignKey:ProcessID" json:"steps,omitempty"` } // TableName 表名 @@ -67,7 +67,7 @@ type ApprovalStep struct { UpdatedAt time.Time `json:"updated_at"` // 关联 - ApproverUser *User `json:"approver_user,omitempty"` + ApproverUser *User `gorm:"foreignKey:Approver" json:"approver_user,omitempty"` } // TableName 表名 diff --git a/backend/pkg/database/database.go b/backend/pkg/database/database.go index 6136f07..73a638f 100644 --- a/backend/pkg/database/database.go +++ b/backend/pkg/database/database.go @@ -9,6 +9,7 @@ import ( "gorm.io/driver/mysql" "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) @@ -26,6 +27,8 @@ func New(cfg config.DatabaseConfig) (*gorm.DB, error) { dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.DBName) dialector = postgres.Open(dsn) + case "sqlite": + dialector = sqlite.Open(cfg.DBName) default: return nil, fmt.Errorf("unsupported database driver: %s", cfg.Driver) } diff --git a/docs/PROJECT_SUMMARY.md b/docs/PROJECT_SUMMARY.md index c6d8cdb..79bed59 100644 --- a/docs/PROJECT_SUMMARY.md +++ b/docs/PROJECT_SUMMARY.md @@ -8,7 +8,7 @@ EZOps 是一个专业的 AIOps 智能运维平台,包含完整的前后端代 ``` /mnt/okcomputer/output/ -├── app/ # 前端项目 (React + TypeScript + Vite) +├── frontend/ # 前端项目 (React + TypeScript + Vite) │ ├── src/ │ │ ├── components/ # 组件目录 │ │ │ ├── layout/ # 布局组件 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..5595073 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,23 @@ +# EZOps AIOps 开发文档 + +本目录包含 EZOps AIOps 智能运维平台的全部开发文档。 + +## 文档索引 + +| 文档 | 说明 | +|------|------| +| [项目结构](./project-structure.md) | 前后端目录结构说明 | +| [快速开始](./quick-start.md) | 环境准备、安装、启动 | +| [API 接口](./api-reference.md) | 后端 API 接口文档 | +| [数据库设计](./database-schema.md) | 数据模型和表结构 | +| [开发规范](./development-guide.md) | 代码规范、Git 工作流 | +| [部署指南](./deployment.md) | 生产环境部署 | + +## 设计文档 + +| 文档 | 说明 | +|------|------| +| [ROADMAP](./ROADMAP.md) | 项目路线图 | +| [PDD 计划](./PDD_PLAN.md) | 产品驱动开发计划 | +| [项目摘要](./PROJECT_SUMMARY.md) | 项目整体概述 | +| [后端摘要](./BACKEND_SUMMARY.md) | 后端架构概述 | diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 0000000..53bd0e0 --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,261 @@ +# API 接口文档 + +## 基础信息 + +| 项目 | 值 | +|------|---| +| Base URL | `http://localhost:8080/api/v1` | +| 认证方式 | JWT Bearer Token | +| 请求格式 | JSON | +| 响应格式 | JSON | + +## 通用响应结构 + +```json +{ + "code": 200, + "message": "success", + "data": {} +} +``` + +分页响应: + +```json +{ + "code": 200, + "message": "success", + "data": { + "list": [], + "total": 100 + } +} +``` + +## 认证 + +### POST /auth/login + +用户登录,获取 JWT Token。 + +**请求体:** +```json +{ + "username": "admin", + "password": "admin123" +} +``` + +**响应:** +```json +{ + "code": 200, + "message": "登录成功", + "data": { + "token": "eyJhbGciOiJIUzI1NiIs...", + "expires_in": 86400, + "user": { + "id": 1, + "username": "admin", + "real_name": "管理员" + } + } +} +``` + +### POST /auth/register + +用户注册。 + +### GET /user/profile + +获取当前登录用户信息。(需认证) + +### PUT /user/profile + +更新当前用户信息。(需认证) + +### PUT /user/password + +修改密码。(需认证) + +## 用户管理(管理员) + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/admin/users` | 用户列表 | +| GET | `/admin/users/:id` | 用户详情 | +| POST | `/admin/users` | 创建用户 | +| PUT | `/admin/users/:id` | 更新用户 | +| DELETE | `/admin/users/:id` | 删除用户 | + +## 仪表盘 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/dashboard/stats` | 统计数据 | +| GET | `/dashboard/recent-alerts` | 最近告警 | +| GET | `/dashboard/recent-releases` | 最近发布 | +| GET | `/dashboard/metrics` | 性能指标 | + +## 服务器管理 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/servers` | 服务器列表 | +| GET | `/servers/stats` | 服务器统计 | +| GET | `/servers/:id` | 服务器详情 | +| POST | `/servers` | 添加服务器 | +| PUT | `/servers/:id` | 更新服务器 | +| DELETE | `/servers/:id` | 删除服务器 | +| POST | `/servers/batch-delete` | 批量删除 | + +## 云平台管理 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/cloud-accounts` | 云账号列表 | +| POST | `/cloud-accounts` | 添加云账号 | +| DELETE | `/cloud-accounts/:id` | 删除云账号 | +| GET | `/domains` | 域名列表 | +| POST | `/domains` | 添加域名 | +| DELETE | `/domains/:id` | 删除域名 | + +## K8s 集群 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/k8s-clusters` | 集群列表 | +| GET | `/k8s-clusters/:id` | 集群详情 | +| POST | `/k8s-clusters` | 添加集群 | +| PUT | `/k8s-clusters/:id` | 更新集群 | +| DELETE | `/k8s-clusters/:id` | 删除集群 | +| GET | `/k8s-clusters/:id/nodes` | 节点列表 | +| GET | `/k8s-clusters/:id/pods` | Pod 列表 | +| GET | `/k8s-clusters/:id/deployments` | Deployment 列表 | +| GET | `/k8s-clusters/:id/services` | Service 列表 | +| GET | `/k8s-clusters/:id/ingresses` | Ingress 列表 | + +## 监控告警 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/alerts` | 告警事件列表 | +| GET | `/alerts/rules` | 告警规则列表 | +| POST | `/alerts/rules` | 创建告警规则 | +| PUT | `/alerts/rules/:id` | 更新规则 | +| DELETE | `/alerts/rules/:id` | 删除规则 | +| POST | `/alerts/rules/:id/toggle` | 启用/禁用规则 | +| POST | `/alerts/:id/ack` | 确认告警 | + +## CI/CD 流水线 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/pipelines` | 流水线列表 | +| POST | `/pipelines` | 创建流水线 | +| GET | `/pipelines/:id` | 流水线详情 | +| PUT | `/pipelines/:id` | 更新流水线 | +| DELETE | `/pipelines/:id` | 删除流水线 | +| POST | `/pipelines/:id/trigger` | 触发流水线 | +| GET | `/pipelines/:id/runs` | 执行记录 | + +## 应用发布 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/releases` | 发布列表 | +| POST | `/releases` | 创建发布 | +| POST | `/releases/:id/approve` | 审批发布 | +| POST | `/releases/:id/deploy` | 部署发布 | +| POST | `/releases/:id/rollback` | 回滚发布 | + +## 自动化运维 + +### 定时任务 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/cron-jobs` | 任务列表 | +| POST | `/cron-jobs` | 创建任务 | +| GET | `/cron-jobs/:id` | 任务详情 | +| PUT | `/cron-jobs/:id` | 更新任务 | +| DELETE | `/cron-jobs/:id` | 删除任务 | +| POST | `/cron-jobs/:id/toggle` | 启用/禁用 | +| POST | `/cron-jobs/:id/execute` | 手动执行 | + +### 脚本管理 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/scripts` | 脚本列表 | +| POST | `/scripts` | 创建脚本 | +| GET | `/scripts/:id` | 脚本详情 | +| PUT | `/scripts/:id` | 更新脚本 | +| DELETE | `/scripts/:id` | 删除脚本 | +| POST | `/scripts/:id/execute` | 执行脚本 | + +## 审批流程 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/approvals/templates` | 审批模板列表 | +| POST | `/approvals/templates` | 创建模板 | +| GET | `/approvals/templates/:id` | 模板详情 | +| PUT | `/approvals/templates/:id` | 更新模板 | +| DELETE | `/approvals/templates/:id` | 删除模板 | +| GET | `/approvals/processes` | 审批流程列表 | +| POST | `/approvals/processes` | 发起审批 | +| GET | `/approvals/processes/:id` | 流程详情 | +| POST | `/approvals/processes/:id/approve` | 审批通过 | +| POST | `/approvals/processes/:id/reject` | 审批驳回 | + +## 导航管理 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/navigations/groups` | 导航分组列表 | +| POST | `/navigations/groups` | 创建分组 | +| PUT | `/navigations/groups/:id` | 更新分组 | +| DELETE | `/navigations/groups/:id` | 删除分组 | +| GET | `/navigations/links` | 导航链接列表 | +| POST | `/navigations/links` | 创建链接 | +| PUT | `/navigations/links/:id` | 更新链接 | +| DELETE | `/navigations/links/:id` | 删除链接 | +| POST | `/navigations/links/:id/favorite` | 收藏/取消收藏 | + +## 审计日志 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/audit/logs` | 审计日志列表 | +| GET | `/audit/logs/:id` | 日志详情 | +| GET | `/audit/modules` | 审计模块列表 | +| GET | `/audit/actions` | 审计操作列表 | +| GET | `/audit/logs/export` | 导出日志 | +| GET | `/audit/statistics` | 审计统计 | +| POST | `/audit/logs/clear` | 清空日志 | + +## 健康检查 + +### GET /health + +无需认证。 + +```json +{ + "status": "ok" +} +``` + +### GET /version + +无需认证。 + +```json +{ + "version": "dev", + "build_time": "unknown", + "git_commit": "unknown" +} +``` diff --git a/docs/database-schema.md b/docs/database-schema.md new file mode 100644 index 0000000..547992a --- /dev/null +++ b/docs/database-schema.md @@ -0,0 +1,210 @@ +# 数据库设计 + +## 概述 + +EZOps AIOps 使用 GORM 作为 ORM 框架,支持 SQLite / MySQL / PostgreSQL。 + +- **默认**: SQLite(零配置,数据文件 `backend/data/aiops.db`) +- **生产**: 推荐使用 MySQL 或 PostgreSQL + +首次启动时,`AutoMigrate` 会自动创建所有表。 + +## 数据模型 + +### 用户体系 + +#### users - 用户表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | uint | 主键 | +| username | string | 用户名(唯一) | +| password | string | 密码(bcrypt 加密) | +| email | string | 邮箱 | +| phone | string | 手机号 | +| real_name | string | 真实姓名 | +| avatar | string | 头像 URL | +| status | int | 状态(1=正常 0=禁用) | +| last_login | time | 最后登录时间 | +| created_at | time | 创建时间 | +| updated_at | time | 更新时间 | + +#### roles - 角色表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | uint | 主键 | +| name | string | 角色名称 | +| code | string | 角色代码 | +| description | string | 角色描述 | +| sort | int | 排序 | + +#### permissions - 权限表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | uint | 主键 | +| name | string | 权限名称 | +| code | string | 权限代码 | +| type | string | 权限类型(menu/btn/api) | +| path | string | 路由路径 | +| method | string | HTTP 方法 | + +### 服务器管理 + +#### servers - 服务器表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | uint | 主键 | +| name | string | 服务器名称 | +| hostname | string | 主机名 | +| ip | string | IP 地址 | +| port | int | SSH 端口 | +| os | string | 操作系统 | +| cpu | int | CPU 核数 | +| memory | int64 | 内存(MB) | +| disk | int64 | 磁盘(GB) | +| status | string | 状态(online/offline/maintenance) | +| tags | string | 标签(JSON) | +| cloud_id | uint | 关联云账号 | +| region | string | 地域 | + +#### clouds - 云平台表 +#### cloud_accounts - 云账号表 +#### regions - 地域表 +#### domains - 域名表 + +### K8s 管理 + +#### k8s_clusters - K8s 集群表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | uint | 主键 | +| name | string | 集群名称 | +| version | string | K8s 版本 | +| api_server | string | API Server 地址 | +| kubeconfig | string | Kubeconfig 内容 | +| status | string | 集群状态 | + +#### k8s_nodes, k8s_pods, k8s_deployments, k8s_services, k8s_ingresses + +### 监控告警 + +#### alert_rules - 告警规则表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | uint | 主键 | +| name | string | 规则名称 | +| description | string | 规则描述 | +| type | string | 告警类型 | +| source | string | 数据源 | +| expression | string | PromQL 表达式 | +| severity | string | 严重级别(critical/warning/info) | +| duration | string | 持续时间 | +| notify_channels | string | 通知渠道(JSON) | +| enabled | bool | 是否启用 | + +#### alert_events - 告警事件表 +#### notify_channels - 通知渠道表 +#### metric_data - 指标数据表 + +### CI/CD + +#### pipelines - 流水线表 +#### pipeline_runs - 流水线执行记录表 +#### app_releases - 应用发布表 + +### 自动化运维 + +#### cron_jobs - 定时任务表 +#### scripts - 脚本表 +#### task_executions - 任务执行记录表 +#### playbooks - Ansible Playbook 表 + +### 审批流程 + +#### approval_templates - 审批模板表 +#### approval_processes - 审批流程表 +#### approval_steps - 审批步骤表 + +### 其他 + +#### navigation_groups - 导航分组表 +#### navigation_links - 导航链接表 +#### audit_logs - 审计日志表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | uint | 主键 | +| user_id | *uint | 操作用户 ID | +| module | string | 模块 | +| action | string | 操作类型 | +| resource | string | 资源标识 | +| request | string | 请求内容 | +| response | string | 响应内容 | +| error | string | 错误信息 | +| ip | string | 操作 IP | +| created_at | time | 操作时间 | + +## 表关系 + +``` +users ──< user_roles >── roles ──< role_permissions >── permissions + +servers ──> cloud_accounts ──> clouds +servers ──> regions + +alert_rules ──< alert_events +alert_rules ──> notify_channels + +pipelines ──< pipeline_runs +app_releases ──> pipelines + +approval_templates ──< approval_processes ──< approval_steps + +navigation_groups ──< navigation_links +``` + +## AutoMigrate + +表结构在 `backend/pkg/database/database.go` 中定义,服务启动时自动执行: + +```go +func AutoMigrate(db *gorm.DB) error { + return db.AutoMigrate( + &models.User{}, &models.Role{}, &models.Permission{}, + &models.Server{}, &models.Cloud{}, ... + ) +} +``` + +## 切换数据库 + +### MySQL + +编辑 `backend/config.yaml`: +```yaml +database: + driver: mysql + host: localhost + port: 3306 + user: root + password: your_password + dbname: aiops + charset: utf8mb4 +``` + +### PostgreSQL + +```yaml +database: + driver: postgres + host: localhost + port: 5432 + user: postgres + password: your_password + dbname: aiops +``` diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..01d2d7b --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,139 @@ +# 部署指南 + +## 开发环境 + +参见 [快速开始](./quick-start.md)。 + +```bash +./run.sh start +``` + +## 生产构建 + +### 后端编译 + +```bash +cd backend +CGO_ENABLED=1 go build -ldflags="-s -w" -o aiops-server cmd/main.go +``` + +### 前端构建 + +```bash +cd app +npm run build # 输出到 dist/ +``` + +### 使用 Makefile + +```bash +make build # 同时构建前后端 +``` + +## Docker 部署 + +### Dockerfile + +后端已有 `backend/Dockerfile`,可基于此构建镜像。 + +```bash +cd backend +docker build -t aiops-server . +``` + +### docker-compose + +```bash +cd backend +docker-compose up -d +``` + +## Nginx 反向代理 + +前端构建后部署到 Nginx,后端 API 反向代理: + +```nginx +server { + listen 80; + server_name aiops.example.com; + + # 前端静态文件 + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + } + + # 后端 API 代理 + location /api/ { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # WebSocket 代理 + location /ws/ { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +参考配置文件:`backend/nginx.conf` + +## 系统服务(systemd) + +创建 `/etc/systemd/system/aiops.service`: + +```ini +[Unit] +Description=EZOps AIOps Server +After=network.target + +[Service] +Type=simple +User=www-data +WorkingDirectory=/opt/aiops +ExecStart=/opt/aiops/aiops-server +Restart=always +RestartSec=5 +Environment=GIN_MODE=release + +[Install] +WantedBy=multi-user.target +``` + +```bash +systemctl enable aiops +systemctl start aiops +``` + +## 环境变量 + +| 变量 | 默认值 | 说明 | +|------|--------|------| +| PORT | 8080 | 后端端口 | +| GIN_MODE | debug | Gin 模式(release/debug) | +| GOPROXY | - | Go 模块代理 | + +## 生产配置建议 + +1. **数据库**: 生产环境使用 MySQL/PostgreSQL,不要用 SQLite +2. **JWT Secret**: 更换为高强度随机字符串 +3. **CORS**: 生产环境配置允许的具体域名,不要用 `*` +4. **HTTPS**: 配置 TLS 证书 +5. **日志**: 使用文件日志 + 日志轮转 +6. **备份**: 定期备份数据库 + +## 健康检查 + +```bash +# 检查后端是否正常 +curl http://localhost:8080/health +# {"status":"ok"} + +# 检查版本 +curl http://localhost:8080/version +# {"version":"dev","build_time":"...","git_commit":"..."} +``` diff --git a/docs/development-guide.md b/docs/development-guide.md new file mode 100644 index 0000000..6f82cef --- /dev/null +++ b/docs/development-guide.md @@ -0,0 +1,188 @@ +# 开发规范 + +## Git 工作流 + +### 分支策略 + +| 分支 | 说明 | +|------|------| +| `main` / `master` | 生产分支,保护分支 | +| `dev` | 开发集成分支 | +| `feature/*` | 功能分支 | +| `fix/*` | 修复分支 | +| `hotfix/*` | 紧急修复 | + +### 提交规范 + +``` +: + +- 变更点 1 +- 变更点 2 +``` + +Type 列表: +- `feat`: 新功能 +- `fix`: Bug 修复 +- `refactor`: 重构 +- `docs`: 文档 +- `style`: 样式 +- `test`: 测试 +- `chore`: 构建/工具 + +示例: +``` +feat: 添加服务器批量导入功能 + +- 支持 Excel 导入 +- 支持云平台同步 +``` + +## 后端规范(Go) + +### 目录职责 + +| 目录 | 职责 | +|------|------| +| `cmd/` | 应用入口 | +| `internal/handlers/` | HTTP 请求处理(参数校验、响应) | +| `internal/models/` | 数据模型定义 | +| `internal/config/` | 配置管理 | +| `internal/middleware/` | 中间件 | +| `internal/routes/` | 路由注册 | +| `pkg/database/` | 数据库连接 | +| `pkg/logger/` | 日志工具 | + +### 命名规范 + +- 文件名:`snake_case.go` +- 类型/方法:`PascalCase` +- 变量:`camelCase` +- 常量:`PascalCase` 或 `ALL_CAPS` + +### 统一响应格式 + +```go +// 成功响应 +c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "success", + Data: result, +}) + +// 分页响应 +c.JSON(http.StatusOK, models.Response{ + Code: 200, + Message: "success", + Data: models.ListResponse{ + List: items, + Total: total, + }, +}) + +// 错误响应 +c.JSON(http.StatusBadRequest, models.Response{ + Code: 400, + Message: "参数错误: " + err.Error(), +}) +``` + +### Handler 模板 + +```go +func (h *ServerHandler) CreateServer(c *gin.Context) { + // 1. 参数绑定 + var req models.ServerRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: err.Error()}) + return + } + + // 2. 业务逻辑 + server := models.Server{ + Name: req.Name, + IP: req.IP, + } + if err := h.db.Create(&server).Error; err != nil { + c.JSON(http.StatusInternalServerError, models.Response{Code: 500, Message: "创建失败"}) + return + } + + // 3. 返回结果 + c.JSON(http.StatusOK, models.Response{Code: 200, Message: "创建成功", Data: server}) +} +``` + +### 注意事项 + +- GORM `First()` 连续查询会带上次 ID,用 `Take()` 代替 +- Go 编译前设置 `GOPROXY=https://goproxy.cn,direct` +- SQLite 需要 `CGO_ENABLED=1` +- JWT 使用 `golang-jwt/jwt/v5`,`jwt.TimeFunc()` 已移除,用 `time.Now()` + +## 前端规范(React + TypeScript) + +### 目录职责 + +| 目录 | 职责 | +|------|------| +| `src/components/*/` | 业务组件 | +| `src/components/ui/` | shadcn/ui 基础组件 | +| `src/hooks/` | 自定义 Hooks | +| `src/lib/` | 工具函数 | +| `src/services/` | API 封装 | +| `src/store/` | Zustand 状态管理 | +| `src/types/` | TypeScript 类型 | +| `src/mocks/` | Mock 数据 | + +### 命名规范 + +- 组件文件:`PascalCase.tsx`(如 `ServerManager.tsx`) +- 工具文件:`camelCase.ts`(如 `formatDate.ts`) +- 组件名:`PascalCase` + +### UI 设计原则 + +- 能去掉的装饰都去掉(无边框、无多余分隔线) +- 选中态用 `bg-surface-active`,不用彩色高亮 +- 页面打开即用,不需要额外的"新建"按钮跳转 +- 操作成功不弹 toast,仅在失败时提示 + +### API 调用 + +前端通过 Vite 代理访问后端 API: + +```typescript +// 开发环境请求会被代理到 http://localhost:8080 +fetch('/api/v1/servers') +``` + +## 测试 + +### 后端测试 + +```bash +cd backend +go test -v ./... +``` + +### 前端构建测试 + +```bash +cd app +npm run build # TypeScript 编译 + Vite 构建 +``` + +## 日志 + +| 级别 | 使用场景 | +|------|---------| +| Info | 服务启动、正常操作 | +| Warn | 配置缺失、降级处理 | +| Error | 操作失败、异常 | +| Fatal | 启动失败、不可恢复 | + +```go +log.Info("server started", zap.String("port", cfg.Port)) +log.Error("database error", zap.Error(err)) +``` diff --git a/docs/project-structure.md b/docs/project-structure.md new file mode 100644 index 0000000..9311458 --- /dev/null +++ b/docs/project-structure.md @@ -0,0 +1,136 @@ +# 项目结构 + +## 整体结构 + +``` +aiops/ +├── frontend/ # 前端 (React 18 + TypeScript + Vite) +│ ├── src/ +│ │ ├── components/ # 业务组件 +│ │ │ ├── aiops/ # AI 智能运维组件 +│ │ │ ├── approval/ # 审批流程 +│ │ │ ├── audit/ # SQL 审计 +│ │ │ ├── automation/ # 自动化运维 +│ │ │ ├── cloud/ # 云平台管理 +│ │ │ ├── dashboard/ # 仪表盘 +│ │ │ ├── deploy/ # CI/CD 部署 +│ │ │ ├── k8s/ # K8s 集群管理 +│ │ │ ├── layout/ # 布局组件 +│ │ │ ├── monitor/ # 监控告警 +│ │ │ ├── navigation/ # 导航管理 +│ │ │ ├── server/ # 服务器管理 +│ │ │ ├── system/ # 系统管理 +│ │ │ └── ui/ # shadcn/ui 基础组件 +│ │ ├── hooks/ # 自定义 Hooks +│ │ ├── lib/ # 工具库 +│ │ ├── mocks/ # Mock 数据 +│ │ ├── services/ # API 封装 +│ │ ├── store/ # Zustand 状态管理 +│ │ ├── types/ # TypeScript 类型定义 +│ │ ├── App.css # 全局样式 +│ │ ├── App.tsx # 根组件 +│ │ ├── index.css # 基础样式 +│ │ └── main.tsx # 应用入口 +│ ├── package.json +│ ├── vite.config.ts # Vite 配置(含 API 代理) +│ ├── tailwind.config.js # Tailwind CSS 配置 +│ └── tsconfig.json +│ +├── backend/ # 后端 (Go + Gin + GORM) +│ ├── cmd/ +│ │ └── main.go # 应用入口 +│ ├── internal/ +│ │ ├── api/ +│ │ │ ├── handlers/ # HTTP 处理器 +│ │ │ │ ├── alert.go # 告警管理 +│ │ │ │ ├── approval.go # 审批流程 +│ │ │ │ ├── audit.go # 审计日志 +│ │ │ │ ├── automation.go # 自动化运维 +│ │ │ │ ├── dashboard.go # 仪表盘 +│ │ │ │ ├── k8s.go # K8s 管理 +│ │ │ │ ├── navigation.go # 导航管理 +│ │ │ │ ├── pipeline.go # CI/CD 流水线 +│ │ │ │ ├── server.go # 服务器管理 +│ │ │ │ └── user.go # 用户认证 +│ │ │ ├── middleware/ # 中间件 +│ │ │ │ └── auth.go # JWT 认证 + RBAC +│ │ │ └── routes/ +│ │ │ └── routes.go # 路由注册 +│ │ ├── config/ +│ │ │ └── config.go # 配置加载 (Viper) +│ │ └── models/ # 数据模型 +│ │ ├── alert.go # 告警模型 +│ │ ├── approval.go # 审批模型 +│ │ ├── audit.go # 审计日志模型 +│ │ ├── automation.go # 自动化运维模型 +│ │ ├── cicd.go # CI/CD 模型 +│ │ ├── common.go # 通用模型 (Response/List/Page) +│ │ ├── k8s.go # K8s 模型 +│ │ ├── navigation.go # 导航模型 +│ │ ├── server.go # 服务器模型 +│ │ └── user.go # 用户模型 +│ ├── pkg/ +│ │ ├── database/ +│ │ │ └── database.go # 数据库连接 + AutoMigrate +│ │ └── logger/ +│ │ └── logger.go # Zap 日志 +│ ├── scripts/ +│ │ ├── init.sql # 初始化 SQL +│ │ └── mock.go # Mock 数据生成 +│ ├── config.yaml # 配置文件(SQLite 默认) +│ ├── go.mod # Go 模块定义 +│ └── Dockerfile # Docker 构建 +│ +├── docs/ # 开发文档 +│ ├── README.md # 文档索引 +│ ├── project-structure.md # 本文件 +│ ├── quick-start.md # 快速开始 +│ ├── api-reference.md # API 接口 +│ ├── database-schema.md # 数据库设计 +│ ├── development-guide.md # 开发规范 +│ ├── deployment.md # 部署指南 +│ ├── ROADMAP.md # 项目路线图 +│ ├── PDD_PLAN.md # 产品开发计划 +│ ├── PROJECT_SUMMARY.md # 项目概述 +│ └── BACKEND_SUMMARY.md # 后端摘要 +│ +├── run.sh # 一键启动脚本 +├── Makefile # 快捷命令 +├── .env.example # 环境变量模板 +├── .gitignore +└── README.md +``` + +## 技术栈 + +### 前端 + +| 技术 | 版本 | 用途 | +|------|------|------| +| React | 18+ | 前端框架 | +| TypeScript | 5+ | 类型安全 | +| Vite | 7+ | 构建工具 | +| Tailwind CSS | 3+ | 样式框架 | +| shadcn/ui | - | UI 组件库 | +| Zustand | - | 状态管理 | +| TanStack Query | - | 数据请求 | + +### 后端 + +| 技术 | 版本 | 用途 | +|------|------|------| +| Go | 1.21+ | 编程语言 | +| Gin | v1.10+ | Web 框架 | +| GORM | v1.25+ | ORM 框架 | +| JWT | v5 (golang-jwt) | 认证 | +| Viper | - | 配置管理 | +| Zap | - | 结构化日志 | +| SQLite/MySQL/PostgreSQL | - | 数据库 | + +## 端口分配 + +| 服务 | 端口 | 说明 | +|------|------|------| +| 前端 (Vite dev) | 5173 | http://localhost:5173 | +| 后端 (Gin) | 8080 | http://localhost:8080 | +| 健康检查 | 8080 | http://localhost:8080/health | diff --git a/docs/quick-start.md b/docs/quick-start.md new file mode 100644 index 0000000..c2a589a --- /dev/null +++ b/docs/quick-start.md @@ -0,0 +1,163 @@ +# 快速开始 + +## 环境要求 + +| 工具 | 最低版本 | 说明 | +|------|---------|------| +| Go | 1.21+ | 后端编译运行(需 CGO 支持 SQLite) | +| Node.js | 18+ | 前端构建 | +| npm | 9+ | 包管理 | +| gcc/clang | - | CGO 编译 SQLite 需要 | + +### 检查环境 + +```bash +go version # >= 1.21 +node -v # >= 18 +gcc --version # macOS 自带 clang +``` + +## 安装 + +### 1. 克隆仓库 + +```bash +git clone https://gitee.com/attacker/aiops.git +cd aiops +``` + +### 2. 安装依赖 + +```bash +make install +# 或手动安装 +cd backend && go mod download && cd .. +cd frontend cd app &&cd app && npm install && cd .. +``` + +### 3. 配置(可选) + +默认使用 SQLite,无需任何额外配置即可启动。 + +如需使用 MySQL 等其他数据库,编辑 `backend/config.yaml`: + +```yaml +database: + driver: mysql # 改为 mysql / postgres + host: localhost + port: 3306 + user: root + password: your_password + dbname: aiops +``` + +## 启动 + +### 方式一:一键启动(推荐) + +```bash +chmod +x run.sh +./run.sh start +``` + +输出示例: +``` +[INFO] ========== EZOps AIOps 启动脚本 ========== +[INFO] 正在启动后端服务... +[INFO] 编译 Go 程序... +[SUCCESS] 后端服务已启动 (PID: 12345, 端口: 8080) +[INFO] 正在启动前端服务... +[SUCCESS] 前端服务已启动 (PID: 12346, 端口: 5173) +[SUCCESS] ========== 所有服务已启动 ========== +[INFO] 前端地址: http://localhost:5173 +[INFO] 后端地址: http://localhost:8080 +[INFO] 健康检查: http://localhost:8080/health +``` + +### 方式二:分别启动 + +```bash +# 仅后端 +./run.sh backend + +# 仅前端 +./run.sh frontend +``` + +### 方式三:使用 Makefile + +```bash +make dev # 启动 +make status # 查看状态 +make stop # 停止 +``` + +## 访问 + +| 地址 | 说明 | +|------|------| +| http://localhost:5173 | 前端界面 | +| http://localhost:8080/health | 后端健康检查 | +| http://localhost:8080/version | 版本信息 | +| http://localhost:8080/api/v1/auth/login | 登录接口 | + +## 常用命令 + +```bash +./run.sh status # 查看服务状态 +./run.sh stop # 停止所有服务 +./run.sh restart # 重启所有服务 +./run.sh help # 查看帮助 + +make logs # 查看实时日志 +make build # 构建生产包 +make clean # 清理构建产物 +``` + +## 日志位置 + +| 服务 | 日志文件 | +|------|---------| +| 后端 | `backend/logs/server.log` | +| 前端 | `frontend/logs/dev.log` | +| 数据库 | `backend/data/aiops.db`(SQLite 文件) | + +## 数据库 + +默认使用 SQLite,数据库文件位于 `backend/data/aiops.db`,首次启动时自动创建表结构(AutoMigrate)。 + +### 重置数据库 + +```bash +./run.sh stop +rm -f backend/data/aiops.db +./run.sh start +``` + +## 常见问题 + +### Q: CGO 编译失败? + +SQLite 需要 CGO 支持。确保环境变量 `CGO_ENABLED=1`: + +```bash +export CGO_ENABLED=1 +``` + +### Q: Go 模块下载慢? + +设置国内代理: + +```bash +export GOPROXY=https://goproxy.cn,direct +``` + +### Q: 端口被占用? + +```bash +./run.sh stop # 先停止 +./run.sh start # 再启动 +# 或强制释放端口 +lsof -ti:8080 | xargs kill -9 +lsof -ti:5173 | xargs kill -9 +``` diff --git a/app/README.md b/frontend/README.md similarity index 100% rename from app/README.md rename to frontend/README.md diff --git a/app/components.json b/frontend/components.json similarity index 100% rename from app/components.json rename to frontend/components.json diff --git a/app/eslint.config.js b/frontend/eslint.config.js similarity index 100% rename from app/eslint.config.js rename to frontend/eslint.config.js diff --git a/app/index.html b/frontend/index.html similarity index 100% rename from app/index.html rename to frontend/index.html diff --git a/app/info.md b/frontend/info.md similarity index 100% rename from app/info.md rename to frontend/info.md diff --git a/app/package-lock.json b/frontend/package-lock.json similarity index 100% rename from app/package-lock.json rename to frontend/package-lock.json diff --git a/app/package.json b/frontend/package.json similarity index 100% rename from app/package.json rename to frontend/package.json diff --git a/app/postcss.config.js b/frontend/postcss.config.js similarity index 100% rename from app/postcss.config.js rename to frontend/postcss.config.js diff --git a/app/src/App.css b/frontend/src/App.css similarity index 100% rename from app/src/App.css rename to frontend/src/App.css diff --git a/app/src/App.tsx b/frontend/src/App.tsx similarity index 100% rename from app/src/App.tsx rename to frontend/src/App.tsx diff --git a/app/src/components/aiops/AIAlertAnalyzer.tsx b/frontend/src/components/aiops/AIAlertAnalyzer.tsx similarity index 100% rename from app/src/components/aiops/AIAlertAnalyzer.tsx rename to frontend/src/components/aiops/AIAlertAnalyzer.tsx diff --git a/app/src/components/aiops/AIFaultDiagnosis.tsx b/frontend/src/components/aiops/AIFaultDiagnosis.tsx similarity index 100% rename from app/src/components/aiops/AIFaultDiagnosis.tsx rename to frontend/src/components/aiops/AIFaultDiagnosis.tsx diff --git a/app/src/components/aiops/AIOpsAssistant.tsx b/frontend/src/components/aiops/AIOpsAssistant.tsx similarity index 100% rename from app/src/components/aiops/AIOpsAssistant.tsx rename to frontend/src/components/aiops/AIOpsAssistant.tsx diff --git a/app/src/components/aiops/AIOpsHub.tsx b/frontend/src/components/aiops/AIOpsHub.tsx similarity index 100% rename from app/src/components/aiops/AIOpsHub.tsx rename to frontend/src/components/aiops/AIOpsHub.tsx diff --git a/app/src/components/aiops/AIPredictiveAnalytics.tsx b/frontend/src/components/aiops/AIPredictiveAnalytics.tsx similarity index 100% rename from app/src/components/aiops/AIPredictiveAnalytics.tsx rename to frontend/src/components/aiops/AIPredictiveAnalytics.tsx diff --git a/app/src/components/aiops/AIScriptGenerator.tsx b/frontend/src/components/aiops/AIScriptGenerator.tsx similarity index 100% rename from app/src/components/aiops/AIScriptGenerator.tsx rename to frontend/src/components/aiops/AIScriptGenerator.tsx diff --git a/app/src/components/approval/ApprovalFlow.tsx b/frontend/src/components/approval/ApprovalFlow.tsx similarity index 100% rename from app/src/components/approval/ApprovalFlow.tsx rename to frontend/src/components/approval/ApprovalFlow.tsx diff --git a/app/src/components/audit/SQLAuditManager.tsx b/frontend/src/components/audit/SQLAuditManager.tsx similarity index 100% rename from app/src/components/audit/SQLAuditManager.tsx rename to frontend/src/components/audit/SQLAuditManager.tsx diff --git a/app/src/components/automation/AnsibleManager.tsx b/frontend/src/components/automation/AnsibleManager.tsx similarity index 100% rename from app/src/components/automation/AnsibleManager.tsx rename to frontend/src/components/automation/AnsibleManager.tsx diff --git a/app/src/components/automation/Automation.tsx b/frontend/src/components/automation/Automation.tsx similarity index 100% rename from app/src/components/automation/Automation.tsx rename to frontend/src/components/automation/Automation.tsx diff --git a/app/src/components/automation/CronJobManager.tsx b/frontend/src/components/automation/CronJobManager.tsx similarity index 100% rename from app/src/components/automation/CronJobManager.tsx rename to frontend/src/components/automation/CronJobManager.tsx diff --git a/app/src/components/cloud/CloudAccountManager.tsx b/frontend/src/components/cloud/CloudAccountManager.tsx similarity index 100% rename from app/src/components/cloud/CloudAccountManager.tsx rename to frontend/src/components/cloud/CloudAccountManager.tsx diff --git a/app/src/components/cloud/DomainManager.tsx b/frontend/src/components/cloud/DomainManager.tsx similarity index 100% rename from app/src/components/cloud/DomainManager.tsx rename to frontend/src/components/cloud/DomainManager.tsx diff --git a/app/src/components/cloud/PlatformManager.tsx b/frontend/src/components/cloud/PlatformManager.tsx similarity index 100% rename from app/src/components/cloud/PlatformManager.tsx rename to frontend/src/components/cloud/PlatformManager.tsx diff --git a/app/src/components/cloud/RegionManager.tsx b/frontend/src/components/cloud/RegionManager.tsx similarity index 100% rename from app/src/components/cloud/RegionManager.tsx rename to frontend/src/components/cloud/RegionManager.tsx diff --git a/app/src/components/dashboard/Dashboard.tsx b/frontend/src/components/dashboard/Dashboard.tsx similarity index 100% rename from app/src/components/dashboard/Dashboard.tsx rename to frontend/src/components/dashboard/Dashboard.tsx diff --git a/app/src/components/deploy/AppRelease.tsx b/frontend/src/components/deploy/AppRelease.tsx similarity index 100% rename from app/src/components/deploy/AppRelease.tsx rename to frontend/src/components/deploy/AppRelease.tsx diff --git a/app/src/components/deploy/PipelineManager.tsx b/frontend/src/components/deploy/PipelineManager.tsx similarity index 100% rename from app/src/components/deploy/PipelineManager.tsx rename to frontend/src/components/deploy/PipelineManager.tsx diff --git a/app/src/components/deploy/ReleaseManager.tsx b/frontend/src/components/deploy/ReleaseManager.tsx similarity index 100% rename from app/src/components/deploy/ReleaseManager.tsx rename to frontend/src/components/deploy/ReleaseManager.tsx diff --git a/app/src/components/k8s/K8sClusters.tsx b/frontend/src/components/k8s/K8sClusters.tsx similarity index 100% rename from app/src/components/k8s/K8sClusters.tsx rename to frontend/src/components/k8s/K8sClusters.tsx diff --git a/app/src/components/k8s/TopologyView.tsx b/frontend/src/components/k8s/TopologyView.tsx similarity index 100% rename from app/src/components/k8s/TopologyView.tsx rename to frontend/src/components/k8s/TopologyView.tsx diff --git a/app/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx similarity index 100% rename from app/src/components/layout/Header.tsx rename to frontend/src/components/layout/Header.tsx diff --git a/app/src/components/layout/MainLayout.tsx b/frontend/src/components/layout/MainLayout.tsx similarity index 100% rename from app/src/components/layout/MainLayout.tsx rename to frontend/src/components/layout/MainLayout.tsx diff --git a/app/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx similarity index 100% rename from app/src/components/layout/Sidebar.tsx rename to frontend/src/components/layout/Sidebar.tsx diff --git a/app/src/components/monitor/AlertManager.tsx b/frontend/src/components/monitor/AlertManager.tsx similarity index 100% rename from app/src/components/monitor/AlertManager.tsx rename to frontend/src/components/monitor/AlertManager.tsx diff --git a/app/src/components/monitor/alert-center/AlertCenter.tsx b/frontend/src/components/monitor/alert-center/AlertCenter.tsx similarity index 100% rename from app/src/components/monitor/alert-center/AlertCenter.tsx rename to frontend/src/components/monitor/alert-center/AlertCenter.tsx diff --git a/app/src/components/monitor/dashboard/GrafanaDashboard.tsx b/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx similarity index 100% rename from app/src/components/monitor/dashboard/GrafanaDashboard.tsx rename to frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx diff --git a/app/src/components/monitor/trace/TraceManager.tsx b/frontend/src/components/monitor/trace/TraceManager.tsx similarity index 100% rename from app/src/components/monitor/trace/TraceManager.tsx rename to frontend/src/components/monitor/trace/TraceManager.tsx diff --git a/app/src/components/navigation/NavigationPage.tsx b/frontend/src/components/navigation/NavigationPage.tsx similarity index 100% rename from app/src/components/navigation/NavigationPage.tsx rename to frontend/src/components/navigation/NavigationPage.tsx diff --git a/app/src/components/server/BatchImport.tsx b/frontend/src/components/server/BatchImport.tsx similarity index 100% rename from app/src/components/server/BatchImport.tsx rename to frontend/src/components/server/BatchImport.tsx diff --git a/app/src/components/server/CabinetView.tsx b/frontend/src/components/server/CabinetView.tsx similarity index 100% rename from app/src/components/server/CabinetView.tsx rename to frontend/src/components/server/CabinetView.tsx diff --git a/app/src/components/server/CloudServerImport.tsx b/frontend/src/components/server/CloudServerImport.tsx similarity index 100% rename from app/src/components/server/CloudServerImport.tsx rename to frontend/src/components/server/CloudServerImport.tsx diff --git a/app/src/components/server/ExcelServerImport.tsx b/frontend/src/components/server/ExcelServerImport.tsx similarity index 100% rename from app/src/components/server/ExcelServerImport.tsx rename to frontend/src/components/server/ExcelServerImport.tsx diff --git a/app/src/components/server/ResourceModelManager.tsx b/frontend/src/components/server/ResourceModelManager.tsx similarity index 100% rename from app/src/components/server/ResourceModelManager.tsx rename to frontend/src/components/server/ResourceModelManager.tsx diff --git a/app/src/components/server/ServerManager.tsx b/frontend/src/components/server/ServerManager.tsx similarity index 100% rename from app/src/components/server/ServerManager.tsx rename to frontend/src/components/server/ServerManager.tsx diff --git a/app/src/components/server/WebShell.tsx b/frontend/src/components/server/WebShell.tsx similarity index 100% rename from app/src/components/server/WebShell.tsx rename to frontend/src/components/server/WebShell.tsx diff --git a/app/src/components/system/SystemManager.tsx b/frontend/src/components/system/SystemManager.tsx similarity index 100% rename from app/src/components/system/SystemManager.tsx rename to frontend/src/components/system/SystemManager.tsx diff --git a/app/src/components/ui/accordion.tsx b/frontend/src/components/ui/accordion.tsx similarity index 100% rename from app/src/components/ui/accordion.tsx rename to frontend/src/components/ui/accordion.tsx diff --git a/app/src/components/ui/alert-dialog.tsx b/frontend/src/components/ui/alert-dialog.tsx similarity index 100% rename from app/src/components/ui/alert-dialog.tsx rename to frontend/src/components/ui/alert-dialog.tsx diff --git a/app/src/components/ui/alert.tsx b/frontend/src/components/ui/alert.tsx similarity index 100% rename from app/src/components/ui/alert.tsx rename to frontend/src/components/ui/alert.tsx diff --git a/app/src/components/ui/aspect-ratio.tsx b/frontend/src/components/ui/aspect-ratio.tsx similarity index 100% rename from app/src/components/ui/aspect-ratio.tsx rename to frontend/src/components/ui/aspect-ratio.tsx diff --git a/app/src/components/ui/avatar.tsx b/frontend/src/components/ui/avatar.tsx similarity index 100% rename from app/src/components/ui/avatar.tsx rename to frontend/src/components/ui/avatar.tsx diff --git a/app/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx similarity index 100% rename from app/src/components/ui/badge.tsx rename to frontend/src/components/ui/badge.tsx diff --git a/app/src/components/ui/breadcrumb.tsx b/frontend/src/components/ui/breadcrumb.tsx similarity index 100% rename from app/src/components/ui/breadcrumb.tsx rename to frontend/src/components/ui/breadcrumb.tsx diff --git a/app/src/components/ui/button-group.tsx b/frontend/src/components/ui/button-group.tsx similarity index 100% rename from app/src/components/ui/button-group.tsx rename to frontend/src/components/ui/button-group.tsx diff --git a/app/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx similarity index 100% rename from app/src/components/ui/button.tsx rename to frontend/src/components/ui/button.tsx diff --git a/app/src/components/ui/calendar.tsx b/frontend/src/components/ui/calendar.tsx similarity index 100% rename from app/src/components/ui/calendar.tsx rename to frontend/src/components/ui/calendar.tsx diff --git a/app/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx similarity index 100% rename from app/src/components/ui/card.tsx rename to frontend/src/components/ui/card.tsx diff --git a/app/src/components/ui/carousel.tsx b/frontend/src/components/ui/carousel.tsx similarity index 100% rename from app/src/components/ui/carousel.tsx rename to frontend/src/components/ui/carousel.tsx diff --git a/app/src/components/ui/chart.tsx b/frontend/src/components/ui/chart.tsx similarity index 100% rename from app/src/components/ui/chart.tsx rename to frontend/src/components/ui/chart.tsx diff --git a/app/src/components/ui/checkbox.tsx b/frontend/src/components/ui/checkbox.tsx similarity index 100% rename from app/src/components/ui/checkbox.tsx rename to frontend/src/components/ui/checkbox.tsx diff --git a/app/src/components/ui/collapsible.tsx b/frontend/src/components/ui/collapsible.tsx similarity index 100% rename from app/src/components/ui/collapsible.tsx rename to frontend/src/components/ui/collapsible.tsx diff --git a/app/src/components/ui/command.tsx b/frontend/src/components/ui/command.tsx similarity index 100% rename from app/src/components/ui/command.tsx rename to frontend/src/components/ui/command.tsx diff --git a/app/src/components/ui/context-menu.tsx b/frontend/src/components/ui/context-menu.tsx similarity index 100% rename from app/src/components/ui/context-menu.tsx rename to frontend/src/components/ui/context-menu.tsx diff --git a/app/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx similarity index 100% rename from app/src/components/ui/dialog.tsx rename to frontend/src/components/ui/dialog.tsx diff --git a/app/src/components/ui/drawer.tsx b/frontend/src/components/ui/drawer.tsx similarity index 100% rename from app/src/components/ui/drawer.tsx rename to frontend/src/components/ui/drawer.tsx diff --git a/app/src/components/ui/dropdown-menu.tsx b/frontend/src/components/ui/dropdown-menu.tsx similarity index 100% rename from app/src/components/ui/dropdown-menu.tsx rename to frontend/src/components/ui/dropdown-menu.tsx diff --git a/app/src/components/ui/empty.tsx b/frontend/src/components/ui/empty.tsx similarity index 100% rename from app/src/components/ui/empty.tsx rename to frontend/src/components/ui/empty.tsx diff --git a/app/src/components/ui/field.tsx b/frontend/src/components/ui/field.tsx similarity index 100% rename from app/src/components/ui/field.tsx rename to frontend/src/components/ui/field.tsx diff --git a/app/src/components/ui/form.tsx b/frontend/src/components/ui/form.tsx similarity index 100% rename from app/src/components/ui/form.tsx rename to frontend/src/components/ui/form.tsx diff --git a/app/src/components/ui/hover-card.tsx b/frontend/src/components/ui/hover-card.tsx similarity index 100% rename from app/src/components/ui/hover-card.tsx rename to frontend/src/components/ui/hover-card.tsx diff --git a/app/src/components/ui/input-group.tsx b/frontend/src/components/ui/input-group.tsx similarity index 100% rename from app/src/components/ui/input-group.tsx rename to frontend/src/components/ui/input-group.tsx diff --git a/app/src/components/ui/input-otp.tsx b/frontend/src/components/ui/input-otp.tsx similarity index 100% rename from app/src/components/ui/input-otp.tsx rename to frontend/src/components/ui/input-otp.tsx diff --git a/app/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx similarity index 100% rename from app/src/components/ui/input.tsx rename to frontend/src/components/ui/input.tsx diff --git a/app/src/components/ui/item.tsx b/frontend/src/components/ui/item.tsx similarity index 100% rename from app/src/components/ui/item.tsx rename to frontend/src/components/ui/item.tsx diff --git a/app/src/components/ui/kbd.tsx b/frontend/src/components/ui/kbd.tsx similarity index 100% rename from app/src/components/ui/kbd.tsx rename to frontend/src/components/ui/kbd.tsx diff --git a/app/src/components/ui/label.tsx b/frontend/src/components/ui/label.tsx similarity index 100% rename from app/src/components/ui/label.tsx rename to frontend/src/components/ui/label.tsx diff --git a/app/src/components/ui/menubar.tsx b/frontend/src/components/ui/menubar.tsx similarity index 100% rename from app/src/components/ui/menubar.tsx rename to frontend/src/components/ui/menubar.tsx diff --git a/app/src/components/ui/navigation-menu.tsx b/frontend/src/components/ui/navigation-menu.tsx similarity index 100% rename from app/src/components/ui/navigation-menu.tsx rename to frontend/src/components/ui/navigation-menu.tsx diff --git a/app/src/components/ui/pagination.tsx b/frontend/src/components/ui/pagination.tsx similarity index 100% rename from app/src/components/ui/pagination.tsx rename to frontend/src/components/ui/pagination.tsx diff --git a/app/src/components/ui/popover.tsx b/frontend/src/components/ui/popover.tsx similarity index 100% rename from app/src/components/ui/popover.tsx rename to frontend/src/components/ui/popover.tsx diff --git a/app/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx similarity index 100% rename from app/src/components/ui/progress.tsx rename to frontend/src/components/ui/progress.tsx diff --git a/app/src/components/ui/radio-group.tsx b/frontend/src/components/ui/radio-group.tsx similarity index 100% rename from app/src/components/ui/radio-group.tsx rename to frontend/src/components/ui/radio-group.tsx diff --git a/app/src/components/ui/resizable.tsx b/frontend/src/components/ui/resizable.tsx similarity index 100% rename from app/src/components/ui/resizable.tsx rename to frontend/src/components/ui/resizable.tsx diff --git a/app/src/components/ui/scroll-area.tsx b/frontend/src/components/ui/scroll-area.tsx similarity index 100% rename from app/src/components/ui/scroll-area.tsx rename to frontend/src/components/ui/scroll-area.tsx diff --git a/app/src/components/ui/select.tsx b/frontend/src/components/ui/select.tsx similarity index 100% rename from app/src/components/ui/select.tsx rename to frontend/src/components/ui/select.tsx diff --git a/app/src/components/ui/separator.tsx b/frontend/src/components/ui/separator.tsx similarity index 100% rename from app/src/components/ui/separator.tsx rename to frontend/src/components/ui/separator.tsx diff --git a/app/src/components/ui/sheet.tsx b/frontend/src/components/ui/sheet.tsx similarity index 100% rename from app/src/components/ui/sheet.tsx rename to frontend/src/components/ui/sheet.tsx diff --git a/app/src/components/ui/sidebar.tsx b/frontend/src/components/ui/sidebar.tsx similarity index 100% rename from app/src/components/ui/sidebar.tsx rename to frontend/src/components/ui/sidebar.tsx diff --git a/app/src/components/ui/skeleton.tsx b/frontend/src/components/ui/skeleton.tsx similarity index 100% rename from app/src/components/ui/skeleton.tsx rename to frontend/src/components/ui/skeleton.tsx diff --git a/app/src/components/ui/slider.tsx b/frontend/src/components/ui/slider.tsx similarity index 100% rename from app/src/components/ui/slider.tsx rename to frontend/src/components/ui/slider.tsx diff --git a/app/src/components/ui/sonner.tsx b/frontend/src/components/ui/sonner.tsx similarity index 100% rename from app/src/components/ui/sonner.tsx rename to frontend/src/components/ui/sonner.tsx diff --git a/app/src/components/ui/spinner.tsx b/frontend/src/components/ui/spinner.tsx similarity index 100% rename from app/src/components/ui/spinner.tsx rename to frontend/src/components/ui/spinner.tsx diff --git a/app/src/components/ui/switch.tsx b/frontend/src/components/ui/switch.tsx similarity index 100% rename from app/src/components/ui/switch.tsx rename to frontend/src/components/ui/switch.tsx diff --git a/app/src/components/ui/table.tsx b/frontend/src/components/ui/table.tsx similarity index 100% rename from app/src/components/ui/table.tsx rename to frontend/src/components/ui/table.tsx diff --git a/app/src/components/ui/tabs.tsx b/frontend/src/components/ui/tabs.tsx similarity index 100% rename from app/src/components/ui/tabs.tsx rename to frontend/src/components/ui/tabs.tsx diff --git a/app/src/components/ui/textarea.tsx b/frontend/src/components/ui/textarea.tsx similarity index 100% rename from app/src/components/ui/textarea.tsx rename to frontend/src/components/ui/textarea.tsx diff --git a/app/src/components/ui/toggle-group.tsx b/frontend/src/components/ui/toggle-group.tsx similarity index 100% rename from app/src/components/ui/toggle-group.tsx rename to frontend/src/components/ui/toggle-group.tsx diff --git a/app/src/components/ui/toggle.tsx b/frontend/src/components/ui/toggle.tsx similarity index 100% rename from app/src/components/ui/toggle.tsx rename to frontend/src/components/ui/toggle.tsx diff --git a/app/src/components/ui/tooltip.tsx b/frontend/src/components/ui/tooltip.tsx similarity index 100% rename from app/src/components/ui/tooltip.tsx rename to frontend/src/components/ui/tooltip.tsx diff --git a/app/src/hooks/use-mobile.ts b/frontend/src/hooks/use-mobile.ts similarity index 100% rename from app/src/hooks/use-mobile.ts rename to frontend/src/hooks/use-mobile.ts diff --git a/app/src/index.css b/frontend/src/index.css similarity index 100% rename from app/src/index.css rename to frontend/src/index.css diff --git a/app/src/lib/utils.ts b/frontend/src/lib/utils.ts similarity index 100% rename from app/src/lib/utils.ts rename to frontend/src/lib/utils.ts diff --git a/app/src/main.tsx b/frontend/src/main.tsx similarity index 100% rename from app/src/main.tsx rename to frontend/src/main.tsx diff --git a/app/src/mocks/data.ts b/frontend/src/mocks/data.ts similarity index 100% rename from app/src/mocks/data.ts rename to frontend/src/mocks/data.ts diff --git a/app/src/services/api.ts b/frontend/src/services/api.ts similarity index 100% rename from app/src/services/api.ts rename to frontend/src/services/api.ts diff --git a/app/src/store/index.ts b/frontend/src/store/index.ts similarity index 100% rename from app/src/store/index.ts rename to frontend/src/store/index.ts diff --git a/app/src/types/index.ts b/frontend/src/types/index.ts similarity index 100% rename from app/src/types/index.ts rename to frontend/src/types/index.ts diff --git a/app/tailwind.config.js b/frontend/tailwind.config.js similarity index 100% rename from app/tailwind.config.js rename to frontend/tailwind.config.js diff --git a/app/tsconfig.app.json b/frontend/tsconfig.app.json similarity index 100% rename from app/tsconfig.app.json rename to frontend/tsconfig.app.json diff --git a/app/tsconfig.json b/frontend/tsconfig.json similarity index 100% rename from app/tsconfig.json rename to frontend/tsconfig.json diff --git a/app/tsconfig.node.json b/frontend/tsconfig.node.json similarity index 100% rename from app/tsconfig.node.json rename to frontend/tsconfig.node.json diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..908fb1b --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,33 @@ +import path from "path" +import react from "@vitejs/plugin-react" +import { defineConfig } from "vite" +import { inspectAttr } from 'kimi-plugin-inspect-react' + +// https://vite.dev/config/ +export default defineConfig({ + base: './', + plugins: [inspectAttr(), react()], + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, + server: { + port: 5173, + host: true, + proxy: { + "/api": { + target: "http://localhost:8080", + changeOrigin: true, + }, + "/health": { + target: "http://localhost:8080", + changeOrigin: true, + }, + "/version": { + target: "http://localhost:8080", + changeOrigin: true, + }, + }, + }, +}) diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..e532108 --- /dev/null +++ b/run.sh @@ -0,0 +1,288 @@ +#!/bin/bash +# EZOps AIOps 启动脚本 +# 一键启动前后端服务,支持日志轮转 +# 用法: ./run.sh [start|stop|restart|status|frontend|backend|help] + +set -e + +# ============================================================ +# 配置区 +# ============================================================ +PROJECT_NAME="EZOps AIOps" +BACKEND_PORT=8080 +FRONTEND_PORT=5173 +BACKEND_DIR="backend" +FRONTEND_DIR="frontend" +BACKEND_BIN="aiops-server" + +# 日志配置 +MAX_LOG_FILES=3 +MAX_LOG_SIZE=$((50 * 1024 * 1024)) # 50MB + +# ============================================================ +# 通用函数 +# ============================================================ +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +info() { echo -e "${BLUE}[INFO]${NC} $1"; } +success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } +error() { echo -e "${RED}[ERROR]${NC} $1"; } + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# PID 文件 +BACKEND_PID_FILE="$SCRIPT_DIR/$BACKEND_DIR/.backend.pid" +FRONTEND_PID_FILE="$SCRIPT_DIR/$FRONTEND_DIR/.frontend.pid" + +# 检查端口是否被占用 +check_port() { + lsof -ti:$1 2>/dev/null | head -1 +} + +# 释放端口 +kill_port() { + local pids=$(lsof -ti:$1 2>/dev/null || echo "") + if [ -n "$pids" ]; then + info "释放端口 $1..." + echo "$pids" | xargs kill -9 2>/dev/null || true + sleep 1 + fi +} + +# 日志轮转(仅启动时调用) +rotate_log() { + local log_file=$1 + [ ! -f "$log_file" ] && return 0 + + local file_size=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo "0") + if [ "$file_size" -gt "$MAX_LOG_SIZE" ]; then + [ -f "${log_file}.${MAX_LOG_FILES}" ] && rm -f "${log_file}.${MAX_LOG_FILES}" + for i in $(seq $((MAX_LOG_FILES - 1)) -1 1); do + [ -f "${log_file}.${i}" ] && mv "${log_file}.${i}" "${log_file}.$((i + 1))" + done + mv "$log_file" "${log_file}.1" + info "日志已轮转: $log_file" + fi +} + +# ============================================================ +# 后端服务 +# ============================================================ +start_backend() { + info "正在启动后端服务..." + cd "$SCRIPT_DIR/$BACKEND_DIR" + mkdir -p logs data + rotate_log "logs/server.log" + + # 释放被占用的端口 + kill_port $BACKEND_PORT + + export PORT=$BACKEND_PORT + export GIN_MODE=debug + + # 确保使用国内代理 + export GOPROXY=https://goproxy.cn,direct + + # 编译并运行 + info "编译 Go 程序..." + if ! go build -o "$BACKEND_BIN" cmd/main.go; then + error "Go 编译失败" + return 1 + fi + + nohup ./"$BACKEND_BIN" >> logs/server.log 2>&1 & + echo $! > "$BACKEND_PID_FILE" + + # 等待服务启动 + local retries=0 + while ! curl -s "http://localhost:$BACKEND_PORT/health" > /dev/null 2>&1; do + retries=$((retries + 1)) + if [ $retries -ge 30 ]; then + error "后端服务启动超时,请检查日志: $BACKEND_DIR/logs/server.log" + tail -20 "$BACKEND_DIR/logs/server.log" 2>/dev/null + return 1 + fi + sleep 1 + done + + success "后端服务已启动 (PID: $(cat "$BACKEND_PID_FILE"), 端口: $BACKEND_PORT)" + return 0 +} + +stop_backend() { + if [ -f "$BACKEND_PID_FILE" ]; then + local pid=$(cat "$BACKEND_PID_FILE" 2>/dev/null || echo "") + if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then + kill "$pid" 2>/dev/null || true + success "后端服务已停止" + fi + rm -f "$BACKEND_PID_FILE" + fi + kill_port $BACKEND_PORT +} + +# ============================================================ +# 前端服务 +# ============================================================ +start_frontend() { + info "正在启动前端服务..." + cd "$SCRIPT_DIR/$FRONTEND_DIR" + mkdir -p logs + rotate_log "logs/dev.log" + + kill_port $FRONTEND_PORT + + if [ ! -d "node_modules" ]; then + warning "node_modules 不存在,正在安装依赖..." + npm install + fi + + nohup npm run dev >> logs/dev.log 2>&1 & + echo $! > "$FRONTEND_PID_FILE" + + # 等待服务启动 + local retries=0 + while ! curl -s "http://localhost:$FRONTEND_PORT" > /dev/null 2>&1; do + retries=$((retries + 1)) + if [ $retries -ge 30 ]; then + error "前端服务启动超时,请检查日志: $FRONTEND_DIR/logs/dev.log" + tail -20 "$FRONTEND_DIR/logs/dev.log" 2>/dev/null + return 1 + fi + sleep 1 + done + + success "前端服务已启动 (PID: $(cat "$FRONTEND_PID_FILE"), 端口: $FRONTEND_PORT)" + return 0 +} + +stop_frontend() { + if [ -f "$FRONTEND_PID_FILE" ]; then + local pid=$(cat "$FRONTEND_PID_FILE" 2>/dev/null || echo "") + if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then + kill "$pid" 2>/dev/null || true + success "前端服务已停止" + fi + rm -f "$FRONTEND_PID_FILE" + fi + kill_port $FRONTEND_PORT +} + +# ============================================================ +# 命令 +# ============================================================ +stop() { + info "正在停止服务..." + stop_backend + stop_frontend + info "所有服务已停止" +} + +show_status() { + info "服务状态:" + if [ -n "$(check_port $BACKEND_PORT)" ]; then + success "后端服务: 运行中 (PID: $(check_port $BACKEND_PORT), 端口: $BACKEND_PORT)" + else + warning "后端服务: 未运行" + fi + + if [ -n "$(check_port $FRONTEND_PORT)" ]; then + success "前端服务: 运行中 (PID: $(check_port $FRONTEND_PORT), 端口: $FRONTEND_PORT)" + else + warning "前端服务: 未运行" + fi +} + +show_help() { + echo "$PROJECT_NAME 启动脚本" + echo "" + echo "用法: ./run.sh [命令]" + echo "" + echo "命令:" + echo " start 启动前后端服务 (默认)" + echo " stop 停止前后端服务" + echo " restart 重启前后端服务" + echo " backend 仅启动后端服务" + echo " frontend 仅启动前端服务" + echo " status 查看服务状态" + echo " help 显示帮助信息" + echo "" + echo "示例:" + echo " ./run.sh # 启动所有服务" + echo " ./run.sh stop # 停止服务" + echo " ./run.sh restart # 重启服务" + echo " ./run.sh backend # 仅启动后端" +} + +# ============================================================ +# 主入口 +# ============================================================ +main() { + local command=${1:-start} + + case "$command" in + start) + info "========== $PROJECT_NAME 启动脚本 ==========" + + # 检查依赖 + if ! command -v go &> /dev/null; then + error "Go 未安装,请先安装 Go" + exit 1 + fi + if ! command -v npm &> /dev/null; then + error "npm 未安装,请先安装 Node.js" + exit 1 + fi + + if start_backend && start_frontend; then + echo "" + success "========== 所有服务已启动 ==========" + info "前端地址: http://localhost:$FRONTEND_PORT" + info "后端地址: http://localhost:$BACKEND_PORT" + info "健康检查: http://localhost:$BACKEND_PORT/health" + echo "" + info "使用 './run.sh stop' 停止服务" + else + error "服务启动失败" + exit 1 + fi + ;; + stop) + stop + ;; + restart) + stop + sleep 2 + main start + ;; + backend) + info "========== $PROJECT_NAME (仅后端) ==========" + start_backend + show_status + ;; + frontend) + info "========== $PROJECT_NAME (仅前端) ==========" + start_frontend + show_status + ;; + status) + show_status + ;; + help|--help|-h) + show_help + ;; + *) + error "未知命令: $command" + show_help + exit 1 + ;; + esac +} + +main "$@"