250 lines
7.2 KiB
Go
250 lines
7.2 KiB
Go
/*
|
||
* Copyright (C) 2020-2022, IrineSistiana
|
||
*
|
||
* This file is part of mosdns.
|
||
*
|
||
* mosdns is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* mosdns is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
package coremain
|
||
|
||
import (
|
||
"io"
|
||
"io/fs"
|
||
"net/http"
|
||
"strings"
|
||
|
||
"github.com/go-chi/chi/v5"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
var webUIFS fs.FS
|
||
|
||
// SetWebUIFS 设置 Web UI 文件系统(由 main 包调用)
|
||
func SetWebUIFS(fsys fs.FS) {
|
||
webUIFS = fsys
|
||
}
|
||
|
||
// registerWebUI 注册 Web 管理界面路由(Vue SPA)
|
||
func (m *Mosdns) registerWebUI() {
|
||
// 获取 Vue 构建产物
|
||
distFS, err := fs.Sub(webUIFS, "web-ui/dist")
|
||
if err != nil {
|
||
m.logger.Error("failed to get Vue dist files", zap.Error(err))
|
||
return
|
||
}
|
||
|
||
// 直接处理所有请求
|
||
m.webMux.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
|
||
path := r.URL.Path
|
||
m.logger.Debug("Web UI request", zap.String("path", path))
|
||
|
||
// API 请求交给 API 路由处理(不应该到这里,但作为保护)
|
||
if strings.HasPrefix(path, "/api/") {
|
||
http.NotFound(w, r)
|
||
return
|
||
}
|
||
|
||
// 尝试打开文件
|
||
trimmedPath := strings.TrimPrefix(path, "/")
|
||
if trimmedPath == "" {
|
||
trimmedPath = "index.html"
|
||
}
|
||
|
||
m.logger.Debug("Trying to open file", zap.String("trimmedPath", trimmedPath))
|
||
file, err := distFS.Open(trimmedPath)
|
||
if err == nil {
|
||
// 文件存在,直接返回
|
||
defer file.Close()
|
||
stat, _ := file.Stat()
|
||
if !stat.IsDir() {
|
||
m.logger.Debug("Serving file", zap.String("path", trimmedPath))
|
||
http.ServeContent(w, r, path, stat.ModTime(), file.(io.ReadSeeker))
|
||
return
|
||
}
|
||
} else {
|
||
m.logger.Debug("File not found", zap.String("path", trimmedPath), zap.Error(err))
|
||
}
|
||
|
||
// 文件不存在或是目录,返回 index.html (SPA 路由)
|
||
m.logger.Debug("Serving index.html for SPA")
|
||
m.serveVueIndex(w, r, distFS)
|
||
})
|
||
}
|
||
|
||
// serveVueIndex 提供 Vue SPA 的 index.html
|
||
func (m *Mosdns) serveVueIndex(w http.ResponseWriter, r *http.Request, distFS fs.FS) {
|
||
// 读取 index.html
|
||
indexHTML, err := fs.ReadFile(distFS, "index.html")
|
||
if err != nil {
|
||
m.logger.Error("failed to read Vue index.html", zap.Error(err))
|
||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 设置响应头
|
||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||
w.Header().Set("Pragma", "no-cache")
|
||
w.Header().Set("Expires", "0")
|
||
|
||
// 返回 HTML 内容
|
||
w.Write(indexHTML)
|
||
}
|
||
|
||
// registerWebAPI 注册 Web 管理 API 路由(使用独立的 webMux)
|
||
func (m *Mosdns) registerWebAPI() {
|
||
// Web API 路由组(在 Web UI 服务器上提供 API 代理)
|
||
m.webMux.Route("/api", func(r chi.Router) {
|
||
// 服务器信息和状态
|
||
r.Get("/server/info", m.handleWebServerInfo)
|
||
r.Get("/server/status", m.handleWebServerStatus)
|
||
|
||
// 配置管理
|
||
r.Get("/config", m.handleWebGetConfig)
|
||
r.Put("/config", m.handleWebUpdateConfig)
|
||
r.Post("/config/reload", m.handleWebReloadConfig)
|
||
r.Post("/config/validate", m.handleWebValidateConfig)
|
||
r.Post("/config/backup", m.handleWebBackupConfig)
|
||
|
||
// 域名文件管理
|
||
r.Get("/domain-files", m.handleWebListDomainFiles)
|
||
r.Get("/domain-files/{filename}", m.handleWebGetDomainFile)
|
||
r.Put("/domain-files/{filename}", m.handleWebUpdateDomainFile)
|
||
r.Delete("/domain-files/{filename}", m.handleWebDeleteDomainFile)
|
||
|
||
// 插件管理
|
||
r.Get("/plugins", m.handleWebListPlugins)
|
||
r.Get("/plugins/{tag}/status", m.handleWebPluginStatus)
|
||
|
||
// 统计信息
|
||
r.Get("/stats/detailed", m.handleWebDetailedStats)
|
||
|
||
// 日志管理
|
||
r.Get("/logs", m.handleWebGetLogs)
|
||
r.Post("/logs/clear", m.handleWebClearLogs)
|
||
|
||
// 缓存管理
|
||
r.Post("/cache/flush", m.handleWebFlushCache)
|
||
|
||
// MikroTik 管理
|
||
r.Get("/mikrotik/list", m.handleListMikroTik)
|
||
r.Post("/mikrotik/add", m.handleAddMikroTik)
|
||
r.Delete("/mikrotik/{tag}", m.handleDeleteMikroTik)
|
||
|
||
// 系统操作
|
||
r.Post("/system/restart", m.handleSystemRestart)
|
||
})
|
||
}
|
||
|
||
// Web API 处理函数的简化版本,复用现有的管理 API 逻辑
|
||
func (m *Mosdns) handleWebServerInfo(w http.ResponseWriter, r *http.Request) {
|
||
m.handleServerInfo(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebServerStatus(w http.ResponseWriter, r *http.Request) {
|
||
m.handleServerStatus(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebGetConfig(w http.ResponseWriter, r *http.Request) {
|
||
m.handleGetConfig(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebUpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||
m.handleUpdateConfig(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebReloadConfig(w http.ResponseWriter, r *http.Request) {
|
||
m.handleReloadConfig(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebValidateConfig(w http.ResponseWriter, r *http.Request) {
|
||
m.handleValidateConfig(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebBackupConfig(w http.ResponseWriter, r *http.Request) {
|
||
m.handleBackupConfig(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebListDomainFiles(w http.ResponseWriter, r *http.Request) {
|
||
m.handleListDomainFiles(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebGetDomainFile(w http.ResponseWriter, r *http.Request) {
|
||
m.handleGetDomainFile(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebUpdateDomainFile(w http.ResponseWriter, r *http.Request) {
|
||
m.handleUpdateDomainFile(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebDeleteDomainFile(w http.ResponseWriter, r *http.Request) {
|
||
m.handleDeleteDomainFile(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebListPlugins(w http.ResponseWriter, r *http.Request) {
|
||
m.handleListPlugins(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebPluginStatus(w http.ResponseWriter, r *http.Request) {
|
||
m.handlePluginStatus(w, r)
|
||
}
|
||
|
||
func (m *Mosdns) handleWebDetailedStats(w http.ResponseWriter, r *http.Request) {
|
||
// 返回详细统计信息
|
||
stats := map[string]interface{}{
|
||
"totalQueries": 0,
|
||
"successfulQueries": 0,
|
||
"failedQueries": 0,
|
||
"cacheHits": 0,
|
||
"cacheMisses": 0,
|
||
"avgResponseTime": 0,
|
||
}
|
||
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Data: stats,
|
||
Message: "统计信息获取成功",
|
||
})
|
||
}
|
||
|
||
func (m *Mosdns) handleWebGetLogs(w http.ResponseWriter, r *http.Request) {
|
||
// 返回日志内容(这里可以根据实际需求实现)
|
||
logs := map[string]interface{}{
|
||
"content": "MosDNS 日志内容将在这里显示...\n[INFO] MosDNS 服务已启动\n[INFO] 配置文件加载成功\n[INFO] 所有插件已加载完成",
|
||
}
|
||
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Data: logs,
|
||
Message: "日志获取成功",
|
||
})
|
||
}
|
||
|
||
func (m *Mosdns) handleWebClearLogs(w http.ResponseWriter, r *http.Request) {
|
||
// 清空日志(这里可以根据实际需求实现)
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Message: "日志清空成功",
|
||
})
|
||
}
|
||
|
||
func (m *Mosdns) handleWebFlushCache(w http.ResponseWriter, r *http.Request) {
|
||
// 清空缓存(这里可以根据实际需求实现)
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Message: "缓存清空成功",
|
||
})
|
||
}
|