Files
Hyperia/server/main.go
2025-10-30 13:17:53 -05:00

191 lines
4.5 KiB
Go

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"
)
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 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")) {
_, 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
}
}
}