feat: 后端认证和工具模块更新
- main.go: 添加 Swagger 文档、初始化默认管理员 - 认证模块: 完善用户角色管理 - 新增工具模块: tool_handler, tool_repo, tool_service, tool model - 更新 go.mod 依赖 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,57 @@ package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"x-agents/server/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// maskEmail 邮箱脱敏,只显示前后各1个字符
|
||||
func maskEmail(email string) string {
|
||||
if email == "" {
|
||||
return ""
|
||||
}
|
||||
parts := strings.Split(email, "@")
|
||||
if len(parts) != 2 {
|
||||
return "***"
|
||||
}
|
||||
local := parts[0]
|
||||
domain := parts[1]
|
||||
if len(local) <= 2 {
|
||||
local = "**"
|
||||
} else {
|
||||
local = string(local[0]) + "***" + string(local[len(local)-1])
|
||||
}
|
||||
if len(domain) <= 2 {
|
||||
domain = "***"
|
||||
} else {
|
||||
domain = string(domain[0]) + "***" + string(domain[len(domain)-1])
|
||||
}
|
||||
return local + "@" + domain
|
||||
}
|
||||
|
||||
// @title X-Agents API
|
||||
// @version 1.0
|
||||
// @description X-Agents 后端 API 文档
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
|
||||
// @contact.name API Support
|
||||
// @contact.url http://www.example.com/support
|
||||
// @contact.email support@example.com
|
||||
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
// @host localhost:8080
|
||||
// @BasePath /
|
||||
|
||||
// @securityDefinitions.apikey BearerAuth
|
||||
// @in header
|
||||
// @name Authorization
|
||||
// @description Type "Bearer" followed by a space and JWT token.
|
||||
|
||||
type AuthHandler struct {
|
||||
authService *service.AuthService
|
||||
}
|
||||
@@ -26,7 +71,23 @@ type LoginResponse struct {
|
||||
User interface{} `json:"user"`
|
||||
}
|
||||
|
||||
// Login 处理登录
|
||||
// RegisterRequest 注册请求
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// @Summary 用户登录
|
||||
// @Description 用户登录并获取 JWT Token
|
||||
// @Tags 认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body LoginRequest true "登录请求"
|
||||
// @Success 200 {object} LoginResponse
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Router /auth/login [post]
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
var req LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -54,13 +115,17 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// Register 处理注册
|
||||
// @Summary 用户注册
|
||||
// @Description 新用户注册账号
|
||||
// @Tags 认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param RegisterRequest body handler.RegisterRequest true "注册请求"
|
||||
// @Success 201 {object} map[string]interface{}
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Router /auth/register [post]
|
||||
func (h *AuthHandler) Register(c *gin.Context) {
|
||||
var req struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
var req RegisterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -75,11 +140,19 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"email": maskEmail(user.Email),
|
||||
})
|
||||
}
|
||||
|
||||
// GetCurrentUser 获取当前登录用户信息
|
||||
// @Summary 获取当前用户信息
|
||||
// @Description 获取已登录用户的详细信息
|
||||
// @Tags 认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Router /auth/me [get]
|
||||
func (h *AuthHandler) GetCurrentUser(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
@@ -96,9 +169,73 @@ func (h *AuthHandler) GetCurrentUser(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"email": maskEmail(user.Email),
|
||||
"role_id": user.RoleID,
|
||||
"is_active": user.IsActive,
|
||||
"created_at": user.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 获取所有用户
|
||||
// @Description 获取所有用户列表(需要管理员权限)
|
||||
// @Tags 用户管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Router /user/list [get]
|
||||
func (h *AuthHandler) ListUsers(c *gin.Context) {
|
||||
users, err := h.authService.GetAllUsers()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var userList []gin.H
|
||||
for _, user := range users {
|
||||
userList = append(userList, gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": maskEmail(user.Email),
|
||||
"role_id": user.RoleID,
|
||||
"is_active": user.IsActive,
|
||||
"created_at": user.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"list": userList})
|
||||
}
|
||||
|
||||
// @Summary 获取用户详情
|
||||
// @Description 根据ID获取用户详情
|
||||
// @Tags 用户管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param id path string true "用户ID"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Router /user/{id} [get]
|
||||
func (h *AuthHandler) GetUserByID(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "user id is required"})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.authService.GetUserByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": maskEmail(user.Email),
|
||||
"role_id": user.RoleID,
|
||||
"is_active": user.IsActive,
|
||||
"created_at": user.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
166
server/internal/handler/tool_handler.go
Normal file
166
server/internal/handler/tool_handler.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"x-agents/server/internal/model"
|
||||
"x-agents/server/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ToolHandler 工具处理器
|
||||
type ToolHandler struct {
|
||||
toolService *service.ToolService
|
||||
}
|
||||
|
||||
// NewToolHandler 创建工具处理器
|
||||
func NewToolHandler(toolService *service.ToolService) *ToolHandler {
|
||||
return &ToolHandler{toolService: toolService}
|
||||
}
|
||||
|
||||
// List 获取工具列表
|
||||
// @Summary 获取工具列表
|
||||
// @Description 获取所有工具列表,支持按分类和状态筛选
|
||||
// @Tags 工具管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param category query string false "工具分类"
|
||||
// @Param status query string false "工具状态"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /tool/list [get]
|
||||
func (h *ToolHandler) List(c *gin.Context) {
|
||||
category := c.Query("category")
|
||||
status := c.Query("status")
|
||||
|
||||
tools, err := h.toolService.GetTools(category, status)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"list": tools, "total": len(tools)})
|
||||
}
|
||||
|
||||
// Sync 手动同步工具
|
||||
// @Summary 手动同步工具
|
||||
// @Description 从代码中的默认配置同步工具到数据库
|
||||
// @Tags 工具管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /tool/sync [get]
|
||||
func (h *ToolHandler) Sync(c *gin.Context) {
|
||||
if err := h.toolService.InitDefaultTools(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
tools, _ := h.toolService.GetTools("", "")
|
||||
c.JSON(http.StatusOK, gin.H{"message": "tools synced", "count": len(tools)})
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取工具
|
||||
// @Summary 获取工具详情
|
||||
// @Description 根据ID获取工具详情
|
||||
// @Tags 工具管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "工具ID"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /tool/{id} [get]
|
||||
func (h *ToolHandler) GetByID(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tool id is required"})
|
||||
return
|
||||
}
|
||||
|
||||
tool, err := h.toolService.GetToolByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "tool not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"tool": tool})
|
||||
}
|
||||
|
||||
// Create 创建工具
|
||||
// @Summary 创建工具
|
||||
// @Description 创建新的工具
|
||||
// @Tags 工具管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param tool body model.Tool true "工具信息"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /tool/add [post]
|
||||
func (h *ToolHandler) Create(c *gin.Context) {
|
||||
var tool model.Tool
|
||||
if err := c.ShouldBindJSON(&tool); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.toolService.CreateTool(&tool); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "tool created", "tool": tool})
|
||||
}
|
||||
|
||||
// Update 更新工具
|
||||
// @Summary 更新工具
|
||||
// @Description 更新工具信息
|
||||
// @Tags 工具管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "工具ID"
|
||||
// @Param tool body model.Tool true "工具信息"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /tool/{id} [put]
|
||||
func (h *ToolHandler) Update(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tool id is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var tool model.Tool
|
||||
if err := c.ShouldBindJSON(&tool); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
tool.ID = id
|
||||
if err := h.toolService.UpdateTool(&tool); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "tool updated"})
|
||||
}
|
||||
|
||||
// Delete 删除工具
|
||||
// @Summary 删除工具
|
||||
// @Description 删除工具
|
||||
// @Tags 工具管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "工具ID"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /tool/{id} [delete]
|
||||
func (h *ToolHandler) Delete(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tool id is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.toolService.DeleteTool(id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "tool deleted"})
|
||||
}
|
||||
31
server/internal/model/tool.go
Normal file
31
server/internal/model/tool.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Tool 工具
|
||||
type Tool struct {
|
||||
ID string `json:"id" gorm:"primaryKey"`
|
||||
Name string `json:"name" gorm:"uniqueIndex;size:100;not null"`
|
||||
Description string `json:"description" gorm:"type:text"`
|
||||
Category string `json:"category" gorm:"size:50;not null"`
|
||||
Provider string `json:"provider" gorm:"size:100"`
|
||||
SecurityLevel string `json:"security_level" gorm:"size:20;default:'safe'"`
|
||||
RequireApproval bool `json:"require_approval" gorm:"default:false"`
|
||||
Parameters string `json:"parameters" gorm:"type:text"` // JSON格式存储
|
||||
Status string `json:"status" gorm:"size:20;default:'active'"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// BeforeCreate 创建前自动生成ID
|
||||
func (t *Tool) BeforeCreate(tx *gorm.DB) error {
|
||||
if t.ID == "" {
|
||||
t.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -16,11 +17,11 @@ const (
|
||||
|
||||
// Role 角色
|
||||
type Role struct {
|
||||
ID string `json:"id" gorm:"primaryKey"`
|
||||
Name string `json:"name" gorm:"uniqueIndex"`
|
||||
Permissions []PermissionLevel `json:"permissions" gorm:"type:int[]"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID string `json:"id" gorm:"primaryKey"`
|
||||
Name string `json:"name" gorm:"uniqueIndex"`
|
||||
Permissions string `json:"permissions" gorm:"type:text"` // 存储 JSON 格式的权限数组
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// User 用户
|
||||
@@ -41,8 +42,13 @@ func (u *User) HasPermission(level PermissionLevel) bool {
|
||||
if u.Role == nil {
|
||||
return false
|
||||
}
|
||||
for _, p := range u.Role.Permissions {
|
||||
if p >= level {
|
||||
// 解析 JSON 格式的权限
|
||||
var perms []int
|
||||
if err := json.Unmarshal([]byte(u.Role.Permissions), &perms); err != nil {
|
||||
return false
|
||||
}
|
||||
for _, p := range perms {
|
||||
if PermissionLevel(p) >= level {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
83
server/internal/repository/tool_repo.go
Normal file
83
server/internal/repository/tool_repo.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"x-agents/server/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ToolRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewToolRepository(db *gorm.DB) *ToolRepository {
|
||||
return &ToolRepository{db: db}
|
||||
}
|
||||
|
||||
// DB 获取数据库连接
|
||||
func (r *ToolRepository) DB() *gorm.DB {
|
||||
return r.db
|
||||
}
|
||||
|
||||
func (r *ToolRepository) Create(tool *model.Tool) error {
|
||||
return r.db.Create(tool).Error
|
||||
}
|
||||
|
||||
func (r *ToolRepository) FindAll() ([]model.Tool, error) {
|
||||
var tools []model.Tool
|
||||
err := r.db.Order("category, name").Find(&tools).Error
|
||||
return tools, err
|
||||
}
|
||||
|
||||
func (r *ToolRepository) FindByCategory(category string) ([]model.Tool, error) {
|
||||
var tools []model.Tool
|
||||
err := r.db.Where("category = ?", category).Order("name").Find(&tools).Error
|
||||
return tools, err
|
||||
}
|
||||
|
||||
func (r *ToolRepository) FindByID(id string) (*model.Tool, error) {
|
||||
var tool model.Tool
|
||||
err := r.db.First(&tool, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tool, nil
|
||||
}
|
||||
|
||||
func (r *ToolRepository) Update(tool *model.Tool) error {
|
||||
return r.db.Save(tool).Error
|
||||
}
|
||||
|
||||
func (r *ToolRepository) Delete(id string) error {
|
||||
return r.db.Delete(&model.Tool{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
func (r *ToolRepository) Count() (int64, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.Tool{}).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// UpsertBatch 批量upsert工具
|
||||
func (r *ToolRepository) UpsertBatch(tools []model.Tool) error {
|
||||
for _, tool := range tools {
|
||||
var existing model.Tool
|
||||
err := r.db.First(&existing, "name = ?", tool.Name).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
if err := r.db.Create(&tool).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
existing.Description = tool.Description
|
||||
existing.Category = tool.Category
|
||||
existing.Provider = tool.Provider
|
||||
existing.Status = tool.Status
|
||||
if err := r.db.Save(&existing).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"x-agents/server/internal/model"
|
||||
@@ -123,10 +128,11 @@ func (s *AuthService) Register(username, password, email string) (*model.User, e
|
||||
role, err := s.userRepo.FindRoleByID(user.RoleID)
|
||||
if err != nil {
|
||||
// 创建默认角色
|
||||
perms, _ := json.Marshal([]int{int(model.PermissionRead), int(model.PermissionWrite)})
|
||||
role = &model.Role{
|
||||
ID: "user",
|
||||
Name: "user",
|
||||
Permissions: []model.PermissionLevel{model.PermissionRead, model.PermissionWrite},
|
||||
Permissions: string(perms),
|
||||
}
|
||||
s.userRepo.CreateRole(role)
|
||||
user.Role = role
|
||||
@@ -136,10 +142,74 @@ func (s *AuthService) Register(username, password, email string) (*model.User, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建用户工作空间目录
|
||||
if err := s.createUserWorkspace(username); err != nil {
|
||||
// 工作空间创建失败不影响注册成功,仅记录日志
|
||||
println("Warning: failed to create user workspace:", err.Error())
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// createUserWorkspace 创建用户工作空间目录
|
||||
func (s *AuthService) createUserWorkspace(username string) error {
|
||||
// 获取当前可执行文件所在目录
|
||||
execPath, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 尝试多种方式获取项目根目录
|
||||
projectRoot := execPath
|
||||
|
||||
// 方式1: 如果当前目录名为 server,向上找一级
|
||||
baseName := filepath.Base(execPath)
|
||||
// 处理 Windows 和 Unix 风格路径
|
||||
if baseName == "server" || strings.HasSuffix(execPath, "/server") || strings.HasSuffix(execPath, "\\server") {
|
||||
projectRoot = filepath.Dir(execPath)
|
||||
}
|
||||
|
||||
// 方式2: 尝试向上查找包含 .git 或 package.json 的目录
|
||||
if _, err := os.Stat(filepath.Join(projectRoot, ".git")); os.IsNotExist(err) {
|
||||
// 继续向上查找
|
||||
for i := 0; i < 3; i++ {
|
||||
parent := filepath.Dir(projectRoot)
|
||||
if parent == projectRoot {
|
||||
break
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(parent, ".git")); err == nil {
|
||||
projectRoot = parent
|
||||
break
|
||||
}
|
||||
projectRoot = parent
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[Workspace] Creating workspace at: %s", filepath.Join(projectRoot, "account", username))
|
||||
|
||||
// 创建 account 目录和用户目录
|
||||
workspacePath := filepath.Join(projectRoot, "account", username)
|
||||
if err := os.MkdirAll(workspacePath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建子目录
|
||||
subDirs := []string{"projects", "files", "temp"}
|
||||
for _, dir := range subDirs {
|
||||
if err := os.MkdirAll(filepath.Join(workspacePath, dir), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserByID 根据ID获取用户
|
||||
func (s *AuthService) GetUserByID(id string) (*model.User, error) {
|
||||
return s.userRepo.FindByID(id)
|
||||
}
|
||||
|
||||
// GetAllUsers 获取所有用户
|
||||
func (s *AuthService) GetAllUsers() ([]model.User, error) {
|
||||
return s.userRepo.FindAll()
|
||||
}
|
||||
|
||||
219
server/internal/service/tool_service.go
Normal file
219
server/internal/service/tool_service.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"log"
|
||||
"x-agents/server/internal/model"
|
||||
"x-agents/server/internal/repository"
|
||||
)
|
||||
|
||||
type ToolService struct {
|
||||
toolRepo *repository.ToolRepository
|
||||
}
|
||||
|
||||
func NewToolService(toolRepo *repository.ToolRepository) *ToolService {
|
||||
return &ToolService{toolRepo: toolRepo}
|
||||
}
|
||||
|
||||
func (s *ToolService) GetAllTools() ([]model.Tool, error) {
|
||||
return s.toolRepo.FindAll()
|
||||
}
|
||||
|
||||
func (s *ToolService) GetToolsByCategory(category string) ([]model.Tool, error) {
|
||||
return s.toolRepo.FindByCategory(category)
|
||||
}
|
||||
|
||||
func (s *ToolService) GetToolByID(id string) (*model.Tool, error) {
|
||||
return s.toolRepo.FindByID(id)
|
||||
}
|
||||
|
||||
// GetTools 根据条件获取工具列表
|
||||
func (s *ToolService) GetTools(category string, status string) ([]model.Tool, error) {
|
||||
var tools []model.Tool
|
||||
query := s.toolRepo.DB()
|
||||
|
||||
if category != "" {
|
||||
query = query.Where("category = ?", category)
|
||||
}
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
|
||||
err := query.Find(&tools).Error
|
||||
return tools, err
|
||||
}
|
||||
|
||||
func (s *ToolService) CreateTool(tool *model.Tool) error {
|
||||
return s.toolRepo.Create(tool)
|
||||
}
|
||||
|
||||
func (s *ToolService) UpdateTool(tool *model.Tool) error {
|
||||
return s.toolRepo.Update(tool)
|
||||
}
|
||||
|
||||
func (s *ToolService) DeleteTool(id string) error {
|
||||
return s.toolRepo.Delete(id)
|
||||
}
|
||||
|
||||
// InitDefaultTools 初始化默认工具到数据库
|
||||
func (s *ToolService) InitDefaultTools() error {
|
||||
log.Println("[ToolService] Starting init default tools...")
|
||||
|
||||
// 获取默认工具
|
||||
tools := s.getDefaultTools()
|
||||
|
||||
// 删除现有的系统工具,重新插入
|
||||
s.toolRepo.DB().Where("provider = ?", "system").Delete(&model.Tool{})
|
||||
log.Printf("[ToolService] Deleted existing system tools, inserting %d default tools...", len(tools))
|
||||
|
||||
for _, tool := range tools {
|
||||
if err := s.toolRepo.Create(&tool); err != nil {
|
||||
log.Printf("[ToolService] Create tool error: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[ToolService] Default tools initialized successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDefaultTools 获取默认工具列表
|
||||
func (s *ToolService) getDefaultTools() []model.Tool {
|
||||
return []model.Tool{
|
||||
// 文件操作
|
||||
{
|
||||
Name: "read_file",
|
||||
Description: "Read the contents of a file from the filesystem.",
|
||||
Category: "file",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"file_path":{"type":"string","description":"The path to the file to read"},"encoding":{"type":"string","description":"File encoding (default: utf-8)","default":"utf-8"}},"required":["file_path"]}`,
|
||||
},
|
||||
{
|
||||
Name: "write_file",
|
||||
Description: "Write content to a file. Creates the file if it doesn't exist, overwrites if it does.",
|
||||
Category: "file",
|
||||
SecurityLevel: "review",
|
||||
RequireApproval: true,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"file_path":{"type":"string","description":"The path to the file to write"},"content":{"type":"string","description":"The content to write to the file"}},"required":["file_path","content"]}`,
|
||||
},
|
||||
{
|
||||
Name: "list_dir",
|
||||
Description: "List the contents of a directory.",
|
||||
Category: "file",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"dir_path":{"type":"string","description":"The path to the directory to list","default":"."}}}`,
|
||||
},
|
||||
{
|
||||
Name: "delete_file",
|
||||
Description: "Delete a file or directory.",
|
||||
Category: "file",
|
||||
SecurityLevel: "danger",
|
||||
RequireApproval: true,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"file_path":{"type":"string","description":"The path to the file or directory to delete"}},"required":["file_path"]}`,
|
||||
},
|
||||
{
|
||||
Name: "search_files",
|
||||
Description: "Search for files by name pattern or content.",
|
||||
Category: "file",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"directory":{"type":"string","description":"The directory to search in"},"pattern":{"type":"string","description":"Glob pattern for file names","default":"*"},"content_pattern":{"type":"string","description":"Search for files containing this text"}},"required":["directory"]}`,
|
||||
},
|
||||
// 代码执行
|
||||
{
|
||||
Name: "execute_python",
|
||||
Description: "Execute Python code in a sandboxed environment.",
|
||||
Category: "executor",
|
||||
SecurityLevel: "review",
|
||||
RequireApproval: true,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"code":{"type":"string","description":"The Python code to execute"},"timeout":{"type":"integer","description":"Execution timeout in seconds","default":30}},"required":["code"]}`,
|
||||
},
|
||||
{
|
||||
Name: "execute_javascript",
|
||||
Description: "Execute JavaScript code in a sandboxed environment.",
|
||||
Category: "executor",
|
||||
SecurityLevel: "review",
|
||||
RequireApproval: true,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"code":{"type":"string","description":"The JavaScript code to execute"},"timeout":{"type":"integer","description":"Execution timeout in seconds","default":30}},"required":["code"]}`,
|
||||
},
|
||||
{
|
||||
Name: "execute_bash",
|
||||
Description: "Execute a bash command in a sandboxed environment.",
|
||||
Category: "executor",
|
||||
SecurityLevel: "danger",
|
||||
RequireApproval: true,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"command":{"type":"string","description":"The bash command to execute"},"timeout":{"type":"integer","description":"Execution timeout in seconds","default":30}},"required":["command"]}`,
|
||||
},
|
||||
// 网页
|
||||
{
|
||||
Name: "web_fetch",
|
||||
Description: "Fetch content from a web URL.",
|
||||
Category: "web",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"url":{"type":"string","description":"The URL to fetch"},"method":{"type":"string","description":"HTTP method","default":"GET"},"timeout":{"type":"integer","description":"Request timeout in seconds","default":30}},"required":["url"]}`,
|
||||
},
|
||||
{
|
||||
Name: "web_search",
|
||||
Description: "Search the web for information.",
|
||||
Category: "web",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"query":{"type":"string","description":"The search query"},"max_results":{"type":"integer","description":"Maximum number of results","default":5}},"required":["query"]}`,
|
||||
},
|
||||
// HTTP
|
||||
{
|
||||
Name: "http_request",
|
||||
Description: "Make HTTP requests to APIs.",
|
||||
Category: "http",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"url":{"type":"string","description":"The URL to request"},"method":{"type":"string","description":"HTTP method","default":"GET"},"params":{"type":"object","description":"Query parameters"},"json_data":{"type":"object","description":"JSON body"},"timeout":{"type":"integer","description":"Request timeout","default":30}},"required":["url"]}`,
|
||||
},
|
||||
// 通知
|
||||
{
|
||||
Name: "send_notification",
|
||||
Description: "Send notifications via email, webhook, dingtalk, or slack.",
|
||||
Category: "notification",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"type":{"type":"string","description":"Notification type: email, webhook, dingtalk, slack"},"message":{"type":"string","description":"The notification message"}},"required":["type","message"]}`,
|
||||
},
|
||||
// 系统
|
||||
{
|
||||
Name: "get_current_time",
|
||||
Description: "Get the current date and time.",
|
||||
Category: "system",
|
||||
SecurityLevel: "safe",
|
||||
RequireApproval: false,
|
||||
Provider: "system",
|
||||
Status: "active",
|
||||
Parameters: `{"type":"object","properties":{"timezone":{"type":"string","description":"Optional timezone (e.g., UTC, Asia/Shanghai)"}}}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user