package web import ( "encoding/json" "fmt" "html/template" "log" "net/http" "os" "path/filepath" "clintonambulance.com/calculate_negative_points/internal/config" "github.com/go-chi/chi/v5" "github.com/swaggest/rest/web" "go.uber.org/zap" ) type ManifestEntry struct { Src string `json:"src"` File string `json:"file"` IsEntry bool `json:"isEntry"` Css []string `json:"css"` } func withEntryPoint(config *config.ApplicationConfig, ep string, tmplParams map[string]interface{}) { if config.Environment == "dev" { tmplParams["jsFileAddress"] = fmt.Sprintf("%s/%s", tmplParams["baseAddress"], ep) } else { var parsedManifest map[string]ManifestEntry manifestContent, err := os.ReadFile(filepath.Join(config.PublicPath, ".vite", "manifest.json")) if err != nil { log.Fatal(err) } err = json.Unmarshal(manifestContent, &parsedManifest) if err != nil { log.Fatal(err) } tmplParams["jsFileAddress"] = parsedManifest[ep].File tmplParams["css"] = parsedManifest[ep].Css } } func MountWebEndpoints(e *web.Service, config *config.ApplicationConfig, logger *zap.Logger) { rootResponder := func(w http.ResponseWriter, r *http.Request) { tmpl, err := template.ParseFiles(filepath.Join(config.ViewPath, "web.html")) tmplParams := map[string]interface{}{ "baseAddress": "http://localhost:5173", "isDev": config.Environment == "dev", "environment": config.Environment, } withEntryPoint(config, "src/main.tsx", tmplParams) if err != nil { log.Println(err) http.Error(w, "Something went wrong", http.StatusInternalServerError) return } if err := tmpl.Execute(w, tmplParams); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } loggedOutResponder := func(w http.ResponseWriter, r *http.Request) { tmpl, err := template.ParseFiles(filepath.Join(config.ViewPath, "logged_out.html")) if err != nil { http.Error(w, "Something went wrong", http.StatusInternalServerError) return } if err := tmpl.Execute(w, &struct{}{}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } notAuthorizedResponder := func(w http.ResponseWriter, r *http.Request) { tmpl, err := template.ParseFiles(filepath.Join(config.ViewPath, "auth_error.html")) if err != nil { http.Error(w, "Something went wrong", http.StatusInternalServerError) return } if err := tmpl.Execute(w, &struct{}{}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } loginHandler := func(w http.ResponseWriter, r *http.Request) { returnTo := r.URL.Query().Get("returnTo") if returnTo == "" { returnTo = "/" // fallback } session, _ := config.CookieStore.Get(r, config.SessionName) session.Values["return_to"] = returnTo session.Save(r, w) state := "random-state" // Replace with actual CSRF protection http.Redirect(w, r, config.OidcConfig.OAuth2.AuthCodeURL(state), http.StatusFound) } callbackHandler := func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() code := r.URL.Query().Get("code") token, err := config.OidcConfig.OAuth2.Exchange(ctx, code) if err != nil { http.Error(w, "Token exchange failed", http.StatusInternalServerError) return } rawIDToken, ok := token.Extra("id_token").(string) if !ok { http.Error(w, "Missing id_token", http.StatusInternalServerError) return } _, err = config.OidcConfig.Verifier.Verify(ctx, rawIDToken) if err != nil { http.Error(w, "Invalid ID token", http.StatusUnauthorized) return } session, _ := config.CookieStore.Get(r, config.SessionName) if refreshToken, ok := token.Extra("refresh_token").(string); ok { session.Values["refresh_token"] = refreshToken } returnTo, ok := session.Values["return_to"].(string) if !ok || returnTo == "" { returnTo = "/" } session.Values["id_token"] = rawIDToken session.Save(r, w) http.Redirect(w, r, returnTo, http.StatusFound) } e.Wrapper.Get("/users/logged_out", loggedOutResponder) e.Wrapper.Get("/403", notAuthorizedResponder) e.Wrapper.Post("/403", notAuthorizedResponder) e.Route("/", func(r chi.Router) { r.HandleFunc("/", rootResponder) r.Get("/auth/login", loginHandler) r.Get("/auth/callback", callbackHandler) r.MethodFunc(http.MethodGet, "/*", rootResponder) }) }