feat: api serve frontend, build workflow, router readyness

This commit is contained in:
Floriansylvain
2025-12-24 01:05:58 +01:00
parent aee595dee9
commit 4a6209a8db
13 changed files with 174 additions and 74 deletions
+5 -3
View File
@@ -32,7 +32,9 @@ type LoginCredentials struct {
Password string `json:"password" validate:"required,min=8"`
}
var shouldCookieBeSecure = os.Getenv("ENVIRONMENT") == "production"
func shouldCookieBeSecure() bool {
return os.Getenv("ENVIRONMENT") == "production"
}
func removeJwtCookie(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
@@ -40,7 +42,7 @@ func removeJwtCookie(w http.ResponseWriter) {
Value: "",
Expires: time.Now(),
MaxAge: -1,
Secure: shouldCookieBeSecure,
Secure: shouldCookieBeSecure(),
HttpOnly: true,
Path: "/",
})
@@ -64,7 +66,7 @@ func (h *Handler) SetJwtCookie(w *http.ResponseWriter, userId uint32) error {
Name: "jwt",
Value: tokenString,
Expires: time.Now().Add(24 * time.Hour),
Secure: shouldCookieBeSecure,
Secure: shouldCookieBeSecure(),
HttpOnly: true,
Path: "/",
})
+79
View File
@@ -0,0 +1,79 @@
package frontend
import (
"RenewCMS/internal/infrastructure/assets"
"io"
"io/fs"
"net/http"
"path/filepath"
"strings"
"time"
"github.com/go-chi/chi/v5"
)
type spaHandler struct {
staticFS fs.FS
modTime time.Time
}
func NewFrontendRouter() http.Handler {
r := chi.NewRouter()
distFS, _ := fs.Sub(assets.DistEmbed, "dist")
handler := &spaHandler{
staticFS: distFS,
modTime: time.Now(),
}
r.Get("/*", handler.ServeHTTP)
return r
}
func (h *spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := h.cleanPath(r.URL.Path)
if h.fileExists(path) {
h.serveAsset(w, r, path)
return
}
h.serveIndex(w, r)
}
func (h *spaHandler) cleanPath(path string) string {
return strings.TrimPrefix(filepath.Clean(path), "/")
}
func (h *spaHandler) fileExists(path string) bool {
if path == "" || path == "." {
return false
}
f, err := h.staticFS.Open(path)
if err != nil {
return false
}
defer f.Close()
return true
}
func (h *spaHandler) serveAsset(w http.ResponseWriter, r *http.Request, path string) {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
http.FileServer(http.FS(h.staticFS)).ServeHTTP(w, r)
}
func (h *spaHandler) serveIndex(w http.ResponseWriter, r *http.Request) {
f, err := h.staticFS.Open("index.html")
if err != nil {
http.Error(w, "Index not found", http.StatusNotFound)
return
}
defer f.Close()
content, ok := f.(io.ReadSeeker)
if !ok {
http.Error(w, "File not seekable", http.StatusInternalServerError)
return
}
w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate")
http.ServeContent(w, r, "index.html", h.modTime, content)
}
+1 -10
View File
@@ -2,18 +2,9 @@ package middleware
import "net/http"
const KeyContentType = "Content-Type"
func JsonContentTypeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(KeyContentType, "application/json")
next.ServeHTTP(w, r)
})
}
func HtmlContentTypeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(KeyContentType, "text/html")
w.Header().Set("Content-Type", "application/json")
next.ServeHTTP(w, r)
})
}
+22 -2
View File
@@ -3,6 +3,7 @@ package api
import (
"RenewCMS/api/handlers/article"
"RenewCMS/api/handlers/auth"
"RenewCMS/api/handlers/frontend"
"RenewCMS/api/middleware"
"encoding/json"
"net/http"
@@ -15,6 +16,12 @@ import (
"github.com/go-chi/jwtauth/v5"
)
func enableLoggingIfDev(r *chi.Mux, routerName string) {
if os.Getenv("ENVIRONMENT") == "development" {
r.Use(httplog.LoggerWithName(routerName))
}
}
func GetHelloWorld(w http.ResponseWriter, _ *http.Request) {
msg, _ := json.Marshal(map[string]string{"message": "Hello World"})
_, _ = w.Write(msg)
@@ -46,7 +53,8 @@ func NewAuthRouter(container *UseCases) http.Handler {
func InitBackendRoutes(container *UseCases) *chi.Mux {
r := chi.NewRouter()
r.Use(httplog.LoggerWithName("backend"))
enableLoggingIfDev(r, "backend")
r.Use(middleware.JsonContentTypeMiddleware)
r.Get("/", GetHelloWorld)
r.Group(func(r chi.Router) {
@@ -59,19 +67,31 @@ func InitBackendRoutes(container *UseCases) *chi.Mux {
return r
}
func InitFrontendRoutes() *chi.Mux {
r := chi.NewRouter()
enableLoggingIfDev(r, "frontend")
r.Mount("/", frontend.NewFrontendRouter())
return r
}
func InitRoutes(container *UseCases) *chi.Mux {
backend := InitBackendRoutes(container)
frontend := InitFrontendRoutes()
apiRouter := chi.NewRouter()
apiRouter.Use(cors.Handler(cors.Options{
AllowedOrigins: strings.Split(os.Getenv("CORS_ALLOWED_ORIGINS"), ";"),
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", middleware.KeyContentType, "X-CSRF-Token"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: false,
MaxAge: 300,
}))
apiRouter.Mount("/v1", backend)
apiRouter.Mount("/", frontend)
return apiRouter
}