package main import ( "fmt" "net/http" "path/filepath" "hyperia/config" "hyperia/db" "hyperia/handlers" "hyperia/logger" // "runtime/debug" "strings" "github.com/golang-jwt/jwt/v5" "github.com/rs/zerolog/log" ) func main() { config.SetConfiguration() logger.ConfigureLogger() err := db.InitDB() if err != nil { log.Fatal().Msgf("failed to connect to database: %v", err) } else { log.Info().Msg("successfully connected to PostgreSQL") } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // Keeps server from crashing if a request fails // defer func() { // if r := recover(); r != nil { // log.Error(). // Interface("panic_reason", r). // Bytes("stack_trace", debug.Stack()). // Msg("panic in http goroutine") // } // }() if(loggedIn(w, r)) { log.Info().Msg("logged") handleSite(w, r) } else { handlePublic(w, r) } }) log.Info().Msgf("Server starting on http://localhost: %s", config.PORT) err = http.ListenAndServe(":"+config.PORT, nil) if err != nil { log.Fatal().Msgf("failed to start server: %v", err) } } func handlePublic(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/api/signup" { handlers.HandleSignup(w, r) return } if r.URL.Path == "/api/login" { handlers.HandleLogin(w, r) return } if strings.HasPrefix(r.URL.Path, "/_") { handleAsset(w, r) return } servePublicFile(w, r) } func servePublicFile(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if path == "/" { w.Header().Set("Cache-Control", "no-store") w.Header().Set("Pragma", "no-cache") w.Header().Set("Expires", "0") path = "/index.html" } else if !strings.Contains(path, ".") { path = filepath.Join("/pages", path) + ".html" } filePath := filepath.Join("../ui/public", path) log.Debug().Msgf("serving: %s", filePath) http.ServeFile(w, r, filePath) } func handleSite(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/signout" { handlers.HandleLogout(w, r) return } if strings.HasPrefix(r.URL.Path, "/_") { handleAsset(w, r) return } serveSiteFiles(w, r) } func serveSiteFiles(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if path == "/" { path = "/index.html" } else if !strings.Contains(path, ".") { path = filepath.Join("/pages", path) + ".html" } filePath := filepath.Join("../ui/site", path) log.Debug().Msgf("serving: %s", filePath) http.ServeFile(w, r, filePath) } func handleAsset(w http.ResponseWriter, r *http.Request) { path := r.URL.Path filePath := filepath.Join("../ui", path) log.Debug().Msgf("serving asset: %s", filePath) http.ServeFile(w, r, filePath) } func loggedIn(w http.ResponseWriter, r *http.Request) bool { cookie, err := r.Cookie("auth_token") if err != nil { log.Warn().Msg("Unauthorized - missing auth token") return false } jwtToken := cookie.Value token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return []byte(config.JWT_SECRET), nil }) if err != nil { log.Err(err).Msg("error authenticating jwt") return false } if err != nil || !token.Valid { return false } return true }