diff --git a/app.go b/app.go
index 5b07bf6..e3192fc 100644
--- a/app.go
+++ b/app.go
@@ -6,16 +6,13 @@ import (
"net/http"
"os"
"path/filepath"
- "regexp"
stdruntime "runtime"
"strings"
"time"
- "github.com/jung-kurt/gofpdf"
"golang.org/x/sys/windows/registry"
"u-desk/internal/api"
"u-desk/internal/common"
- "u-desk/internal/database"
"u-desk/internal/filesystem"
"u-desk/internal/service"
"u-desk/internal/storage"
@@ -24,13 +21,9 @@ import (
"github.com/wailsapp/wails/v2/pkg/runtime"
)
-// PDF 有序列表正则(包级变量,避免循环内重复编译)
-var orderedListRe = regexp.MustCompile(`^\d+\.\s+`)
-
// App 应用结构体
type App struct {
ctx context.Context
- db *database.DB
connectionAPI *api.ConnectionAPI
sqlAPI *api.SqlAPI
tabAPI *api.TabAPI
@@ -229,36 +222,6 @@ func (a *App) Shutdown(ctx context.Context) {
}
}
-// QueryUsers 查询用户列表
-func (a *App) QueryUsers(keyword string, status int, role int, organid int, page int, pageSize int, sortField string, sortOrder string) (map[string]interface{}, error) {
- db, err := a.getDB()
- if err != nil {
- return nil, err
- }
- return db.QueryUsers(keyword, status, role, organid, page, pageSize, sortField, sortOrder)
-}
-
-// getDB 获取数据库连接(延迟加载,按需初始化)
-func (a *App) getDB() (*database.DB, error) {
- if a.db != nil {
- return a.db, nil
- }
-
- // 首次调用时才连接数据库
- db, err := database.Init()
- if err != nil {
- return nil, fmt.Errorf("数据库连接失败: %v", err)
- }
-
- a.db = db
- return db, nil
-}
-
-// Greet 测试方法
-func (a *App) Greet(name string) string {
- return "Hello " + name + ", It's show time!"
-}
-
// GetSystemInfo 获取系统信息
func (a *App) GetSystemInfo() (map[string]interface{}, error) {
return system.GetSystemInfo()
@@ -992,70 +955,3 @@ func (a *App) SelectPDFSaveDirectory() (string, error) {
return a.pdfAPI.SelectDirectory()
}
-// ExportMarkdownToPDF 使用gofpdf导出Markdown为PDF
-func (a *App) ExportMarkdownToPDF(markdownContent string) (string, error) {
- // 1. 弹出保存对话框
- savePath, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
- Title: "保存 PDF",
- DefaultFilename: "document.pdf",
- Filters: []runtime.FileFilter{
- {DisplayName: "PDF 文件", Pattern: "*.pdf"},
- },
- })
- if err != nil || savePath == "" {
- return "", err
- }
-
- // 2. 创建PDF
- pdf := gofpdf.New("P", "mm", "A4", "")
- pdf.AddPage()
- pdf.SetAutoPageBreak(true, 15)
-
- // 3. 解析Markdown并写入PDF
- lines := strings.Split(markdownContent, "\n")
- for _, line := range lines {
- if strings.HasPrefix(line, "# ") {
- // H1 标题
- pdf.SetFont("Arial", "B", 24)
- pdf.Cell(40, 10, strings.TrimPrefix(line, "# "))
- pdf.Ln(12)
- } else if strings.HasPrefix(line, "## ") {
- // H2 标题
- pdf.SetFont("Arial", "B", 18)
- pdf.Cell(40, 10, strings.TrimPrefix(line, "## "))
- pdf.Ln(10)
- } else if strings.HasPrefix(line, "### ") {
- // H3 标题
- pdf.SetFont("Arial", "B", 14)
- pdf.Cell(40, 10, strings.TrimPrefix(line, "### "))
- pdf.Ln(8)
- } else if strings.HasPrefix(line, "- ") || strings.HasPrefix(line, "* ") {
- // 无序列表
- pdf.SetFont("Arial", "", 12)
- pdf.Cell(10, 7, "•")
- pdf.Cell(0, 7, strings.TrimPrefix(line, "- "))
- pdf.Ln(7)
- } else if orderedListRe.MatchString(line) {
- // 有序列表
- pdf.SetFont("Arial", "", 12)
- pdf.Cell(10, 7, strings.TrimSpace(strings.SplitN(line, ".", 2)[0]) + ".")
- pdf.Cell(0, 7, strings.TrimSpace(strings.SplitN(line, ".", 2)[1]))
- pdf.Ln(7)
- } else if line == "" {
- // 空行
- pdf.Ln(7)
- } else {
- // 普通文本
- pdf.SetFont("Arial", "", 12)
- pdf.MultiCell(190, 7, line, "", "", false)
- }
- }
-
- // 4. 保存文件
- err = pdf.OutputFileAndClose(savePath)
- if err != nil {
- return "", fmt.Errorf("保存PDF文件失败: %v", err)
- }
-
- return savePath, nil
-}
diff --git a/go.mod b/go.mod
index 5336583..9472fd1 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,6 @@ require (
github.com/chromedp/chromedp v0.14.2
github.com/glebarez/sqlite v1.11.0
github.com/go-sql-driver/mysql v1.9.3
- github.com/jung-kurt/gofpdf v1.16.2
github.com/redis/go-redis/v9 v9.17.3
github.com/shirou/gopsutil/v3 v3.24.5
github.com/wailsapp/wails/v2 v2.12.0
diff --git a/go.sum b/go.sum
index 509ec70..ef26d5a 100644
--- a/go.sum
+++ b/go.sum
@@ -4,7 +4,6 @@ git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
-github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@@ -17,7 +16,6 @@ github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZ
github.com/chromedp/chromedp v0.14.2/go.mod h1:rHzAv60xDE7VNy/MYtTUrYreSc0ujt2O1/C3bzctYBo=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
@@ -59,9 +57,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
-github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
-github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
-github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/labstack/echo/v4 v4.15.0 h1:hoRTKWcnR5STXZFe9BmYun9AMTNeSbjHi2vtDuADJ24=
@@ -93,10 +88,8 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
-github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -110,7 +103,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
@@ -119,7 +111,6 @@ github.com/shoenig/go-m1cpu v0.1.7 h1:C76Yd0ObKR82W4vhfjZiCp0HxcSZ8Nqd84v+HZ0qyI
github.com/shoenig/go-m1cpu v0.1.7/go.mod h1:KkDOw6m3ZJQAPHbrzkZki4hnx+pDRR1Lo+ldA56wD5w=
github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk=
github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
@@ -159,7 +150,6 @@ golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
-golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
diff --git a/internal/common/constants.go b/internal/common/constants.go
index 29df0bb..a8fc91a 100644
--- a/internal/common/constants.go
+++ b/internal/common/constants.go
@@ -12,6 +12,3 @@ const (
// DefaultVisibleTabs 默认可见的 Tabs
var DefaultVisibleTabs = []string{TabDatabase, TabFileSystem, TabDevice}
-
-// DefaultTab 默认打开的 Tab
-const DefaultTab = TabDatabase
diff --git a/internal/common/utils.go b/internal/common/utils.go
index d4129c9..f9d6170 100644
--- a/internal/common/utils.go
+++ b/internal/common/utils.go
@@ -61,12 +61,3 @@ func IsWindows() bool {
return runtime.GOOS == "windows"
}
-// IsMac 判断是否为Mac系统
-func IsMac() bool {
- return runtime.GOOS == "darwin"
-}
-
-// IsLinux 判断是否为Linux系统
-func IsLinux() bool {
- return runtime.GOOS == "linux"
-}
diff --git a/internal/filesystem/asset_handler.go b/internal/filesystem/asset_handler.go
index 544df87..5c9f32a 100644
--- a/internal/filesystem/asset_handler.go
+++ b/internal/filesystem/asset_handler.go
@@ -2,7 +2,6 @@ package filesystem
import (
"context"
- "encoding/base64"
"fmt"
"log"
"net/http"
@@ -303,52 +302,6 @@ func getContentType(ext string) string {
return defaultFileTypeManager.GetMIMEType(ext)
}
-// ReadFileAsBase64 读取文件并返回 base64 编码的字符串
-// 用于读取从 ZIP 提取的临时图片文件
-func ReadFileAsBase64(filePath string) (string, error) {
- log.Printf("[ReadFileAsBase64] 读取文件: %s", filePath)
-
- if !isSafePath(filePath) {
- return "", fmt.Errorf("路径不安全")
- }
-
- // 检查文件是否存在
- fileInfo, err := os.Stat(filePath)
- if err != nil {
- if os.IsNotExist(err) {
- return "", fmt.Errorf("文件不存在: %s", filePath)
- }
- return "", fmt.Errorf("无法访问文件: %v", err)
- }
-
- log.Printf("[ReadFileAsBase64] 文件大小: %d bytes", fileInfo.Size())
-
- // 读取文件
- data, err := os.ReadFile(filePath)
- if err != nil {
- return "", fmt.Errorf("读取文件失败: %v", err)
- }
-
- // 编码为 base64
- encoded := base64.StdEncoding.EncodeToString(data)
- log.Printf("[ReadFileAsBase64] 编码成功: 原始=%d, base64=%d", len(data), len(encoded))
-
- // 获取文件扩展名并确定 MIME 类型
- ext := strings.ToLower(filepath.Ext(filePath))
- mimeType := getContentType(ext)
-
- // 返回 data URI 格式: data:image/png;base64,iVBORw0KG...
- return fmt.Sprintf("data:%s;base64,%s", mimeType, encoded), nil
-}
-
-// HandleLocalFile 处理 /localfs/ 路由的 HTTP 请求
-// 前端可以请求 http://localhost:18765/localfs/C:/path/to/image.jpg
-// 注意:此函数与 ServeHTTP 功能重复,建议统一使用 ServeHTTP
-func HandleLocalFile(w http.ResponseWriter, r *http.Request) {
- handler := NewLocalFileHandler()
- handler.ServeHTTP(w, r)
-}
-
// isAllowedFileType 检查文件类型是否在白名单中
func isAllowedFileType(ext string) bool {
return defaultFileTypeManager.IsAllowed(ext)
diff --git a/internal/filesystem/audit_log.go b/internal/filesystem/audit_log.go
index 2ad5e5b..7ab97ba 100644
--- a/internal/filesystem/audit_log.go
+++ b/internal/filesystem/audit_log.go
@@ -220,37 +220,6 @@ func (a *AuditLogger) Close() error {
return a.logFile.Close()
}
-// RotateLog 日志轮转(每天创建新文件)
-func (a *AuditLogger) RotateLog() error {
- a.mu.Lock()
- defer a.mu.Unlock()
-
- // 刷新缓冲区
- if err := a.flush(); err != nil {
- return err
- }
-
- // 关闭当前文件
- if err := a.logFile.Close(); err != nil {
- return err
- }
-
- // 生成新的日志文件名
- timestamp := time.Now().Format("2006-01-02")
- logPath := filepath.Join(filepath.Dir(a.logPath), fmt.Sprintf("audit_%s.log", timestamp))
-
- // 打开新文件
- logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
- if err != nil {
- return err
- }
-
- a.logFile = logFile
- a.logPath = logPath
-
- return nil
-}
-
// GetRecentLogs 获取最近的审计日志
func GetRecentLogs(logDir string, limit int) ([]AuditLogEntry, error) {
// 读取今天的日志文件
@@ -309,22 +278,8 @@ func parseLines(text string) []string {
var globalAuditLogger *AuditLogger
var auditLoggerOnce sync.Once
-// InitAuditLogger 初始化全局审计日志记录器
-func InitAuditLogger(logDir string) error {
- var err error
- globalAuditLogger, err = NewAuditLogger(logDir)
- return err
-}
-
// GetAuditLogger 获取全局审计日志记录器
func GetAuditLogger() *AuditLogger {
return globalAuditLogger
}
-// CloseAuditLogger 关闭全局审计日志记录器
-func CloseAuditLogger() error {
- if globalAuditLogger != nil {
- return globalAuditLogger.Close()
- }
- return nil
-}
diff --git a/internal/filesystem/constants.go b/internal/filesystem/constants.go
index 38113e9..d3a6743 100644
--- a/internal/filesystem/constants.go
+++ b/internal/filesystem/constants.go
@@ -13,22 +13,13 @@ const (
// HTTP 文件服务大小限制
MaxHTTPFileSize = 500 * 1024 * 1024 // 500MB - HTTP 访问文件最大大小
-
- // 删除操作限制
- MaxDeleteSizeGB = 1 * 1024 * 1024 * 1024 // 1GB - 单个文件删除大小限制
- MaxDeleteDirSizeGB = 1 * 1024 * 1024 * 1024 // 1GB - 目录删除大小限制
)
// 时间相关常量
const (
// 审计日志
- AuditFlushInterval = 5 * time.Second // 审计日志刷新间隔
AuditLogBufferSize = 100 // 审计日志缓冲区大小
- // 回收站
- RecycleBinRetentionDays = 30 // 回收站文件保留天数(天)
- RecycleBinRetentionPeriod = 30 * 24 * time.Hour // 回收站文件保留期
-
// 临时文件
TempFileCleanupAge = 24 * time.Hour // 临时文件清理周期
TempFileDir = "u-desk-zip" // 临时文件目录名
@@ -36,7 +27,6 @@ const (
// 数量限制常量
const (
- MaxDirectoryDepth = 15 // 最大目录深度
MaxFileCount = 1000 // 最大文件数量(目录)
)
@@ -48,15 +38,9 @@ const (
// 随机字符串相关常量
const (
- RandomStringCharset = "abcdefghijklmnopqrstuvwxyz0123456789"
RandomStringDefaultLength = 6 // 回收站文件名随机后缀长度
)
-// 文件路径相关常量
-const (
- WindowsDriveLength = 2 // Windows 盘符长度 (C:)
-)
-
// 路径遍历检测字符串
const (
PathTraversalPattern = ".." // 路径遍历特征字符串
@@ -69,17 +53,5 @@ const (
FileTypeAudio = "audio"
FileTypeDocument = "document"
FileTypeText = "text"
- FileTypeArchive = "archive"
FileTypeApplication = "application"
)
-
-// 安全相关常量
-const (
- // ZIP 安全
- MinValidZipSize = 22 // ZIP 文件最小有效大小(文件头)
- ZipFileHeaderSignature = 0x504B // "PK" - ZIP 文件头签名
-
- // 文件锁
- LockCheckMaxRetries = 3 // 文件锁检查最大重试次数
- LockCheckRetryInterval = 100 * time.Millisecond // 文件锁检查重试间隔
-)
diff --git a/internal/filesystem/errors.go b/internal/filesystem/errors.go
index c262603..a8b42ab 100644
--- a/internal/filesystem/errors.go
+++ b/internal/filesystem/errors.go
@@ -6,130 +6,6 @@ import (
"runtime"
)
-// ErrorCode 错误码类型
-type ErrorCode string
-
-const (
- // 通用错误
- ErrCodeGeneral ErrorCode = "GENERAL_ERROR"
- ErrCodeInvalid ErrorCode = "INVALID_ARGUMENT"
- ErrCodeNotFound ErrorCode = "NOT_FOUND"
- ErrCodePermission ErrorCode = "PERMISSION_DENIED"
- ErrCodeIO ErrorCode = "IO_ERROR"
-
- // 路径相关错误
- ErrCodePathTraversal ErrorCode = "PATH_TRAVERSAL"
- ErrCodeInvalidPath ErrorCode = "INVALID_PATH"
- ErrCodeSensitivePath ErrorCode = "SENSITIVE_PATH"
-
- // 文件操作错误
- ErrCodeFileNotFound ErrorCode = "FILE_NOT_FOUND"
- ErrCodeFileExists ErrorCode = "FILE_EXISTS"
- ErrCodeDirectoryNotEmpty ErrorCode = "DIRECTORY_NOT_EMPTY"
-
- // 安全相关错误
- ErrCodeSecurityViolation ErrorCode = "SECURITY_VIOLATION"
- ErrCodeSizeLimit ErrorCode = "SIZE_LIMIT_EXCEEDED"
- ErrCodeFileLocked ErrorCode = "FILE_LOCKED"
-
- // ZIP相关错误
- ErrCodeZipInvalid ErrorCode = "ZIP_INVALID"
- ErrCodeZipBomb ErrorCode = "ZIP_BOMB"
- ErrCodeZipExtract ErrorCode = "ZIP_EXTRACT_FAILED"
-)
-
-// FileError 文件系统专用错误类型
-// 包含详细的错误上下文信息,便于调试和用户提示
-type FileNotFoundError struct {
- Path string
- Err error
-}
-
-func (e *FileNotFoundError) Error() string {
- return fmt.Sprintf("文件不存在: %s", e.Path)
-}
-
-func (e *FileNotFoundError) Unwrap() error {
- return e.Err
-}
-
-// PathValidationError 路径验证错误
-type PathValidationError struct {
- Path string
- Reason string
- IsSensitive bool
-}
-
-func (e *PathValidationError) Error() string {
- return fmt.Sprintf("路径验证失败: %s - %s", e.Path, e.Reason)
-}
-
-// SecurityViolationError 安全违规错误
-type SecurityViolationError struct {
- Path string
- Violation string
- Suggestion string
-}
-
-func (e *SecurityViolationError) Error() string {
- msg := fmt.Sprintf("安全违规: %s - %s", e.Path, e.Violation)
- if e.Suggestion != "" {
- msg += fmt.Sprintf("\n建议: %s", e.Suggestion)
- }
- return msg
-}
-
-// SizeLimitError 大小限制错误
-type SizeLimitError struct {
- Path string
- ActualSize int64
- MaxSize int64
- SizeType string // "file" or "directory"
-}
-
-func (e *SizeLimitError) Error() string {
- return fmt.Sprintf("%s大小超限: %s (实际: %.2f GB, 限制: %.2f GB)",
- e.SizeType, e.Path,
- float64(e.ActualSize)/(1024*1024*1024),
- float64(e.MaxSize)/(1024*1024*1024),
- )
-}
-
-// FileLockedError 文件锁定错误
-type FileLockedError struct {
- Path string
- ProcessInfo string
-}
-
-func (e *FileLockedError) Error() string {
- msg := fmt.Sprintf("文件被占用: %s", e.Path)
- if e.ProcessInfo != "" {
- msg += fmt.Sprintf("\n占用程序: %s", e.ProcessInfo)
- }
- return msg
-}
-
-// WrapError 错误包装函数
-// 添加上下文信息到错误中
-func WrapError(operation string, path string, err error) error {
- return fmt.Errorf("%s 失败: %s - %w", operation, path, err)
-}
-
-// WrapErrorf 格式化错误包装
-func WrapErrorf(format string, args ...interface{}) error {
- return fmt.Errorf(format, args...)
-}
-
-// GetStackTrace 获取堆栈跟踪(用于调试)
-func GetStackTrace(skip int) string {
- buf := make([]byte, 4096)
- n := runtime.Stack(buf, false)
- if n > 0 {
- return string(buf[:n])
- }
- return ""
-}
-
// DeleteRestrictionWarning 删除限制警告
// 用于在删除受限文件时提供详细的警告信息
type DeleteRestrictionWarning struct {
@@ -141,3 +17,13 @@ type DeleteRestrictionWarning struct {
func (w *DeleteRestrictionWarning) Error() string {
return fmt.Sprintf("删除限制警告: %s\n%s", w.Path, w.Details)
}
+
+// GetStackTrace 获取堆栈跟踪(用于调试)
+func GetStackTrace(skip int) string {
+ buf := make([]byte, 4096)
+ n := runtime.Stack(buf, false)
+ if n > 0 {
+ return string(buf[:n])
+ }
+ return ""
+}
diff --git a/internal/filesystem/file_lock.go b/internal/filesystem/file_lock.go
index 9772da1..c1e3cf3 100644
--- a/internal/filesystem/file_lock.go
+++ b/internal/filesystem/file_lock.go
@@ -4,14 +4,6 @@ import (
"fmt"
"os"
"syscall"
- "time"
-)
-
-// Windows API 锁相关函数和常量
-var (
- modkernel32 = syscall.NewLazyDLL("kernel32.dll")
- procGetLastError = modkernel32.NewProc("GetLastError")
- procGetProcessId = modkernel32.NewProc("GetProcessId")
)
// FileLockChecker 文件锁检查器
@@ -102,37 +94,6 @@ func (c *FileLockChecker) getProcessInfo(path string) (string, error) {
return "文件正被其他程序使用", nil
}
-// CheckFileWithRetry 带重试的文件锁检查
-func (c *FileLockChecker) CheckFileWithRetry(path string, maxRetries int, retryInterval time.Duration) error {
- for i := 0; i < maxRetries; i++ {
- locked, processInfo, err := c.IsFileLocked(path)
- if err != nil && !locked {
- // 非锁相关的错误,直接返回
- return err
- }
-
- if !locked {
- // 文件未被锁定,可以操作
- return nil
- }
-
- // 文件被锁定
- if i < maxRetries-1 {
- // 还有重试机会,等待后重试
- time.Sleep(retryInterval)
- continue
- }
-
- // 最后一次重试失败,返回错误
- if processInfo != "" {
- return fmt.Errorf("文件被占用: %s", processInfo)
- }
- return fmt.Errorf("文件被其他程序占用,请关闭相关程序后重试")
- }
-
- return fmt.Errorf("文件检查超时")
-}
-
// SafeDeleteWithLockCheck 带锁检查的安全删除
func (c *FileLockChecker) SafeDeleteWithLockCheck(path string) error {
// 检查文件是否被锁定
@@ -158,20 +119,6 @@ const (
ERROR_SHARING_VIOLATION = 32 // syscall.Errno(32)
)
-// BY_HANDLE_FILE_INFORMATION 文件信息结构体
-type BY_HANDLE_FILE_INFORMATION struct {
- FileAttributes uint32
- CreationTime syscall.Filetime
- LastAccessTime syscall.Filetime
- LastWriteTime syscall.Filetime
- VolumeSerialNumber uint32
- FileSizeHigh uint32
- FileSizeLow uint32
- NumberOfLinks uint32
- FileIndexHigh uint32
- FileIndexLow uint32
-}
-
// contains 检查字符串是否包含子串(不区分大小写)
func contains(str, substr string) bool {
return len(str) >= len(substr) && (str == substr || len(substr) == 0 ||
@@ -203,18 +150,3 @@ func containsIgnoreCase(str, substr string) bool {
return false
}
-// 全局文件锁检查器
-var globalLockChecker *FileLockChecker
-
-// InitFileLockChecker 初始化全局文件锁检查器
-func InitFileLockChecker() {
- globalLockChecker = NewFileLockChecker()
-}
-
-// GetFileLockChecker 获取全局文件锁检查器
-func GetFileLockChecker() *FileLockChecker {
- if globalLockChecker == nil {
- globalLockChecker = NewFileLockChecker()
- }
- return globalLockChecker
-}
diff --git a/internal/filesystem/logger.go b/internal/filesystem/logger.go
index 618b494..edcfc93 100644
--- a/internal/filesystem/logger.go
+++ b/internal/filesystem/logger.go
@@ -70,11 +70,6 @@ func (l *Logger) Info(format string, args ...interface{}) {
l.log(LogLevelInfo, "INFO", format, args...)
}
-// Warn 记录警告日志
-func (l *Logger) Warn(format string, args ...interface{}) {
- l.log(LogLevelWarn, "WARN", format, args...)
-}
-
// Error 记录错误日志
func (l *Logger) Error(format string, args ...interface{}) {
l.log(LogLevelError, "ERROR", format, args...)
@@ -141,37 +136,10 @@ func LogError(operation string, path string, err error) {
var (
globalLogger *Logger
- loggerOnce sync.Once
)
-// InitLogger 初始化全局日志记录器
-func InitLogger(logDir string, minLevel LogLevel) error {
- var initErr error
- loggerOnce.Do(func() {
- timestamp := time.Now().Format("2006-01-02")
- logPath := filepath.Join(logDir, fmt.Sprintf("filesystem_%s.log", timestamp))
-
- logger, err := NewLogger(logPath, minLevel)
- if err != nil {
- initErr = err
- return
- }
-
- globalLogger = logger
- log.Printf("[日志系统] 已启动,日志文件: %s", logPath)
- })
- return initErr
-}
-
// GetGlobalLogger 获取全局日志记录器
func GetGlobalLogger() *Logger {
return globalLogger
}
-// CloseLogger 关闭全局日志记录器
-func CloseLogger() error {
- if globalLogger != nil {
- return globalLogger.Close()
- }
- return nil
-}
diff --git a/internal/filesystem/recycle_bin.go b/internal/filesystem/recycle_bin.go
index b0eac95..379c7e4 100644
--- a/internal/filesystem/recycle_bin.go
+++ b/internal/filesystem/recycle_bin.go
@@ -376,16 +376,6 @@ func generateRandomString(length int) string {
// 全局回收站实例
var globalRecycleBin *RecycleBin
-// InitRecycleBin 初始化全局回收站
-func InitRecycleBin(binPath string) error {
- bin, err := NewRecycleBin(binPath)
- if err != nil {
- return err
- }
- globalRecycleBin = bin
- return nil
-}
-
// GetRecycleBin 获取全局回收站实例
func GetRecycleBin() *RecycleBin {
return globalRecycleBin
diff --git a/internal/filesystem/service.go b/internal/filesystem/service.go
index 040056e..a5da202 100644
--- a/internal/filesystem/service.go
+++ b/internal/filesystem/service.go
@@ -119,11 +119,6 @@ func (s *FileSystemService) initRecycleBin() error {
// ========== 核心文件操作 ==========
-// Read 读取文件内容(实现 FileService 接口)
-func (s *FileSystemService) Read(path string) (string, error) {
- return s.ReadFile(path)
-}
-
// ReadFile 读取文件内容(限制最大 10MB)
func (s *FileSystemService) ReadFile(path string) (string, error) {
// 路径验证
@@ -151,10 +146,6 @@ func (s *FileSystemService) ReadFile(path string) (string, error) {
}
// Write 写入文件内容(实现 FileService 接口)
-func (s *FileSystemService) Write(path, content string) error {
- return s.WriteFile(path, content)
-}
-
// writeFile 内部写入实现(路径验证+大小检查+写入+日志)
func (s *FileSystemService) writeFileWithLog(path string, data []byte) error {
if err := s.validatePath(path); err != nil {
@@ -192,31 +183,6 @@ func (s *FileSystemService) SaveBase64File(path, base64Content string) error {
return s.writeFileWithLog(path, data)
}
-// List 列出目录内容(实现 FileService 接口)
-func (s *FileSystemService) List(path string) ([]map[string]interface{}, error) {
- return s.ListDir(path)
-}
-
-// Open 打开文件(实现 FileService 接口)
-func (s *FileSystemService) Open(path string) error {
- // 使用系统默认程序打开文件
- var cmd *exec.Cmd
- switch runtime.GOOS {
- case "windows":
- cmd = exec.Command("cmd", "/c", "start", "", path)
- case "darwin":
- cmd = exec.Command("open", path)
- default:
- cmd = exec.Command("xdg-open", path)
- }
- return cmd.Start()
-}
-
-// Delete 删除文件或目录(实现 FileService 接口)
-func (s *FileSystemService) Delete(path string) (*FileOperationResult, error) {
- return s.DeletePathWithContext(context.Background(), path)
-}
-
// DeletePath 删除文件或目录
func (s *FileSystemService) DeletePath(path string) (*FileOperationResult, error) {
return s.DeletePathWithContext(context.Background(), path)
@@ -430,11 +396,6 @@ func (s *FileSystemService) CreateFile(path string) (*FileOperationResult, error
}, nil
}
-// GetInfo 获取文件信息(实现 FileService 接口)
-func (s *FileSystemService) GetInfo(path string) (map[string]interface{}, error) {
- return s.GetFileInfo(path)
-}
-
// GetFileInfo 获取文件信息
func (s *FileSystemService) GetFileInfo(path string) (map[string]interface{}, error) {
if err := s.validatePath(path); err != nil {
@@ -519,31 +480,16 @@ func (s *FileSystemService) RenamePath(oldPath, newPath string) (*FileOperationR
// ========== ZIP操作接口 ==========
-// ListZip 列出ZIP文件内容
-func (s *FileSystemService) ListZip(zipPath string) ([]map[string]interface{}, error) {
- return ListZipContents(zipPath)
-}
-
// ListZipContents 列出ZIP文件内容(别名,保持向后兼容)
func (s *FileSystemService) ListZipContents(zipPath string) ([]map[string]interface{}, error) {
return ListZipContents(zipPath)
}
-// ExtractZipFile 从ZIP提取文件内容
-func (s *FileSystemService) ExtractZipFile(zipPath, filePath string) (string, error) {
- return ExtractFileFromZip(zipPath, filePath)
-}
-
// ExtractFileFromZip 从ZIP提取文件内容(别名,保持向后兼容)
func (s *FileSystemService) ExtractFileFromZip(zipPath, filePath string) (string, error) {
return ExtractFileFromZip(zipPath, filePath)
}
-// ExtractZipFileToTemp 从ZIP提取文件到临时目录
-func (s *FileSystemService) ExtractZipFileToTemp(zipPath, filePath string) (string, error) {
- return ExtractFileFromZipToTemp(zipPath, filePath)
-}
-
// ExtractFileFromZipToTemp 从ZIP提取文件到临时目录(别名,保持向后兼容)
func (s *FileSystemService) ExtractFileFromZipToTemp(zipPath, filePath string) (string, error) {
return ExtractFileFromZipToTemp(zipPath, filePath)
@@ -564,7 +510,9 @@ func getCurrentTimestamp() time.Time {
// isInRecycleBin 检查路径是否在回收站中
func isInRecycleBin(path string) bool {
recycleBinPath := filepath.Join(common.GetUserDataDir(), "recycle_bin")
- return filepath.HasPrefix(filepath.Clean(path), filepath.Clean(recycleBinPath))
+ cleanPath := filepath.Clean(path)
+ cleanBinPath := filepath.Clean(recycleBinPath)
+ return len(cleanPath) >= len(cleanBinPath) && cleanPath[:len(cleanBinPath)] == cleanBinPath
}
// ========== 辅助方法 ==========
@@ -787,16 +735,3 @@ func GetGlobalService() (*FileSystemService, error) {
return globalService, initErr
}
-// InitGlobalFileSystem 初始化全局文件系统(兼容旧代码)
-func InitGlobalFileSystem() error {
- _, err := GetGlobalService()
- return err
-}
-
-// CloseGlobalFileSystem 关闭全局文件系统
-func CloseGlobalFileSystem(ctx context.Context) error {
- if globalService != nil {
- return globalService.Close(ctx)
- }
- return nil
-}
diff --git a/internal/filesystem/zip.go b/internal/filesystem/zip.go
index 7fe8368..c1154fd 100644
--- a/internal/filesystem/zip.go
+++ b/internal/filesystem/zip.go
@@ -346,46 +346,4 @@ func GetZipFileInfo(zipPath, filePath string) (map[string]interface{}, error) {
return result.(map[string]interface{}), nil
}
-// validateZipFileBasic 验证ZIP文件的基本信息(提取自ListZipContents)
-func validateZipFileBasic(zipPath string) error {
- if err := validateZipPath(zipPath); err != nil {
- return err
- }
-
- fileInfo, err := os.Stat(zipPath)
- if err != nil {
- return fmt.Errorf("无法访问文件: %v", err)
- }
-
- if fileInfo.Size() < MinValidZipSize {
- return fmt.Errorf("文件太小 (%d bytes)", fileInfo.Size())
- }
-
- if fileInfo.Size() > MaxZipSize {
- return fmt.Errorf("ZIP文件过大 (%d bytes)", fileInfo.Size())
- }
-
- return checkZipFileHeader(zipPath)
-}
-
-// checkZipFileHeader 检查ZIP文件头签名
-func checkZipFileHeader(zipPath string) error {
- file, err := os.Open(zipPath)
- if err != nil {
- return fmt.Errorf("无法打开文件: %v", err)
- }
- defer file.Close()
-
- header := make([]byte, 4)
- n, err := file.Read(header)
- if err != nil || n != 4 {
- return fmt.Errorf("无法读取文件头")
- }
-
- if header[0] != 0x50 || header[1] != 0x4B {
- return fmt.Errorf("不是有效的 ZIP 文件")
- }
-
- return nil
-}
diff --git a/internal/filesystem/zip_helper.go b/internal/filesystem/zip_helper.go
index a82e460..f817a10 100644
--- a/internal/filesystem/zip_helper.go
+++ b/internal/filesystem/zip_helper.go
@@ -65,25 +65,6 @@ func isMatchFile(file *zip.File, targetPath string) bool {
filepath.Clean(file.Name) == filepath.Clean(targetPath)
}
-// openZipFileInReader 在ZIP reader中打开指定文件
-// 用于读取文件内容的辅助函数
-func openZipFileInReader(reader *zip.ReadCloser, filePath string) (io.ReadCloser, *zip.File, error) {
- for _, file := range reader.File {
- if isMatchFile(file, filePath) {
- if file.Mode().IsDir() {
- return nil, nil, fmt.Errorf("不能读取目录")
- }
-
- rc, err := file.Open()
- if err != nil {
- return nil, nil, fmt.Errorf("打开 zip 中的文件失败: %v", err)
- }
- return rc, file, nil
- }
- }
- return nil, nil, fmt.Errorf("文件在 zip 中不存在: %s", filePath)
-}
-
// readAllFromFile 从文件读取所有内容
// 辅助函数,避免重复的 io.ReadAll 调用
func readAllFromFile(rc io.ReadCloser) ([]byte, error) {
diff --git a/internal/service/tab_service.go b/internal/service/tab_service.go
index 831d828..1e4f491 100644
--- a/internal/service/tab_service.go
+++ b/internal/service/tab_service.go
@@ -29,8 +29,3 @@ func (s *TabService) SaveTabs(tabs []models.SqlTab) error {
func (s *TabService) ListTabs() ([]models.SqlTab, error) {
return s.repo.FindAll()
}
-
-// DeleteTab 删除标签页
-func (s *TabService) DeleteTab(id uint) error {
- return s.repo.Delete(id)
-}
diff --git a/internal/service/update_config.go b/internal/service/update_config.go
index 5692172..94264dc 100644
--- a/internal/service/update_config.go
+++ b/internal/service/update_config.go
@@ -102,22 +102,6 @@ func SaveUpdateConfig(config *UpdateConfig) error {
return nil
}
-// ShouldCheckUpdate 判断是否应该检查更新
-func (c *UpdateConfig) ShouldCheckUpdate() bool {
- if !c.AutoCheckEnabled {
- return false
- }
-
- // 如果从未检查过,应该检查
- if c.LastCheckTime.IsZero() {
- return true
- }
-
- // 检查是否超过间隔分钟数
- minutesSinceLastCheck := time.Since(c.LastCheckTime).Minutes()
- return minutesSinceLastCheck >= float64(c.CheckIntervalMinutes)
-}
-
// UpdateLastCheckTime 更新最后检查时间
func (c *UpdateConfig) UpdateLastCheckTime() error {
c.LastCheckTime = time.Now()
diff --git a/internal/service/version.go b/internal/service/version.go
index 939f033..6c8a1b9 100644
--- a/internal/service/version.go
+++ b/internal/service/version.go
@@ -64,11 +64,6 @@ func ParseVersion(versionStr string) (*Version, error) {
return &Version{Major: major, Minor: minor, Patch: patch}, nil
}
-// String 返回版本号字符串(格式:v1.0.0)
-func (v *Version) String() string {
- return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Patch)
-}
-
// Compare 比较版本号
// 返回值:-1 表示当前版本小于目标版本,0 表示相等,1 表示大于
func (v *Version) Compare(other *Version) int {
@@ -100,11 +95,6 @@ func (v *Version) IsNewerThan(other *Version) bool {
return v.Compare(other) > 0
}
-// IsOlderThan 判断是否比目标版本旧
-func (v *Version) IsOlderThan(other *Version) bool {
- return v.Compare(other) < 0
-}
-
// ==================== 版本号获取 ====================
// GetCurrentVersion 获取当前版本号(带缓存)
diff --git a/internal/storage/connection_service.go b/internal/storage/connection_service.go
deleted file mode 100644
index 63098a0..0000000
--- a/internal/storage/connection_service.go
+++ /dev/null
@@ -1,279 +0,0 @@
-package storage
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "u-desk/internal/crypto"
- "u-desk/internal/dbclient"
- "u-desk/internal/storage/models"
-
- "gorm.io/gorm"
-)
-
-// ConnectionService 连接管理服务
-type ConnectionService struct {
- db *gorm.DB
-}
-
-// NewConnectionService 创建连接服务
-func NewConnectionService() (*ConnectionService, error) {
- db := GetDB()
- if db == nil {
- // 尝试重新初始化
- var err error
- db, err = Init()
- if err != nil {
- return nil, fmt.Errorf("数据库初始化失败: %v", err)
- }
- }
- return &ConnectionService{db: db}, nil
-}
-
-// SaveConnection 保存连接配置
-func (s *ConnectionService) SaveConnection(conn *models.DbConnection) error {
- if conn.Name == "" {
- return fmt.Errorf("连接名称不能为空")
- }
- if conn.Type == "" {
- return fmt.Errorf("数据库类型不能为空")
- }
- if conn.Host == "" {
- return fmt.Errorf("主机地址不能为空")
- }
-
- // 检查名称是否重复(排除当前记录)
- var count int64
- query := s.db.Model(&models.DbConnection{}).Where("name = ?", conn.Name)
- if conn.ID > 0 {
- query = query.Where("id != ?", conn.ID)
- }
- query.Count(&count)
- if count > 0 {
- return fmt.Errorf("连接名称已存在")
- }
-
- if conn.ID > 0 {
- // 更新模式
- updateData := map[string]interface{}{
- "name": conn.Name,
- "type": conn.Type,
- "host": conn.Host,
- "port": conn.Port,
- "username": conn.Username,
- "database": conn.Database,
- "options": conn.Options,
- "visible_databases": conn.VisibleDatabases,
- }
-
- // 如果提供了新密码,加密后更新
- if conn.Password != "" {
- encrypted, err := crypto.EncryptPassword(conn.Password)
- if err != nil {
- return fmt.Errorf("密码加密失败: %v", err)
- }
- updateData["password"] = encrypted
- }
- // 如果密码为空,不更新密码字段(保留原密码)
-
- return s.db.Model(&models.DbConnection{}).Where("id = ?", conn.ID).Updates(updateData).Error
- }
-
- // 新增模式 - 必须提供密码
- if conn.Password == "" {
- return fmt.Errorf("新增连接时密码不能为空")
- }
-
- // 加密密码
- encrypted, err := crypto.EncryptPassword(conn.Password)
- if err != nil {
- return fmt.Errorf("密码加密失败: %v", err)
- }
- conn.Password = encrypted
-
- return s.db.Create(conn).Error
-}
-
-// ListConnections 获取连接列表
-func (s *ConnectionService) ListConnections() ([]models.DbConnection, error) {
- var connections []models.DbConnection
- err := s.db.Order("created_at DESC").Find(&connections).Error
- return connections, err
-}
-
-// GetConnection 获取连接详情
-func (s *ConnectionService) GetConnection(id uint) (*models.DbConnection, error) {
- var conn models.DbConnection
- err := s.db.First(&conn, id).Error
- if err != nil {
- return nil, err
- }
- return &conn, nil
-}
-
-// DeleteConnection 删除连接配置
-func (s *ConnectionService) DeleteConnection(id uint) error {
- var conn models.DbConnection
- if err := s.db.First(&conn, id).Error; err != nil {
- return nil // 连接不存在视为成功
- }
-
- // 使用事务删除
- return s.db.Transaction(func(tx *gorm.DB) error {
- // 清理关联数据
- tx.Where("connection_id = ?", id).Delete(&models.SqlResultHistory{})
- tx.Where("connection_id = ?", id).Delete(&models.SqlTab{})
-
- // 删除连接
- if err := tx.Delete(&conn).Error; err != nil {
- return err
- }
-
- // 关闭连接池
- dbclient.GetPool().CloseConnection(id, conn.Type)
- return nil
- })
-}
-
-// resolvePassword 解析密码(编辑模式下从已保存连接中获取)
-func (s *ConnectionService) resolvePassword(id uint, password string) (string, error) {
- if id > 0 && password == "" {
- conn, err := s.GetConnection(id)
- if err != nil {
- return "", fmt.Errorf("获取连接信息失败: %v", err)
- }
- decryptPassword, err := crypto.DecryptPassword(conn.Password)
- if err != nil {
- return "", fmt.Errorf("密码解密失败: %v", err)
- }
- return decryptPassword, nil
- }
- return password, nil
-}
-
-// parseMongoOptions 解析 MongoDB 连接选项
-func parseMongoOptions(options string) (authSource, authMechanism string) {
- if options == "" {
- return "", ""
- }
- var opts map[string]interface{}
- if err := json.Unmarshal([]byte(options), &opts); err != nil {
- return "", ""
- }
- authSource, _ = opts["authSource"].(string)
- authMechanism, _ = opts["authMechanism"].(string)
- return authSource, authMechanism
-}
-
-// TestConnection 测试连接(需要根据类型调用不同的测试方法)
-func (s *ConnectionService) TestConnection(conn *models.DbConnection) error {
- password, err := crypto.DecryptPassword(conn.Password)
- if err != nil {
- return fmt.Errorf("密码解密失败: %v", err)
- }
-
- authSource, authMechanism := parseMongoOptions(conn.Options)
-
- return s.testConnectionByType(conn.Type, conn.Host, conn.Port, conn.Username, password, conn.Database, authSource, authMechanism)
-}
-
-// testConnectionByType 根据类型调用对应的测试方法
-func (s *ConnectionService) testConnectionByType(dbType, host string, port int, username, password, database, authSource, authMechanism string) error {
- switch dbType {
- case "mysql":
- return testMySQLConnection(host, port, username, password, database)
- case "redis":
- return testRedisConnection(host, port, password)
- case "mongo":
- return testMongoConnection(host, port, username, password, database, authSource, authMechanism)
- default:
- return fmt.Errorf("不支持的数据库类型: %s", dbType)
- }
-}
-
-// testMySQLConnection 测试 MySQL 连接
-func testMySQLConnection(host string, port int, username, password, database string) error {
- return dbclient.TestMySQLConnection(host, port, username, password, database)
-}
-
-// testRedisConnection 测试 Redis 连接
-func testRedisConnection(host string, port int, password string) error {
- return dbclient.TestRedisConnection(host, port, password)
-}
-
-// testMongoConnection 测试 MongoDB 连接
-func testMongoConnection(host string, port int, username, password, database, authSource, authMechanism string) error {
- return dbclient.TestMongoConnectionWithOptions(host, port, username, password, database, authSource, authMechanism)
-}
-
-// TestConnectionWithParams 使用参数测试连接(不保存数据)
-func (s *ConnectionService) TestConnectionWithParams(dbType, host string, port int, username, password, database, options string, id uint) error {
- password, err := s.resolvePassword(id, password)
- if err != nil {
- return err
- }
-
- authSource, authMechanism := parseMongoOptions(options)
- return s.testConnectionByType(dbType, host, port, username, password, database, authSource, authMechanism)
-}
-
-// LoadAllDatabases 加载全部数据库列表
-func (s *ConnectionService) LoadAllDatabases(dbType, host string, port int, username, password, database, options string, id uint) ([]string, error) {
- password, err := s.resolvePassword(id, password)
- if err != nil {
- return nil, err
- }
-
- authSource, authMechanism := parseMongoOptions(options)
-
- // 根据类型加载数据库列表
- switch dbType {
- case "mysql":
- return loadMySQLDatabases(host, port, username, password, database)
- case "mongo":
- return loadMongoDatabasesWithOptions(host, port, username, password, database, authSource, authMechanism)
- case "redis":
- // Redis 没有数据库概念,返回空列表
- return []string{}, nil
- default:
- return nil, fmt.Errorf("不支持的数据库类型: %s", dbType)
- }
-}
-
-// loadMySQLDatabases 加载 MySQL 数据库列表
-func loadMySQLDatabases(host string, port int, username, password, defaultDatabase string) ([]string, error) {
- config := &dbclient.MySQLConfig{
- Host: host,
- Port: port,
- Username: username,
- Password: password,
- Database: defaultDatabase,
- }
- client, err := dbclient.NewMySQLClient(config)
- if err != nil {
- return nil, err
- }
- defer client.Close()
-
- return client.ListDatabases(context.Background())
-}
-
-// loadMongoDatabasesWithOptions 加载 MongoDB 数据库列表(使用解析后的选项)
-func loadMongoDatabasesWithOptions(host string, port int, username, password, defaultDatabase, authSource, authMechanism string) ([]string, error) {
- mongoConfig := &dbclient.MongoConfig{
- Host: host,
- Port: port,
- Username: username,
- Password: password,
- Database: defaultDatabase,
- AuthSource: authSource,
- AuthMechanism: authMechanism,
- }
- client, err := dbclient.NewMongoClient(mongoConfig)
- if err != nil {
- return nil, err
- }
- defer client.Close()
-
- return client.ListDatabases(context.Background())
-}
diff --git a/internal/storage/models/file.go b/internal/storage/models/file.go
deleted file mode 100644
index b104ebf..0000000
--- a/internal/storage/models/file.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package models
-
-import (
- "time"
-)
-
-// SqlFile SQL 文件记录
-type SqlFile struct {
- ID uint `gorm:"primaryKey" json:"id"`
- Name string `gorm:"type:varchar(200);not null" json:"name"` // 文件名
- Path string `gorm:"type:varchar(500);not null;uniqueIndex" json:"path"` // 文件路径
- Content string `gorm:"type:text" json:"content"` // 文件内容
- CreatedAt time.Time `json:"created_at"`
- UpdatedAt time.Time `json:"updated_at"`
-}
-
-// TableName 指定表名
-func (SqlFile) TableName() string {
- return "sql_file"
-}
diff --git a/internal/storage/models/version.go b/internal/storage/models/version.go
deleted file mode 100644
index 6ef0a94..0000000
--- a/internal/storage/models/version.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package models
-
-import "time"
-
-// Version 版本信息
-type Version struct {
- ID int `gorm:"primaryKey" json:"id"` // 主键ID
- Version string `gorm:"type:varchar(20);not null;uniqueIndex" json:"version"` // 版本号(语义化版本,如1.0.0)
- DownloadURL string `gorm:"type:varchar(500)" json:"download_url"` // 下载地址(更新包下载URL)
- Changelog string `gorm:"type:text" json:"changelog"` // 更新日志(Markdown格式)
- ForceUpdate int `gorm:"type:tinyint;not null;default:0" json:"force_update"` // 是否强制更新(1:是 0:否)
- ReleaseDate *time.Time `gorm:"type:date" json:"release_date"` // 发布日期
- CreatedAt time.Time `gorm:"autoCreateTime:false" json:"created_at"` // 创建时间(由程序设置)
- UpdatedAt time.Time `gorm:"autoUpdateTime:false" json:"updated_at"` // 更新时间(由程序设置)
-}
-
-// TableName 指定表名
-func (Version) TableName() string {
- return "sys_version"
-}
diff --git a/internal/storage/repository/result_repo.go b/internal/storage/repository/result_repo.go
index 08b47eb..e9ee2ac 100644
--- a/internal/storage/repository/result_repo.go
+++ b/internal/storage/repository/result_repo.go
@@ -5,17 +5,13 @@ import (
"u-desk/internal/storage"
"u-desk/internal/storage/models"
"gorm.io/gorm"
- "time"
)
type ResultRepository interface {
Save(connectionID uint, database, sql string, resultType string, data interface{}, columns []string, rowsAffected int, executionTime int64) (*models.SqlResultHistory, error)
FindByID(id uint) (*models.SqlResultHistory, error)
- FindByConnection(connectionID uint, limit int) ([]models.SqlResultHistory, error)
Search(connectionID *uint, keyword string, limit, offset int) ([]models.SqlResultHistory, int64, error)
Delete(id uint) error
- DeleteByConnection(connectionID uint) error
- DeleteOld(keepDays int) error
}
type resultRepository struct {
@@ -61,15 +57,6 @@ func (r *resultRepository) FindByID(id uint) (*models.SqlResultHistory, error) {
return &history, err
}
-func (r *resultRepository) FindByConnection(connectionID uint, limit int) ([]models.SqlResultHistory, error) {
- var histories []models.SqlResultHistory
- query := r.db.Where("connection_id = ?", connectionID).Order("created_at DESC")
- if limit > 0 {
- query = query.Limit(limit)
- }
- return histories, query.Find(&histories).Error
-}
-
func (r *resultRepository) Search(connectionID *uint, keyword string, limit, offset int) ([]models.SqlResultHistory, int64, error) {
query := r.db.Model(&models.SqlResultHistory{})
@@ -101,10 +88,3 @@ func (r *resultRepository) Delete(id uint) error {
return r.db.Delete(&models.SqlResultHistory{}, id).Error
}
-func (r *resultRepository) DeleteByConnection(connectionID uint) error {
- return r.db.Where("connection_id = ?", connectionID).Delete(&models.SqlResultHistory{}).Error
-}
-
-func (r *resultRepository) DeleteOld(keepDays int) error {
- return r.db.Where("created_at < ?", time.Now().AddDate(0, 0, -keepDays)).Delete(&models.SqlResultHistory{}).Error
-}
diff --git a/internal/storage/repository/tab_repo.go b/internal/storage/repository/tab_repo.go
index 3873daf..2100106 100644
--- a/internal/storage/repository/tab_repo.go
+++ b/internal/storage/repository/tab_repo.go
@@ -10,7 +10,6 @@ type TabRepository interface {
SaveAll(tabs []models.SqlTab) error
FindAll() ([]models.SqlTab, error)
Delete(id uint) error
- DeleteAll() error
}
type tabRepository struct {
@@ -50,6 +49,3 @@ func (r *tabRepository) Delete(id uint) error {
return r.db.Delete(&models.SqlTab{}, id).Error
}
-func (r *tabRepository) DeleteAll() error {
- return r.db.Where("1=1").Delete(&models.SqlTab{}).Error
-}
diff --git a/web/src/components/FileSystem/components/FileEditorPanel.vue b/web/src/components/FileSystem/components/FileEditorPanel.vue
index 60a829c..76d66f3 100644
--- a/web/src/components/FileSystem/components/FileEditorPanel.vue
+++ b/web/src/components/FileSystem/components/FileEditorPanel.vue
@@ -29,16 +29,22 @@
{{ config.currentFileName }}
-