新增:Markdown编辑器/数据库优化/安全修复
- Markdown 编辑器:实时预览、PDF 导出、独立查看器 - 数据库优化:动态连接池、查询缓存、Redis Pipeline - 窗口置顶功能 - 文件系统增强:右键菜单、编辑器集成、收藏夹重构 - 安全修复:XSS 防护、路径穿越、HTML 注入 - 代码质量:正则预编译、缓存锁优化、死代码清理
This commit is contained in:
159
app.go
159
app.go
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
"u-desk/internal/api"
|
||||
"u-desk/internal/common"
|
||||
"u-desk/internal/database"
|
||||
@@ -23,15 +24,17 @@ import (
|
||||
|
||||
// App 应用结构体
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
db *database.DB
|
||||
connectionAPI *api.ConnectionAPI
|
||||
sqlAPI *api.SqlAPI
|
||||
tabAPI *api.TabAPI
|
||||
updateAPI *api.UpdateAPI
|
||||
configAPI *api.ConfigAPI
|
||||
fileServer *http.Server
|
||||
filesystem *filesystem.FileSystemService
|
||||
ctx context.Context
|
||||
db *database.DB
|
||||
connectionAPI *api.ConnectionAPI
|
||||
sqlAPI *api.SqlAPI
|
||||
tabAPI *api.TabAPI
|
||||
updateAPI *api.UpdateAPI
|
||||
configAPI *api.ConfigAPI
|
||||
pdfAPI *api.PdfAPI
|
||||
fileServer *http.Server
|
||||
filesystem *filesystem.FileSystemService
|
||||
isAlwaysOnTop bool
|
||||
}
|
||||
|
||||
// NewApp 创建新的应用实例
|
||||
@@ -60,6 +63,17 @@ func (a *App) Startup(ctx context.Context) {
|
||||
// 2.5. 迁移旧配置
|
||||
_ = a.configAPI.MigrateTabConfig()
|
||||
|
||||
// 2.6. 初始化PDF导出API
|
||||
fmt.Println("[启动] 初始化PDF导出模块...")
|
||||
pdfAPI, err := api.NewPdfAPI()
|
||||
if err != nil {
|
||||
fmt.Printf("[启动] PDF导出API初始化失败: %v\n", err)
|
||||
// PDF导出失败不应影响应用启动,所以只警告不panic
|
||||
} else {
|
||||
a.pdfAPI = pdfAPI
|
||||
fmt.Println("[启动] PDF导出模块初始化完成")
|
||||
}
|
||||
|
||||
// 3. 初始化版本号(提前触发缓存,避免后续重复计算)
|
||||
version := service.GetCurrentVersion()
|
||||
fmt.Printf("[启动] 当前版本: %s\n", version)
|
||||
@@ -545,6 +559,16 @@ func (a *App) WindowIsMaximized() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// WindowToggleAlwaysOnTop 切换窗口置顶
|
||||
func (a *App) WindowToggleAlwaysOnTop() bool {
|
||||
if a.ctx == nil {
|
||||
return false
|
||||
}
|
||||
a.isAlwaysOnTop = !a.isAlwaysOnTop
|
||||
runtime.WindowSetAlwaysOnTop(a.ctx, a.isAlwaysOnTop)
|
||||
return a.isAlwaysOnTop
|
||||
}
|
||||
|
||||
// ========== SQL 标签页管理接口 ==========
|
||||
|
||||
// SaveSqlTabs 保存 SQL 标签页列表
|
||||
@@ -630,7 +654,11 @@ func (a *App) startAutoUpdateCheck() {
|
||||
}
|
||||
|
||||
config, err := a.updateAPI.GetUpdateConfig()
|
||||
if err != nil || !config["success"].(bool) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
success, ok := config["success"].(bool)
|
||||
if !ok || !success {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -863,3 +891,114 @@ func (a *App) initFilesystemModule() {
|
||||
|
||||
fmt.Println("[模块] 文件系统模块初始化完成")
|
||||
}
|
||||
|
||||
// ExportPDF 导出PDF文件
|
||||
func (a *App) ExportPDF(content string, title string, fileName string, fontSize int, pageWidth int, pageHeight int) (map[string]interface{}, error) {
|
||||
if a.pdfAPI == nil {
|
||||
return map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "PDF导出功能未初始化",
|
||||
}, fmt.Errorf("PDF导出功能未初始化")
|
||||
}
|
||||
|
||||
req := api.PdfExportRequest{
|
||||
Content: content,
|
||||
Title: title,
|
||||
FileName: fileName,
|
||||
FontSize: fontSize,
|
||||
PageWidth: pageWidth,
|
||||
PageHeight: pageHeight,
|
||||
}
|
||||
|
||||
result, err := a.pdfAPI.ExportMarkdownToPDF(req)
|
||||
if err != nil {
|
||||
return map[string]interface{}{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
}, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"success": result.Success,
|
||||
"message": result.Message,
|
||||
"path": result.Path,
|
||||
"size": result.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SelectPDFSaveDirectory 选择PDF保存目录
|
||||
func (a *App) SelectPDFSaveDirectory() (string, error) {
|
||||
if a.pdfAPI == nil {
|
||||
return "", fmt.Errorf("PDF导出功能未初始化")
|
||||
}
|
||||
|
||||
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 strings.HasPrefix(line, "1. ") || strings.HasPrefix(line, "2. ") || strings.HasPrefix(line, "3. ") {
|
||||
// 有序列表
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user