package cmd import ( "embed" "io/fs" "net/http" "path/filepath" "strings" "github.com/gin-gonic/gin" ) // embeddedFS объявлен в serve.go // setupStaticRoutes настраивает роуты для статических файлов func setupStaticRoutes(r *gin.Engine, embeddedFS embed.FS) { // Получаем подфайловую систему для public sub, err := fs.Sub(embeddedFS, "public") if err != nil { panic(err) } // Обработчик для всех остальных маршрутов (статические файлы) r.NoRoute(func(c *gin.Context) { path := c.Request.URL.Path // Убираем ведущий слеш для fs.Sub path = strings.TrimPrefix(path, "/") // Если путь пустой, показываем index.html if path == "" { path = "index.html" } // Читаем файл из встроенной файловой системы data, err := fs.ReadFile(sub, path) if err != nil { // Если файл не найден, показываем index.html (SPA routing) if strings.Contains(path, ".") { // Это файл с расширением, возвращаем 404 c.Status(http.StatusNotFound) return } // Это маршрут SPA, показываем index.html data, err = fs.ReadFile(sub, "index.html") if err != nil { c.Status(http.StatusNotFound) return } } // Определяем Content-Type по расширению contentType := getContentType(path) c.Header("Content-Type", contentType) // Специальные заголовки для шрифтов if isFontFile(path) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Cache-Control", "public, max-age=31536000") } c.Data(http.StatusOK, contentType, data) }) } // getContentType определяет Content-Type по расширению файла func getContentType(path string) string { ext := strings.ToLower(filepath.Ext(path)) switch ext { case ".html": return "text/html; charset=utf-8" case ".css": return "text/css; charset=utf-8" case ".js": return "application/javascript; charset=utf-8" case ".json": return "application/json; charset=utf-8" case ".png": return "image/png" case ".jpg", ".jpeg": return "image/jpeg" case ".gif": return "image/gif" case ".svg": return "image/svg+xml" case ".ico": return "image/x-icon" case ".woff": return "font/woff" case ".woff2": return "font/woff2" case ".ttf": return "font/ttf" case ".eot": return "application/vnd.ms-fontobject" case ".webmanifest": return "application/manifest+json" default: return "application/octet-stream" } } // isFontFile проверяет, является ли файл шрифтом func isFontFile(path string) bool { ext := strings.ToLower(filepath.Ext(path)) return ext == ".woff" || ext == ".woff2" || ext == ".ttf" || ext == ".eot" }