package util import ( "errors" "fmt" "os" "path/filepath" "regexp" "strings" ) // ValidateProjectDirName 验证项目ID是否为合法的目录名 func ValidateProjectDirName(projectID string) error { if len(projectID) == 0 { return errors.New("项目ID不能为空") } if len(projectID) > 64 { return errors.New("项目ID长度不能超过64字符") } // 合法字符:字母、数字、下划线、连字符 validPattern := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) if !validPattern.MatchString(projectID) { return errors.New("项目ID只能包含字母、数字、下划线和连字符") } // 保留名称检查 reservedNames := []string{".", "..", "con", "nul", "prn", "aux", "com1", "com2", "com3", "com4", "lpt1", "lpt2", "lpt3"} for _, name := range reservedNames { if strings.EqualFold(projectID, name) { return fmt.Errorf("项目ID不能使用系统保留名称: %s", name) } } // 不能以点或空格开头结尾 if strings.HasPrefix(projectID, ".") || strings.HasSuffix(projectID, ".") { return errors.New("项目ID不能以点开头或结尾") } if strings.HasPrefix(projectID, " ") || strings.HasSuffix(projectID, " ") { return errors.New("项目ID不能以空格开头或结尾") } return nil } // CreateProjectDirectory 创建项目目录结构 func CreateProjectDirectory(basePath, projectID string) error { // 验证项目ID if err := ValidateProjectDirName(projectID); err != nil { return fmt.Errorf("项目ID验证失败: %w", err) } // 构建完整项目路径 projectPath := filepath.Join(basePath, projectID) // 创建项目根目录 if err := os.MkdirAll(projectPath, 0755); err != nil { return fmt.Errorf("创建项目目录失败: %w", err) } // 创建子目录 subdirs := []string{ ".opencode", "logs", "data", } for _, subdir := range subdirs { dirPath := filepath.Join(projectPath, subdir) if err := os.MkdirAll(dirPath, 0755); err != nil { return fmt.Errorf("创建子目录 %s 失败: %w", subdir, err) } } return nil } // EnsureDirectoryExists 确保目录存在,如果不存在则创建 func EnsureDirectoryExists(path string) error { if _, err := os.Stat(path); os.IsNotExist(err) { if err := os.MkdirAll(path, 0755); err != nil { return fmt.Errorf("创建目录失败: %w", err) } } return nil } // GetProjectPath 获取项目完整路径 func GetProjectPath(basePath, projectID string) string { return filepath.Join(basePath, projectID) } // GetOpenCodeConfigPath 获取项目OpenCode配置文件路径 func GetOpenCodeConfigPath(basePath, projectID string) string { return filepath.Join(basePath, projectID, ".opencode", "opencode.json") } // GetProjectLogsPath 获取项目日志目录路径 func GetProjectLogsPath(basePath, projectID string) string { return filepath.Join(basePath, projectID, "logs") }