feat: 添加文件传输服务器配置和路由设置,支持外部前端目录|添加文件传输速度计算

This commit is contained in:
MatrixSeven
2025-09-10 16:48:21 +08:00
parent 343e7f1192
commit 84d7caea8c
7 changed files with 385 additions and 101 deletions

124
cmd/config.go Normal file
View File

@@ -0,0 +1,124 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"strconv"
"strings"
)
// Config 应用配置结构
type Config struct {
Port int
FrontendDir string
}
// loadEnvFile 加载环境变量文件
func loadEnvFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// 跳过空行和注释行
if line == "" || strings.HasPrefix(line, "#") {
continue
}
// 解析 KEY=VALUE 格式
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
// 移除值两端的引号
if (strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"")) ||
(strings.HasPrefix(value, "'") && strings.HasSuffix(value, "'")) {
value = value[1 : len(value)-1]
}
// 只有当环境变量不存在时才设置
if os.Getenv(key) == "" {
os.Setenv(key, value)
}
}
}
return scanner.Err()
}
// showHelp 显示帮助信息
func showHelp() {
fmt.Println("文件传输服务器")
fmt.Println("用法:")
fmt.Println(" 配置文件:")
fmt.Println(" .chuan.env - 自动加载的配置文件")
fmt.Println(" 环境变量:")
fmt.Println(" PORT=8080 - 服务器监听端口")
fmt.Println(" FRONTEND_DIR=/path - 外部前端文件目录 (可选)")
fmt.Println(" 命令行参数:")
flag.PrintDefaults()
fmt.Println("")
fmt.Println("配置优先级: 命令行参数 > 环境变量 > 配置文件 > 默认值")
fmt.Println("")
fmt.Println("示例:")
fmt.Println(" ./file-transfer-server")
fmt.Println(" ./file-transfer-server -port 3000")
fmt.Println(" PORT=8080 FRONTEND_DIR=./dist ./file-transfer-server")
}
// loadConfig 加载应用配置
func loadConfig() *Config {
// 首先尝试加载 .chuan.env 文件
if err := loadEnvFile(".chuan.env"); err == nil {
log.Printf("📄 已加载配置文件: .chuan.env")
}
// 从环境变量获取配置,如果没有则使用默认值
defaultPort := 8080
if envPort := os.Getenv("PORT"); envPort != "" {
if port, err := strconv.Atoi(envPort); err == nil {
defaultPort = port
}
}
// 定义命令行参数
var port = flag.Int("port", defaultPort, "服务器监听端口 (可通过 PORT 环境变量设置)")
var help = flag.Bool("help", false, "显示帮助信息")
flag.Parse()
// 显示帮助信息
if *help {
showHelp()
os.Exit(0)
}
config := &Config{
Port: *port,
FrontendDir: os.Getenv("FRONTEND_DIR"),
}
return config
}
// logConfig 记录配置信息
func logConfig(config *Config) {
// 记录前端配置信息
if config.FrontendDir != "" {
if info, err := os.Stat(config.FrontendDir); err == nil && info.IsDir() {
log.Printf("✅ 使用外部前端目录: %s", config.FrontendDir)
} else {
log.Printf("⚠️ 外部前端目录不可用: %s, 回退到内嵌文件", config.FrontendDir)
}
} else {
log.Printf("📦 使用内嵌前端文件")
}
}

View File

@@ -1,104 +1,25 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"chuan/internal/handlers"
"chuan/internal/web"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
)
func main() {
// 定义命令行参数
var port = flag.Int("port", 8080, "服务器监听端口")
var help = flag.Bool("help", false, "显示帮助信息")
flag.Parse()
// 显示帮助信息
if *help {
fmt.Println("文件传输服务器")
fmt.Println("用法:")
flag.PrintDefaults()
os.Exit(0)
// 检查是否需要显示帮助
if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
showHelp()
return
}
// 初始化处理器
h := handlers.NewHandler()
// 加载配置
config := loadConfig()
// 创建路由
r := chi.NewRouter()
// 记录配置信息
logConfig(config)
// 中间件
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Compress(5))
// 设置路由
router := setupRouter()
// CORS 配置
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300,
}))
// 嵌入式前端文件服务
r.Handle("/*", web.CreateFrontendHandler())
// WebRTC信令WebSocket路由
r.Get("/api/ws/webrtc", h.HandleWebRTCWebSocket)
r.Get("/ws/webrtc", h.HandleWebRTCWebSocket)
// WebRTC房间API
r.Post("/api/create-room", h.CreateRoomHandler)
r.Get("/api/room-info", h.WebRTCRoomStatusHandler)
r.Get("/api/webrtc-room-status", h.WebRTCRoomStatusHandler)
// 构建服务器地址
addr := fmt.Sprintf(":%d", *port)
// 启动服务器
srv := &http.Server{
Addr: addr,
Handler: r,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}
// 优雅关闭
go func() {
log.Printf("服务器启动在端口 %s", addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("服务器启动失败: %v", err)
}
}()
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("正在关闭服务器...")
// 设置关闭超时
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("服务器强制关闭:", err)
}
log.Println("服务器已退出")
// 运行服务器(包含启动和优雅关闭)
RunServer(config, router)
}

60
cmd/router.go Normal file
View File

@@ -0,0 +1,60 @@
package main
import (
"net/http"
"chuan/internal/handlers"
"chuan/internal/web"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
)
// setupRouter 设置路由和中间件
func setupRouter() http.Handler {
// 初始化处理器
h := handlers.NewHandler()
router := chi.NewRouter()
// 设置中间件
setupMiddleware(router)
// 设置API路由
setupAPIRoutes(router, h)
// 设置前端路由
router.Handle("/*", web.CreateFrontendHandler())
return router
}
// setupMiddleware 设置中间件
func setupMiddleware(r *chi.Mux) {
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Compress(5))
// CORS 配置
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300,
}))
}
// setupAPIRoutes 设置API路由
func setupAPIRoutes(r *chi.Mux, h *handlers.Handler) {
// WebRTC信令WebSocket路由
r.Get("/api/ws/webrtc", h.HandleWebRTCWebSocket)
r.Get("/ws/webrtc", h.HandleWebRTCWebSocket)
// WebRTC房间API
r.Post("/api/create-room", h.CreateRoomHandler)
r.Get("/api/room-info", h.WebRTCRoomStatusHandler)
r.Get("/api/webrtc-room-status", h.WebRTCRoomStatusHandler)
}

77
cmd/server.go Normal file
View File

@@ -0,0 +1,77 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
// Server 服务器结构
type Server struct {
httpServer *http.Server
config *Config
}
// NewServer 创建新的服务器实例
func NewServer(config *Config, handler http.Handler) *Server {
return &Server{
httpServer: &http.Server{
Addr: fmt.Sprintf(":%d", config.Port),
Handler: handler,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
},
config: config,
}
}
// Start 启动服务器
func (s *Server) Start() error {
log.Printf("🚀 服务器启动在端口 :%d", s.config.Port)
return s.httpServer.ListenAndServe()
}
// Stop 停止服务器
func (s *Server) Stop(ctx context.Context) error {
log.Println("🛑 正在关闭服务器...")
return s.httpServer.Shutdown(ctx)
}
// WaitForShutdown 等待关闭信号并优雅关闭
func (s *Server) WaitForShutdown() {
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// 设置关闭超时
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := s.Stop(ctx); err != nil {
log.Fatal("❌ 服务器强制关闭:", err)
}
log.Println("✅ 服务器已退出")
}
// RunServer 运行服务器(包含启动和优雅关闭)
func RunServer(config *Config, handler http.Handler) {
server := NewServer(config, handler)
// 启动服务器
go func() {
if err := server.Start(); err != nil && err != http.ErrServerClosed {
log.Fatalf("❌ 服务器启动失败: %v", err)
}
}()
// 等待关闭信号
server.WaitForShutdown()
}