Private
Public Access
1
0
Files
u-desk/app.go

596 lines
17 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"context"
"fmt"
"u-desk/internal/api"
"u-desk/internal/database"
"u-desk/internal/filesystem"
"u-desk/internal/storage"
"u-desk/internal/system"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// App 应用结构体
type App struct {
ctx context.Context
db *database.DB
connectionAPI *api.ConnectionAPI
sqlAPI *api.SqlAPI
tabAPI *api.TabAPI
updateAPI *api.UpdateAPI
fileServer *http.Server
}
// NewApp 创建新的应用实例
func NewApp() *App {
return &App{}
}
// Startup 应用启动时调用
func (a *App) Startup(ctx context.Context) {
a.ctx = ctx
// 1. 核心初始化SQLite必须同步很快
sqliteDB, err := storage.InitFast()
if err != nil {
panic(fmt.Sprintf("SQLite 初始化失败,应用无法启动: %v", err))
}
// 2. 快速初始化核心 API都是毫秒级操作不影响启动速度
if err := a.initCoreAPIs(); err != nil {
panic(fmt.Sprintf("核心 API 初始化失败: %v", err))
}
// 3. 异步初始化:文件服务器(不等待)
go a.startFileServer()
// 4. 异步初始化UpdateAPI涉及网络请求完全异步
go func() {
if updateAPI, err := api.NewUpdateAPI("https://img.1216.top/u-desk/last-version.json"); err == nil {
a.updateAPI = updateAPI
a.updateAPI.SetContext(ctx)
}
}()
_ = sqliteDB // 标记已使用
}
// startFileServer 启动文件服务器
func (a *App) startFileServer() {
// 启动独立的本地文件服务器(使用 filesystem 包中的实现)
if _, err := filesystem.StartLocalFileServer(); err != nil {
fmt.Printf("[文件服务器] 启动失败: %v\n", err)
return
}
// 创建一个占位服务器用于保持引用(实际服务器由 StartLocalFileServer 管理)
a.fileServer = &http.Server{
Addr: "localhost:18765",
}
fmt.Println("[文件服务器] 启动在 http://localhost:18765")
}
// Shutdown 应用关闭时调用
func (a *App) Shutdown(ctx context.Context) {
// 停止文件服务器
if a.fileServer != nil {
fmt.Println("[文件服务器] 正在关闭...")
a.fileServer.Shutdown(ctx)
}
}
// 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()
}
// GetCPUInfo 获取 CPU 信息
func (a *App) GetCPUInfo() (map[string]interface{}, error) {
return system.GetCPUInfo()
}
// GetMemoryInfo 获取内存信息
func (a *App) GetMemoryInfo() (map[string]interface{}, error) {
return system.GetMemoryInfo()
}
// GetDiskInfo 获取磁盘信息
func (a *App) GetDiskInfo() ([]map[string]interface{}, error) {
return system.GetDiskInfo()
}
// ReadFile 读取文件
func (a *App) ReadFile(path string) (string, error) {
return filesystem.ReadFile(path)
}
// WriteFileRequest 写入文件请求结构体
type WriteFileRequest struct {
Path string `json:"path"`
Content string `json:"content"`
}
// WriteFile 写入文件
func (a *App) WriteFile(req WriteFileRequest) error {
return filesystem.WriteFile(req.Path, req.Content)
}
// ListDir 列出目录
func (a *App) ListDir(path string) ([]map[string]interface{}, error) {
return filesystem.ListDir(path)
}
// CreateDir 创建目录
func (a *App) CreateDir(path string) error {
return filesystem.CreateDir(path)
}
// CreateFile 创建文件
func (a *App) CreateFile(path string) error {
return filesystem.CreateFile(path)
}
// DeletePath 删除文件或目录
func (a *App) DeletePath(path string) error {
return filesystem.DeletePath(path)
}
// GetFileInfo 获取文件信息
func (a *App) GetFileInfo(path string) (map[string]interface{}, error) {
return filesystem.GetFileInfo(path)
}
// GetEnvVars 获取环境变量
func (a *App) GetEnvVars() (map[string]string, error) {
envVars := make(map[string]string)
for _, env := range os.Environ() {
if key, value, found := strings.Cut(env, "="); found {
envVars[key] = value
}
}
return envVars, nil
}
// OpenPath 使用系统默认程序打开文件或目录
func (a *App) OpenPath(path string) error {
return filesystem.OpenPath(path)
}
// ========== Zip 文件操作接口 ==========
// ListZipContents 列出 zip 文件内容
func (a *App) ListZipContents(zipPath string) ([]map[string]interface{}, error) {
return filesystem.ListZipContents(zipPath)
}
// ExtractFileFromZip 从 zip 文件中提取单个文件内容
func (a *App) ExtractFileFromZip(zipPath, filePath string) (string, error) {
return filesystem.ExtractFileFromZip(zipPath, filePath)
}
// ExtractFileFromZipToTemp 从 zip 文件中提取单个文件到临时目录
// 返回临时文件的完整路径,适用于图片等二进制文件
func (a *App) ExtractFileFromZipToTemp(zipPath, filePath string) (string, error) {
return filesystem.ExtractFileFromZipToTemp(zipPath, filePath)
}
// GetZipFileInfo 获取 zip 文件中特定文件的信息
func (a *App) GetZipFileInfo(zipPath, filePath string) (map[string]interface{}, error) {
return filesystem.GetZipFileInfo(zipPath, filePath)
}
// GetCommonPaths 获取常用系统路径
func (a *App) GetCommonPaths() (map[string]string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, err
}
// 获取所有可用驱动器Windows
drives := getSystemDrives()
paths := map[string]string{
"home": homeDir,
"desktop": filepath.Join(homeDir, "Desktop"),
"documents": filepath.Join(homeDir, "Documents"),
"downloads": filepath.Join(homeDir, "Downloads"),
}
// 动态添加所有盘符
for _, drive := range drives {
key := fmt.Sprintf("root_%s", drive[:1])
paths[key] = drive
}
return paths, nil
}
// getSystemDrives 获取系统所有可用驱动器
func getSystemDrives() []string {
var drives []string
// Windows: 检查 A-Z 所有盘符
for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
path := string(drive) + ":\\"
if _, err := os.Stat(path); err == nil {
drives = append(drives, path)
}
}
return drives
}
// ========== 数据库连接管理接口 ==========
// initCoreAPIs 初始化核心 API快速操作毫秒级完成
func (a *App) initCoreAPIs() error {
var err error
// 初始化 ConnectionAPI
if a.connectionAPI, err = api.NewConnectionAPI(); err != nil {
return err
}
// 初始化 SqlAPI
if a.sqlAPI, err = api.NewSqlAPI(); err != nil {
return err
}
// 初始化 TabAPI
if a.tabAPI, err = api.NewTabAPI(); err != nil {
return err
}
return nil
}
// SaveDbConnection 保存数据库连接配置
func (a *App) SaveDbConnection(req api.SaveConnectionRequest) error {
return a.connectionAPI.SaveDbConnection(req)
}
// ListDbConnections 获取连接列表
func (a *App) ListDbConnections() ([]map[string]interface{}, error) {
return a.connectionAPI.ListDbConnections()
}
// DeleteDbConnection 删除连接配置
func (a *App) DeleteDbConnection(id uint) error {
return a.connectionAPI.DeleteDbConnection(id)
}
// TestDbConnection 测试连接通过已保存的连接ID
func (a *App) TestDbConnection(id uint) error {
return a.connectionAPI.TestDbConnection(id)
}
// TestDbConnectionWithParams 测试数据库连接(直接传入参数,不保存数据)
func (a *App) TestDbConnectionWithParams(req api.TestConnectionRequest) error {
return a.connectionAPI.TestDbConnectionWithParams(req)
}
// ExecuteSQL 执行 SQL 语句
// 注意SQL 语句应该已经包含分页信息LIMIT 和 OFFSET由客户端添加
func (a *App) ExecuteSQL(connectionId uint, sqlStr string, database string) (map[string]interface{}, error) {
return a.sqlAPI.ExecuteSQL(connectionId, sqlStr, database)
}
// GetDatabases 获取数据库列表
func (a *App) GetDatabases(connectionId uint) ([]string, error) {
return a.sqlAPI.GetDatabases(connectionId)
}
// GetTables 获取表列表
func (a *App) GetTables(connectionId uint, database string) ([]string, error) {
return a.sqlAPI.GetTables(connectionId, database)
}
// GetTableStructure 获取表结构
func (a *App) GetTableStructure(connectionId uint, database, tableName string) (map[string]interface{}, error) {
return a.sqlAPI.GetTableStructure(connectionId, database, tableName)
}
// GetIndexes 获取索引列表
func (a *App) GetIndexes(connectionId uint, database, tableName string) ([]map[string]interface{}, error) {
return a.sqlAPI.GetIndexes(connectionId, database, tableName)
}
// PreviewTableStructure 预览表结构变更
func (a *App) PreviewTableStructure(connectionId uint, database, tableName string, structure map[string]interface{}) ([]string, error) {
return a.sqlAPI.PreviewTableStructure(connectionId, database, tableName, structure)
}
// UpdateTableStructure 更新表结构
func (a *App) UpdateTableStructure(connectionId uint, database, tableName string, structure map[string]interface{}) ([]string, error) {
return a.sqlAPI.UpdateTableStructure(connectionId, database, tableName, structure)
}
// SaveResult 手动保存执行结果
func (a *App) SaveResult(connectionId uint, database, sql string, resultType string, data interface{}, columns []string, rowsAffected int, executionTime int64) (map[string]interface{}, error) {
return a.sqlAPI.SaveResult(connectionId, database, sql, resultType, data, columns, rowsAffected, executionTime)
}
// GetResultHistory 获取结果历史
func (a *App) GetResultHistory(connectionId *uint, keyword string, limit, offset int) (map[string]interface{}, error) {
return a.sqlAPI.GetResultHistory(connectionId, keyword, limit, offset)
}
// GetResultHistoryByID 根据ID获取结果历史
func (a *App) GetResultHistoryByID(id uint) (map[string]interface{}, error) {
return a.sqlAPI.GetResultHistoryByID(id)
}
// DeleteResultHistory 删除结果历史
func (a *App) DeleteResultHistory(id uint) error {
return a.sqlAPI.DeleteResultHistory(id)
}
// Reload 重新加载窗口(用于菜单项)
func (a *App) Reload() {
if a.ctx != nil {
runtime.WindowReload(a.ctx)
}
}
// ClearCache 清理本地缓存(用于菜单项)
func (a *App) ClearCache() {
if a.ctx != nil {
// 发送事件到前端,让前端清理 localStorage
runtime.EventsEmit(a.ctx, "clear-cache")
}
}
// ========== 窗口控制方法 ==========
// WindowMinimize 最小化窗口
func (a *App) WindowMinimize() {
if a.ctx != nil {
runtime.WindowMinimise(a.ctx)
}
}
// WindowMaximize 最大化/还原窗口
func (a *App) WindowMaximize() {
if a.ctx != nil {
if runtime.WindowIsMaximised(a.ctx) {
runtime.WindowUnmaximise(a.ctx)
} else {
runtime.WindowMaximise(a.ctx)
}
}
}
// WindowClose 关闭窗口
func (a *App) WindowClose() {
if a.ctx != nil {
runtime.Quit(a.ctx)
}
}
// WindowIsMaximized 检查窗口是否最大化
func (a *App) WindowIsMaximized() bool {
if a.ctx != nil {
return runtime.WindowIsMaximised(a.ctx)
}
return false
}
// ========== SQL 标签页管理接口 ==========
// SaveSqlTabs 保存 SQL 标签页列表
func (a *App) SaveSqlTabs(tabs []map[string]interface{}) error {
return a.tabAPI.SaveSqlTabs(tabs)
}
// ListSqlTabs 获取 SQL 标签页列表
func (a *App) ListSqlTabs() ([]map[string]interface{}, error) {
return a.tabAPI.ListSqlTabs()
}
// ========== 版本更新管理接口 ==========
// CheckUpdate 检查更新UpdateAPI 可能尚未初始化完成)
func (a *App) CheckUpdate() (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.CheckUpdate()
}
// GetCurrentVersion 获取当前版本号
func (a *App) GetCurrentVersion() (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.GetCurrentVersion()
}
// GetUpdateConfig 获取更新配置
func (a *App) GetUpdateConfig() (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.GetUpdateConfig()
}
// SetUpdateConfig 设置更新配置
func (a *App) SetUpdateConfig(autoCheckEnabled bool, checkIntervalMinutes int, checkURL string) (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.SetUpdateConfig(autoCheckEnabled, checkIntervalMinutes, checkURL)
}
// DownloadUpdate 下载更新包
func (a *App) DownloadUpdate(downloadURL string) (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.DownloadUpdate(downloadURL)
}
// InstallUpdate 安装更新包
func (a *App) InstallUpdate(installerPath string, autoRestart bool) (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.InstallUpdate(installerPath, autoRestart)
}
// InstallUpdateWithHash 安装更新包(带哈希验证)
func (a *App) InstallUpdateWithHash(installerPath string, autoRestart bool, expectedHash string, hashType string) (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.InstallUpdateWithHash(installerPath, autoRestart, expectedHash, hashType)
}
// VerifyUpdateFile 验证更新文件哈希值
func (a *App) VerifyUpdateFile(filePath string, expectedHash string, hashType string) (map[string]interface{}, error) {
if a.updateAPI == nil {
return nil, fmt.Errorf("更新功能正在初始化中")
}
return a.updateAPI.VerifyUpdateFile(filePath, expectedHash, hashType)
}
// ========== 应用生命周期管理 ==========
// shutdown 应用关闭时调用,清理资源
func (a *App) shutdown(ctx context.Context) {
// 关闭审计日志
filesystem.CloseAudit()
// 停止文件服务器
if a.fileServer != nil {
_ = a.fileServer.Shutdown(ctx)
}
}
// ========== 审计日志接口 ==========
// GetAuditLogs 获取审计日志
func (a *App) GetAuditLogs(limit int) ([]map[string]interface{}, error) {
userDataDir := getUserDataDir()
logDir := filepath.Join(userDataDir, "logs")
entries, err := filesystem.GetRecentLogs(logDir, limit)
if err != nil {
return nil, err
}
// 转换为map格式
result := make([]map[string]interface{}, len(entries))
for i, entry := range entries {
result[i] = map[string]interface{}{
"timestamp": entry.Timestamp.Format("2006-01-02 15:04:05"),
"operation": entry.Operation,
"path": entry.Path,
"size": entry.Size,
"is_directory": entry.IsDirectory,
"success": entry.Success,
"error": entry.Error,
}
}
return result, nil
}
// ========== 文件服务器接口 ==========
// GetFileServerURL 获取本地文件服务器的URL
func (a *App) GetFileServerURL() string {
return "http://localhost:18765"
}
// ========== 回收站接口 ==========
// GetRecycleBinEntries 获取回收站条目
func (a *App) GetRecycleBinEntries() ([]map[string]interface{}, error) {
bin := filesystem.GetRecycleBin()
if bin == nil {
return []map[string]interface{}{}, nil
}
entries := bin.ListEntries()
result := make([]map[string]interface{}, len(entries))
for i, entry := range entries {
result[i] = map[string]interface{}{
"original_path": entry.OriginalPath,
"deleted_path": entry.DeletedPath,
"deleted_time": entry.DeletedTime.Format("2006-01-02 15:04:05"),
"size": entry.Size,
"is_directory": entry.IsDirectory,
}
}
return result, nil
}
// RestoreFromRecycleBin 从回收站恢复文件
func (a *App) RestoreFromRecycleBin(recyclePath string) error {
bin := filesystem.GetRecycleBin()
if bin == nil {
return fmt.Errorf("回收站未初始化")
}
return bin.RestoreFromRecycleBin(recyclePath)
}
// DeletePermanently 永久删除回收站中的文件
func (a *App) DeletePermanently(recyclePath string) error {
bin := filesystem.GetRecycleBin()
if bin == nil {
return fmt.Errorf("回收站未初始化")
}
return bin.DeletePermanently(recyclePath)
}
// EmptyRecycleBin 清空回收站
func (a *App) EmptyRecycleBin() error {
bin := filesystem.GetRecycleBin()
if bin == nil {
return fmt.Errorf("回收站未初始化")
}
return bin.Empty()
}