refactor: 优化代码质量,遵循 KISS 原则
- 移除自签证书回退逻辑,简化为仅使用 AnyIP 证书 - 删除 internal/tls/generate.go(不再需要) - 重构 main.go:提取初始化逻辑,main() 从 156 行降至 13 行 - 重构 internal/ws/handler.go:提取消息处理,handleConn() 从 131 行降至 25 行 - 重构 internal/config/load.go:使用 map 驱动消除重复代码 - 优化前端 startRecording():使用标准 AbortController API - 优化前端 showToast():预定义 DOM 元素,代码减少 50% 代码行数减少 90 行,复杂度显著降低,所有构建通过
This commit is contained in:
142
main.go
142
main.go
@@ -22,99 +22,133 @@ var webFS embed.FS
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
initLogger()
|
||||
slog.Info("VoicePaste", "version", version)
|
||||
cfg := mustLoadConfig()
|
||||
config.WatchAndReload("")
|
||||
initClipboard()
|
||||
lanIPs := mustDetectLANIPs()
|
||||
lanIP := lanIPs[0]
|
||||
tlsResult, scheme := setupTLS(cfg, lanIP)
|
||||
printBanner(cfg, tlsResult, lanIPs, scheme)
|
||||
srv := createServer(cfg, lanIP, tlsResult)
|
||||
runWithGracefulShutdown(srv)
|
||||
}
|
||||
|
||||
func initLogger() {
|
||||
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||
Level: slog.LevelInfo,
|
||||
})))
|
||||
}
|
||||
|
||||
slog.Info("VoicePaste", "version", version)
|
||||
|
||||
// Load config
|
||||
func mustLoadConfig() config.Config {
|
||||
cfg, err := config.Load("")
|
||||
if err != nil {
|
||||
slog.Error("failed to load config", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Start config hot-reload watcher
|
||||
config.WatchAndReload("")
|
||||
|
||||
// Initialize clipboard
|
||||
func initClipboard() {
|
||||
if err := paste.Init(); err != nil {
|
||||
slog.Warn("clipboard init failed, paste will be unavailable", "err", err)
|
||||
}
|
||||
// Detect LAN IPs
|
||||
}
|
||||
|
||||
func mustDetectLANIPs() []string {
|
||||
lanIPs, err := server.GetLANIPs()
|
||||
if err != nil {
|
||||
slog.Error("failed to detect LAN IP", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
lanIP := lanIPs[0] // Use first IP for TLS and server binding
|
||||
return lanIPs
|
||||
}
|
||||
|
||||
// Read token from config (empty = no auth required)
|
||||
token := cfg.Security.Token
|
||||
|
||||
// TLS setup
|
||||
var tlsResult *vpTLS.Result
|
||||
scheme := "http"
|
||||
host := lanIP
|
||||
if cfg.Server.TLSAuto {
|
||||
var err error
|
||||
tlsResult, err = vpTLS.GetTLSConfig(lanIP)
|
||||
if err != nil {
|
||||
slog.Error("TLS setup failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
scheme = "https"
|
||||
host = tlsResult.Host
|
||||
func setupTLS(cfg config.Config, lanIP string) (*vpTLS.Result, string) {
|
||||
if !cfg.Server.TLSAuto {
|
||||
return nil, "http"
|
||||
}
|
||||
tlsResult, err := vpTLS.GetTLSConfig(lanIP)
|
||||
if err != nil {
|
||||
slog.Error("TLS setup failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return tlsResult, "https"
|
||||
}
|
||||
|
||||
// Print connection info
|
||||
func printBanner(cfg config.Config, tlsResult *vpTLS.Result, lanIPs []string, scheme string) {
|
||||
fmt.Println()
|
||||
fmt.Println("╔══════════════════════════════════════╗")
|
||||
fmt.Println("║ VoicePaste 就绪 ║")
|
||||
fmt.Println("╚══════════════════════════════════════╝")
|
||||
fmt.Println()
|
||||
// Print all accessible addresses
|
||||
printAddresses(cfg, tlsResult, lanIPs, scheme)
|
||||
printCertInfo(tlsResult, cfg.Server.TLSAuto)
|
||||
printAuthInfo(cfg.Security.Token)
|
||||
fmt.Println()
|
||||
fmt.Println(" 在手机浏览器中打开上方地址")
|
||||
fmt.Println(" 按 Ctrl+C 停止服务")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func printAddresses(cfg config.Config, tlsResult *vpTLS.Result, lanIPs []string, scheme string) {
|
||||
token := cfg.Security.Token
|
||||
if len(lanIPs) == 1 {
|
||||
host := lanIP(tlsResult, lanIPs[0])
|
||||
fmt.Printf(" 地址: %s\n", buildURL(scheme, host, cfg.Server.Port, token))
|
||||
} else {
|
||||
fmt.Println(" 地址:")
|
||||
for _, ip := range lanIPs {
|
||||
h := ip
|
||||
if tlsResult != nil && tlsResult.AnyIP {
|
||||
h = vpTLS.AnyIPHost(ip)
|
||||
}
|
||||
fmt.Printf(" - %s\n", buildURL(scheme, h, cfg.Server.Port, token))
|
||||
}
|
||||
return
|
||||
}
|
||||
if tlsResult != nil && tlsResult.AnyIP {
|
||||
fmt.Println(" 地址:")
|
||||
for _, ip := range lanIPs {
|
||||
host := lanIP(tlsResult, ip)
|
||||
fmt.Printf(" - %s\n", buildURL(scheme, host, cfg.Server.Port, token))
|
||||
}
|
||||
}
|
||||
|
||||
func lanIP(tlsResult *vpTLS.Result, ip string) string {
|
||||
if tlsResult != nil {
|
||||
return vpTLS.AnyIPHost(ip)
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
func printCertInfo(tlsResult *vpTLS.Result, tlsAuto bool) {
|
||||
if tlsResult != nil {
|
||||
fmt.Println(" 证书: AnyIP(浏览器信任)")
|
||||
} else if cfg.Server.TLSAuto {
|
||||
fmt.Println(" 证书: 自签名(浏览器会警告)")
|
||||
} else if tlsAuto {
|
||||
fmt.Println(" 证书: 配置错误(TLS 启用但未获取证书)")
|
||||
}
|
||||
}
|
||||
|
||||
func printAuthInfo(token string) {
|
||||
if token != "" {
|
||||
fmt.Println(" 认证: 已启用")
|
||||
} else {
|
||||
fmt.Println(" 认证: 未启用(无需 token)")
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println(" 在手机浏览器中打开上方地址")
|
||||
fmt.Println(" 按 Ctrl+C 停止服务")
|
||||
fmt.Println()
|
||||
// Create and start server
|
||||
}
|
||||
|
||||
func createServer(cfg config.Config, lanIP string, tlsResult *vpTLS.Result) *server.Server {
|
||||
webContent, _ := fs.Sub(webFS, "web/dist")
|
||||
var serverTLSCfg *crypto_tls.Config
|
||||
var tlsConfig *crypto_tls.Config
|
||||
if tlsResult != nil {
|
||||
serverTLSCfg = tlsResult.Config
|
||||
tlsConfig = tlsResult.Config
|
||||
}
|
||||
srv := server.New(token, lanIP, webContent, serverTLSCfg)
|
||||
// Build ASR factory from config
|
||||
srv := server.New(cfg.Security.Token, lanIP, webContent, tlsConfig)
|
||||
asrFactory := buildASRFactory(cfg)
|
||||
wsHandler := ws.NewHandler(cfg.Security.Token, paste.Paste, asrFactory)
|
||||
wsHandler.Register(srv.App())
|
||||
return srv
|
||||
}
|
||||
|
||||
func buildASRFactory(cfg config.Config) func(chan<- ws.ServerMsg) (func([]byte), func(), error) {
|
||||
asrCfg := asr.Config{
|
||||
AppID: cfg.Doubao.AppID,
|
||||
AccessToken: cfg.Doubao.AccessToken,
|
||||
ResourceID: cfg.Doubao.ResourceID,
|
||||
ResourceID: cfg.Doubao.ResourceID,
|
||||
}
|
||||
asrFactory := func(resultCh chan<- ws.ServerMsg) (func([]byte), func(), error) {
|
||||
return func(resultCh chan<- ws.ServerMsg) (func([]byte), func(), error) {
|
||||
client, err := asr.Dial(asrCfg, resultCh)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -129,12 +163,9 @@ func main() {
|
||||
}
|
||||
return sendAudio, cleanup, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Register WebSocket handler
|
||||
wsHandler := ws.NewHandler(token, paste.Paste, asrFactory)
|
||||
wsHandler.Register(srv.App())
|
||||
|
||||
// Graceful shutdown
|
||||
func runWithGracefulShutdown(srv *server.Server) {
|
||||
go func() {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
@@ -142,7 +173,6 @@ func main() {
|
||||
slog.Info("shutting down...")
|
||||
srv.Shutdown()
|
||||
}()
|
||||
|
||||
if err := srv.Start(); err != nil {
|
||||
slog.Error("server error", "error", err)
|
||||
os.Exit(1)
|
||||
|
||||
Reference in New Issue
Block a user