Files
go-site-clone/app.go
2025-11-22 17:49:24 +08:00

408 lines
9.8 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 (
"fmt"
"go-site-clone/config"
"go-site-clone/services"
"go-site-clone/storage"
"go-site-clone/types"
"go-site-clone/utils"
"log"
"net/url"
"os"
"os/exec"
"path/filepath"
"github.com/go-rod/rod/lib/proto"
"github.com/wailsapp/wails/v3/pkg/application"
)
type App struct {
app *application.App
nginxService *services.NginxService
store *storage.Store
}
var siteService services.SiteService
// 服务启动时初始化 nginx 服务和数据库
func (a *App) OnStartup() {
// 初始化数据库
dbPath := "data/site-clone.db"
store, err := storage.NewStore(dbPath)
if err != nil {
log.Printf("初始化数据库失败: %v", err)
} else {
a.store = store
log.Printf("数据库初始化成功")
}
// 初始化 nginx 服务
a.nginxService = &services.NginxService{}
// 检测 nginx 是否已经在运行
running, err := a.nginxService.CheckStatus()
if err == nil && running {
a.nginxService.Running = true
fmt.Println("nginx 服务已检测到正在运行")
} else {
a.nginxService.Running = false
fmt.Println("nginx 服务未运行")
}
}
// 服务关闭时不关闭 nginx但关闭数据库
func (a *App) OnShutdown() {
// 关闭数据库连接
if a.store != nil {
if err := a.store.Close(); err != nil {
log.Printf("关闭数据库失败: %v", err)
} else {
log.Println("数据库已关闭")
}
}
// 不做其他事,让 nginx 继续运行
fmt.Println("应用关闭nginx 服务保持运行状态")
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
// func (a *App) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
// return nil
// }
// func (a *App) ServiceShutdown(ctx context.Context) error {
// return nil
// }
//
// ========== 网站克隆相关方法 ==========
func (a *App) GetResources(rawURL string) *services.ResourcesList {
RequestParamsAll, RouterAll := siteService.GetAllResources(rawURL)
// 再次去重 根据url链接去除重复项
dataList := siteService.DeduplicationRequestByUrl(RequestParamsAll)
resources := &services.ResourcesList{
Dom: RouterAll,
}
// 将页面及资源一起返回
for _, v := range dataList {
if v.Type == proto.NetworkResourceTypeImage {
resources.Image = append(resources.Image, v.URL)
}
if v.Type == proto.NetworkResourceTypeStylesheet {
resources.CSS = append(resources.CSS, v.URL)
}
if v.Type == proto.NetworkResourceTypeScript {
resources.Script = append(resources.Script, v.URL)
}
if v.Type == proto.NetworkResourceTypeMedia {
resources.Video = append(resources.Video, v.URL)
}
}
return resources
}
// 下载网站资源
func (a *App) DownloadSite(uri string, obj services.ResourcesList) bool {
// 将页面及资源一起返回
parsed, _ := url.Parse(uri)
var File utils.File
if len(obj.CSS) > 0 {
for k, v := range obj.CSS {
u, _ := url.Parse(v)
if parsed.Hostname() == u.Hostname() {
File.Download(v)
}
a.app.Event.Emit("download:css", k)
}
}
if len(obj.Script) > 0 {
for k, v := range obj.Script {
u, _ := url.Parse(v)
if parsed.Hostname() == u.Hostname() {
File.Download(v)
}
a.app.Event.Emit("download:script", k)
}
}
if len(obj.Image) > 0 {
for k, v := range obj.Image {
u, _ := url.Parse(v)
if parsed.Hostname() == u.Hostname() {
File.Download(v)
}
a.app.Event.Emit("download:image", k)
}
}
if len(obj.Video) > 0 {
for k, v := range obj.Video {
u, _ := url.Parse(v)
if parsed.Hostname() == u.Hostname() {
File.Download(v)
}
a.app.Event.Emit("download:video", k)
}
}
if len(obj.Dom) > 0 {
for k, v := range obj.Dom {
u, _ := url.Parse(v)
if parsed.Hostname() == u.Hostname() {
File.HTMLDownload(v)
}
a.app.Event.Emit("download:dom", k)
}
}
return true
}
// 获取本地已下载网站列表
func (a *App) GetDownloadList() []utils.FileDir {
return siteService.GetLocalSiteList()
}
// 打开网站文件夹
func (a *App) OpenSiteFileDir(pathDir string) bool {
appConfig, _ := config.LoadConfig()
newPath := filepath.Join(appConfig.SiteFileDir, pathDir)
cmd := exec.Command("explorer", newPath)
err := cmd.Start()
if err != nil {
fmt.Println("打开文件夹失败:", err)
return false
}
return true
}
// 删除网站文件夹
func (a *App) DeleteSiteFileDir(pathDir string) bool {
appConfig, _ := config.LoadConfig()
newPath := filepath.Join(appConfig.SiteFileDir, pathDir)
err := os.RemoveAll(newPath)
if err != nil {
fmt.Println("删除文件夹失败:", err)
return false
}
return true
}
// ========== Nginx 相关方法 ==========
// 启动 Nginx
func (a *App) StartNginx() error {
return a.nginxService.StartNginx()
}
// 停止 Nginx
func (a *App) StopNginx() error {
return a.nginxService.StopNginx()
}
// 重启 Nginx
func (a *App) RestartNginx() error {
return a.nginxService.RestartNginx()
}
// 重载 Nginx 配置
func (a *App) ReloadNginx() error {
return a.nginxService.ReloadNginx()
}
// 检查 Nginx 状态
func (a *App) CheckNginxStatus() (bool, error) {
running, err := a.nginxService.CheckStatus()
if err == nil {
a.nginxService.Running = running
}
return running, err
}
// 测试 Nginx 配置
func (a *App) TestNginxConfig() error {
return a.nginxService.TestConfig()
}
// 添加站点配置
func (a *App) AddNginxSite(site types.NginxSiteConfig) error {
// 先保存到数据库
if a.store != nil {
if err := a.store.AddSite(site); err != nil {
return fmt.Errorf("保存站点到数据库失败: %v", err)
}
}
// 再创建 nginx 配置文件
return a.nginxService.AddSite(site)
}
// 删除站点配置
func (a *App) DeleteNginxSite(siteName string) error {
// 从数据库删除
if a.store != nil {
if err := a.store.DeleteSite(siteName); err != nil {
log.Printf("警告: 从数据库删除站点失败: %v", err)
}
}
// 删除 nginx 配置文件
return a.nginxService.DeleteSite(siteName)
}
// 更新站点配置
func (a *App) UpdateNginxSite(site types.NginxSiteConfig) error {
// 更新数据库
if a.store != nil {
if err := a.store.UpdateSite(site); err != nil {
return fmt.Errorf("更新数据库失败: %v", err)
}
}
// 更新 nginx 配置
return a.nginxService.UpdateSite(site)
}
// 获取所有站点配置
func (a *App) GetAllNginxSites() ([]types.NginxSiteConfig, error) {
// 优先从数据库读取
if a.store != nil {
sites, err := a.store.GetAllSites()
if err == nil && len(sites) > 0 {
return sites, nil
}
}
// 如果数据库为空,从 nginx 配置文件读取并同步到数据库
sites, err := a.nginxService.GetAllSites()
if err != nil {
return nil, err
}
// 同步到数据库
if a.store != nil {
for _, site := range sites {
a.store.AddSite(site)
}
}
return sites, nil
}
// 启用站点
func (a *App) EnableNginxSite(siteName string) error {
// 更新数据库
if a.store != nil {
if err := a.store.UpdateSiteStatus(siteName, true); err != nil {
log.Printf("警告: 更新数据库失败: %v", err)
}
}
return a.nginxService.EnableSite(siteName)
}
// 禁用站点
func (a *App) DisableNginxSite(siteName string) error {
// 更新数据库
if a.store != nil {
if err := a.store.UpdateSiteStatus(siteName, false); err != nil {
log.Printf("警告: 更新数据库失败: %v", err)
}
}
return a.nginxService.DisableSite(siteName)
}
// 获取 Nginx 访问日志
func (a *App) GetNginxAccessLog(lines int) ([]string, error) {
return a.nginxService.GetAccessLog(lines)
}
// 获取 Nginx 错误日志
func (a *App) GetNginxErrorLog(lines int) ([]string, error) {
return a.nginxService.GetErrorLog(lines)
}
// 清空 Nginx 日志
func (a *App) ClearNginxLogs() error {
return a.nginxService.ClearLogs()
}
// 获取可用端口号(从 startPort 开始查找)
func (a *App) getAvailablePort(startPort int) int {
// 获取所有已配置的站点
sites, err := a.nginxService.GetAllSites()
if err != nil {
return startPort
}
// 收集已使用的端口
usedPorts := make(map[int]bool)
for _, site := range sites {
if site.Port > 0 {
usedPorts[site.Port] = true
}
}
// 查找可用端口
port := startPort
for {
if !usedPorts[port] {
return port
}
port++
if port > 65535 {
return startPort // 如果超出范围,返回默认端口
}
}
}
// ========== 下载记录相关方法 ==========
// AddDownloadRecord 添加下载记录
func (a *App) AddDownloadRecord(record storage.DownloadRecord) error {
if a.store == nil {
return fmt.Errorf("数据库未初始化")
}
return a.store.AddDownloadRecord(record)
}
// GetAllDownloadRecords 获取所有下载记录
func (a *App) GetAllDownloadRecords() ([]storage.DownloadRecord, error) {
if a.store == nil {
return nil, fmt.Errorf("数据库未初始化")
}
return a.store.GetAllDownloadRecords()
}
// GetRecentDownloadRecords 获取最近的下载记录
func (a *App) GetRecentDownloadRecords(limit int) ([]storage.DownloadRecord, error) {
if a.store == nil {
return nil, fmt.Errorf("数据库未初始化")
}
return a.store.GetRecentDownloadRecords(limit)
}
// GetDownloadStats 获取下载统计
func (a *App) GetDownloadStats() (map[string]interface{}, error) {
if a.store == nil {
return nil, fmt.Errorf("数据库未初始化")
}
return a.store.GetDownloadStats()
}
// DeleteDownloadRecord 删除下载记录
func (a *App) DeleteDownloadRecord(id string) error {
if a.store == nil {
return fmt.Errorf("数据库未初始化")
}
return a.store.DeleteDownloadRecord(id)
}
// ClearOldDownloadRecords 清理旧的下载记录
func (a *App) ClearOldDownloadRecords(days int) (int, error) {
if a.store == nil {
return 0, fmt.Errorf("数据库未初始化")
}
return a.store.ClearOldDownloadRecords(days)
}
// BackupDatabase 备份数据库
func (a *App) BackupDatabase(backupPath string) error {
if a.store == nil {
return fmt.Errorf("数据库未初始化")
}
return a.store.Backup(backupPath)
}