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) { defer func() { if r := recover(); r != nil { log.Error(). Interface("panic_reason", r). Bytes("stack_trace", debug.Stack()). Msg("panic in http goroutine") } }() subdomain := "" host := strings.Split(r.Host, ":")[0] // remove port parts := strings.Split(host, ".") if len(parts) > 2 || (len(parts) > 1 && parts[1] == "localhost") { subdomain = parts[0] } if strings.HasPrefix(r.URL.Path, "/_") { handleAsset(w, r) } else if subdomain == "apply" { authMiddleware(handleApply)(w, r) } else if subdomain == "pma" { authMiddleware(handlePMA)(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/login" { handlers.HandleLogin(w, r) return } if r.URL.Path == "/api/join" { handlers.HandleJoin(w, r) return } if r.URL.Path == "/verify" { handlers.HandleVerify(w, r) return } servePublicFile(w, r) } 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 servePublicFile(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/public", path) log.Debug().Msgf("serving: %s", filePath) http.ServeFile(w, r, filePath) } func authMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("auth_token") if err != nil { log.Warn().Msg("Unauthorized - missing auth token") http.Error(w, "Unauthorized - missing auth token", http.StatusUnauthorized) return } 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") } if err != nil || !token.Valid { http.Error(w, "Unauthorized - invalid auth token", http.StatusUnauthorized) return } next(w, r) } } func handleApply(w http.ResponseWriter, r *http.Request) { // if r.URL.Path == "/api/application-save" { // handlers.HandleApplicationSubmit(w, r) // return // } // if r.URL.Path == "/api/get-application" { // handlers.HandleGetApplication(w, r) // return // } // if r.URL.Path == "/logout" { // handlers.HandleLogout(w, r) // return // } // if r.URL.Path == "/" { // handlers.CheckApplicationCompleteMiddleware(w, r) // } // if r.URL.Path == "/complete" { // handlers.ApplicationSubmitMiddleware(w, r) // } path := r.URL.Path if path == "/" { path = "/index.html" } else if !strings.Contains(path, ".") { path = filepath.Join("/pages", path) + ".html" } filePath := filepath.Join("../ui/apply", path) log.Debug().Msgf("Serving apply subdomain: %s", filePath) http.ServeFile(w, r, filePath) } func handlePMA(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/pma", path) log.Debug().Msgf("serving pma subdomain: %s", filePath) http.ServeFile(w, r, filePath) }