Initialize AutoLibrary website with Gitea deployment
This commit is contained in:
commit
18216953e7
101 files changed
+12581
No files matched your search
@@ -0,0 +1,117 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"autolibrary.top/webserver/config"
|
||||
"autolibrary.top/webserver/frontend/handlers/errors_handler"
|
||||
"autolibrary.top/webserver/utils"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var hashedAsset = regexp.MustCompile(`-[a-zA-Z0-9_-]{8,}\.[a-z0-9]+$`)
|
||||
|
||||
func createProxy(targetURL string) gin.HandlerFunc {
|
||||
target, _ := url.Parse(targetURL)
|
||||
proxy := httputil.NewSingleHostReverseProxy(target)
|
||||
proxy.ErrorHandler = func(w http.ResponseWriter, _ *http.Request, err error) {
|
||||
log.Printf("[W] backend proxy: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
_, _ = w.Write([]byte(`{"code":502,"msg":"Backend service is unavailable","data":null}`))
|
||||
}
|
||||
return func(c *gin.Context) { c.Request.URL.Path = c.Param("path"); proxy.ServeHTTP(c.Writer, c.Request) }
|
||||
}
|
||||
|
||||
// ResolveStaticPath also accepts the legacy public configuration if a sibling
|
||||
// production build exists. The configured path can be absolute or relative.
|
||||
func ResolveStaticPath(configured string) (string, error) {
|
||||
path := filepath.Clean(configured)
|
||||
if filepath.Base(path) == "public" {
|
||||
sibling := filepath.Join(filepath.Dir(path), "dist")
|
||||
if _, err := os.Stat(filepath.Join(sibling, "index.html")); err == nil {
|
||||
path = sibling
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(path, "index.html")); err != nil {
|
||||
return "", fmt.Errorf("frontend build missing at %s: run npm --prefix frontend run build: %w", path, err)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func NewRouter(cfg *config.Config) (*gin.Engine, error) {
|
||||
staticPath, err := ResolveStaticPath(cfg.Frontend.Static.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entry, err := os.ReadFile(filepath.Join(staticPath, "index.html"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery())
|
||||
pages := errors_handler.New(entry)
|
||||
r.Use(pages.APIAccessMiddleware())
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Header("X-Content-Type-Options", "nosniff")
|
||||
if strings.HasPrefix(c.Request.URL.Path, "/assets/") {
|
||||
if hashedAsset.MatchString(c.Request.URL.Path) {
|
||||
c.Header("Cache-Control", "public, max-age=31536000, immutable")
|
||||
} else {
|
||||
c.Header("Cache-Control", "public, max-age=3600")
|
||||
}
|
||||
} else {
|
||||
c.Header("Cache-Control", "no-cache")
|
||||
}
|
||||
c.Next()
|
||||
})
|
||||
r.Static("/assets", filepath.Join(staticPath, "assets"))
|
||||
backendHost := cfg.Backend.Host
|
||||
if backendHost == "0.0.0.0" || backendHost == "::" || backendHost == "" {
|
||||
backendHost = "127.0.0.1"
|
||||
}
|
||||
r.Any("/api/*path", createProxy("http://"+net.JoinHostPort(backendHost, cfg.Backend.Port)))
|
||||
serve := func(c *gin.Context) { c.Data(http.StatusOK, "text/html; charset=utf-8", entry) }
|
||||
for _, path := range []string{"/", "/downloads", "/marketplace", "/marketplace/upload", "/marketplace/profile", "/marketplace/admin"} {
|
||||
r.GET(path, serve)
|
||||
r.HEAD(path, serve)
|
||||
}
|
||||
for _, method := range []string{http.MethodGet, http.MethodHead} {
|
||||
r.Handle(method, "/changelog", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/downloads") })
|
||||
}
|
||||
pages.RegisterGetErrorRoutes(r, []int{400, 401, 403, 404, 500, 502, 503})
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
if c.Request.URL.Path == "/manuals" || strings.HasPrefix(c.Request.URL.Path, "/manuals/") {
|
||||
c.Redirect(http.StatusMovedPermanently, "https://manuals.autolibrary.kenanzhu.com")
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(c.Request.URL.Path, "/api/") {
|
||||
c.JSON(http.StatusNotFound, gin.H{"code": 404, "msg": "API route not found", "data": nil})
|
||||
return
|
||||
}
|
||||
pages.GetErrorsPage(c, http.StatusNotFound)
|
||||
})
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func StartFrontend() error { return StartFrontendContext(context.Background()) }
|
||||
func StartFrontendContext(ctx context.Context) error {
|
||||
cfg := config.GetConfig()
|
||||
router, err := NewRouter(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
address := net.JoinHostPort(cfg.Frontend.Host, cfg.Frontend.Port)
|
||||
log.Printf("[I] frontend server is running on: http://%s", address)
|
||||
return utils.ServeHTTP(ctx, address, router)
|
||||
}
|
||||
Reference in new issue
Block a user