This commit is contained in:
66 changed files with 19815 additions and 0 deletions

107
back/cmd/static_routes.go Normal file
View File

@@ -0,0 +1,107 @@
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"
}