package main import ( "fmt" "net/http" "path/filepath" "hyperia/config" "hyperia/db" "hyperia/handlers" "hyperia/logger" // "runtime/debug" "strings" "github.com/gorilla/websocket" "github.com/golang-jwt/jwt/v5" "github.com/rs/zerolog/log" "github.com/alexedwards/argon2id" ) func isWebSocketRequest(r *http.Request) bool { connHeader := strings.ToLower(r.Header.Get("Connection")) upgradeHeader := strings.ToLower(r.Header.Get("Upgrade")) return strings.Contains(connHeader, "upgrade") && upgradeHeader == "websocket" } func HashPassword() { hash, err := argon2id.CreateHash("banktest", argon2id.DefaultParams) if err != nil { log.Fatal().Msgf("failed to hash password: %v", err) } fmt.Println("Argon2id hash: ") fmt.Println(hash) } 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) { if(loggedIn(w, r)) { if isWebSocketRequest(r) { handleWebSocket(w, r) return } 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 == "/" { // Required for sign in / sign out redirect to work properly w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate") w.Header().Set("Pragma", "no-cache") w.Header().Set("Expires", "0") w.Header().Set("Surrogate-Control", "no-store") 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.Contains(r.URL.Path, "/_") { handleAsset(w, r) return } serveSiteFiles(w, r) } func serveSiteFiles(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if(strings.Contains(path, "75820185")) { // necessary because, if we are at a url path 2 layers or more, the browser will insert that path at the beginning of the url _, after, _ := strings.Cut(path, "75820185") path = after } else { // Required for sign in / sign out redirect to work properly w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate") w.Header().Set("Pragma", "no-cache") w.Header().Set("Expires", "0") w.Header().Set("Surrogate-Control", "no-store") path = "/index.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) { _, after, _ := strings.Cut(r.URL.Path, "/_") filePath := filepath.Join("../ui", "/_" + after) 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 } var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true // In production, validate the origin! }, } func handleWebSocket(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { fmt.Println("WebSocket upgrade failed:", err) http.Error(w, "WebSocket upgrade failed", http.StatusBadRequest) return } defer conn.Close() fmt.Println("WebSocket connection established") for { msgType, msg, err := conn.ReadMessage() if err != nil { fmt.Println("Read error:", err) break } fmt.Printf("Received: %s\n", msg) if err := conn.WriteMessage(msgType, msg); err != nil { fmt.Println("Write error:", err) break } } }