summaryrefslogtreecommitdiff
path: root/cmd/server
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/server')
-rw-r--r--cmd/server/main.go138
1 files changed, 62 insertions, 76 deletions
diff --git a/cmd/server/main.go b/cmd/server/main.go
index b89362b..36c367c 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -46,16 +46,16 @@ func main() {
os.Exit(1)
}
- // --- Create router and define routes ---
- mux := http.NewServeMux()
+ // --- Create routers for Web and API ---
+ webMux := http.NewServeMux()
+ apiMux := http.NewServeMux()
- // --- Static file serving ---
+ // --- Static file serving (Web) ---
assetsPath := strings.TrimRight(config.Paths.Assets, "/") + "/"
assetsFS := http.FileServer(http.Dir("templates"))
- mux.Handle(assetsPath, http.StripPrefix(assetsPath, assetsFS))
+ webMux.Handle(assetsPath, http.StripPrefix(assetsPath, assetsFS))
- // --- Frontend selection and routing ---
- frontendType := os.Getenv("FRONTEND_TYPE")
+ // --- Rate Limiter ---
rateLimiter := middleware.NewRateLimiter()
// Helper to switch handler based on HTTP method
@@ -69,50 +69,45 @@ func main() {
})
}
- if frontendType == "php" {
- // Create specific handlers for each PHP page
+ // --- Web UI Routes (CSRF-protected) ---
+ if frontendType := os.Getenv("FRONTEND_TYPE"); frontendType == "php" {
+ // PHP frontend routes
loginPageHandler := handlers.NewPhpProxyHandler("login.php")
registerPageHandler := handlers.NewPhpProxyHandler("register.php")
accountPageHandler := handlers.NewPhpProxyHandler("account.php")
accountPasswordPageHandler := handlers.NewPhpProxyHandler("account_password.php")
- // For /login, GET goes to PHP, POST goes to Go's login logic
loginHandler := methodSwitch(loginPageHandler, http.HandlerFunc(handlers.LoginHandler))
- mux.Handle(config.Paths.Login, loginHandler)
+ webMux.Handle(config.Paths.Login, loginHandler)
if config.Paths.RegisterEnabled {
- // For /register, GET goes to PHP, POST goes to Go's register logic
registerPostHandler := middleware.RateLimitMiddleware(rateLimiter)(http.HandlerFunc(handlers.RegisterHandler))
registerHandler := methodSwitch(registerPageHandler, registerPostHandler)
- mux.Handle(config.Paths.Register, registerHandler)
+ webMux.Handle(config.Paths.Register, registerHandler)
}
- // For account pages, GET goes to PHP, POST (for password change) goes to Go
accountPasswordPostHandler := http.HandlerFunc(handlers.ChangePasswordHandler)
accountPasswordHandler := methodSwitch(accountPasswordPageHandler, accountPasswordPostHandler)
- mux.Handle(config.Paths.AccountPassword, middleware.SessionAuth(accountPasswordHandler))
- mux.Handle(config.Paths.Account, middleware.SessionAuth(accountPageHandler)) // This page is GET only
-
- // Admin page still uses Go templates
- mux.Handle(config.Paths.Admin, middleware.SessionAuth(middleware.AdminMiddleware(http.HandlerFunc(handlers.AdminPageHandler))))
+ webMux.Handle(config.Paths.AccountPassword, middleware.SessionAuth(accountPasswordHandler))
+ webMux.Handle(config.Paths.Account, middleware.SessionAuth(accountPageHandler))
+ webMux.Handle(config.Paths.Admin, middleware.SessionAuth(middleware.AdminMiddleware(http.HandlerFunc(handlers.AdminPageHandler))))
} else {
- // --- Public Auth Routes (Go templates) ---
- mux.HandleFunc(config.Paths.Login, handlers.LoginHandler)
+ // Go template frontend routes
+ webMux.HandleFunc(config.Paths.Login, handlers.LoginHandler)
if config.Paths.RegisterEnabled {
- mux.Handle(config.Paths.Register, middleware.RateLimitMiddleware(rateLimiter)(http.HandlerFunc(handlers.RegisterHandler)))
+ webMux.Handle(config.Paths.Register, middleware.RateLimitMiddleware(rateLimiter)(http.HandlerFunc(handlers.RegisterHandler)))
}
- // --- Auth HTML pages (Protected by SessionAuth, Go templates) ---
- mux.Handle(config.Paths.Account, middleware.SessionAuth(http.HandlerFunc(handlers.AccountPageHandler)))
- mux.Handle(config.Paths.Admin, middleware.SessionAuth(middleware.AdminMiddleware(http.HandlerFunc(handlers.AdminPageHandler))))
- mux.Handle(config.Paths.AccountPassword, middleware.SessionAuth(http.HandlerFunc(handlers.ChangePasswordHandler)))
+ webMux.Handle(config.Paths.Account, middleware.SessionAuth(http.HandlerFunc(handlers.AccountPageHandler)))
+ webMux.Handle(config.Paths.Admin, middleware.SessionAuth(middleware.AdminMiddleware(http.HandlerFunc(handlers.AdminPageHandler))))
+ webMux.Handle(config.Paths.AccountPassword, middleware.SessionAuth(http.HandlerFunc(handlers.ChangePasswordHandler)))
}
+ webMux.HandleFunc(config.Paths.Logout, handlers.LogoutHandler)
- // --- Common Routes ---
- mux.HandleFunc(config.Paths.Logout, handlers.LogoutHandler)
- mux.HandleFunc("/auth/refresh", handlers.RefreshTokenHandler)
- mux.HandleFunc("/api/auth/token", handlers.TokenHandler)
+ // --- API Routes (No CSRF) ---
+ apiMux.HandleFunc("/api/auth/token", handlers.TokenHandler)
+ apiMux.HandleFunc("/auth/refresh", handlers.RefreshTokenHandler)
- // --- Protected API Endpoints (for Admin UI, protected by SessionAuth) ---
+ // Admin API endpoints
adminCreateUserAPI := http.HandlerFunc(handlers.CreateUserHandler)
getUsersHandler := http.HandlerFunc(handlers.GetUsersHandler)
adminUsersAPI := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -122,13 +117,12 @@ func main() {
getUsersHandler.ServeHTTP(w, r)
}
})
-
adminUpdateRoleAPI := http.HandlerFunc(handlers.UpdateUserRoleHandler)
adminDeleteUserAPI := http.HandlerFunc(handlers.DeleteUserHandler)
adminSetStatusAPI := http.HandlerFunc(handlers.SetUserActiveStatusHandler)
- mux.Handle(config.Paths.AdminUsersAPI, middleware.BearerAuth(middleware.AdminMiddleware(adminUsersAPI)))
- mux.Handle(config.Paths.AdminUsersAPI+"/", middleware.BearerAuth(middleware.AdminMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ apiMux.Handle(config.Paths.AdminUsersAPI, middleware.BearerAuth(middleware.AdminMiddleware(adminUsersAPI)))
+ apiMux.Handle(config.Paths.AdminUsersAPI+"/", middleware.BearerAuth(middleware.AdminMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
switch {
case strings.HasSuffix(path, "/role") && r.Method == http.MethodPost:
@@ -145,51 +139,16 @@ func main() {
// --- Main Application Proxy ---
proxyHandler := handlers.NewProxy(targetURL)
- finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- _, pattern := mux.Handler(r)
- if pattern != "" {
- mux.ServeHTTP(w, r)
- return
- }
-
- path := r.URL.Path
-
- // API Protection with Bearer Token
- if config.Paths.ProtectAPI && strings.HasPrefix(path, config.Paths.APIPath) {
- middleware.BearerAuth(proxyHandler).ServeHTTP(w, r)
- return
- }
-
- // Frontend Protection with Session Cookie
- if config.Paths.ProtectFrontend {
- middleware.SessionAuth(proxyHandler).ServeHTTP(w, r)
- return
- }
-
- // No protection
- proxyHandler.ServeHTTP(w, r)
- })
-
- // Get server port
- port := os.Getenv("LISTEN_PORT")
- if port == "" {
- port = "8080"
- }
- listenAddr := ":" + port
-
- // --- CSRF Protection ---
+ // --- CSRF Protection (for Web UI) ---
csrfSecret := os.Getenv("CSRF_SECRET_KEY")
if csrfSecret == "" {
logging.AppLog.Error("CSRF_SECRET_KEY environment variable not set")
os.Exit(1)
}
- // In production, you should set csrf.Secure(true).
- // You can use an environment variable to control this setting.
csrfOptions := []csrf.Option{
csrf.Secure(os.Getenv("ENV") == "production"),
csrf.Path("/"),
}
-
sameSiteMode := csrf.SameSiteLaxMode
switch strings.ToLower(os.Getenv("CSRF_SAME_SITE")) {
case "strict":
@@ -198,17 +157,44 @@ func main() {
sameSiteMode = csrf.SameSiteNoneMode
}
csrfOptions = append(csrfOptions, csrf.SameSite(sameSiteMode))
-
trustedOrigins := os.Getenv("CSRF_TRUSTED_ORIGINS")
if trustedOrigins != "" {
origins := strings.Split(trustedOrigins, ",")
csrfOptions = append(csrfOptions, csrf.TrustedOrigins(origins))
}
+ csrfMiddleware := csrf.Protect([]byte(csrfSecret), csrfOptions...)
+ csrfProtectedWebMux := middleware.HandleOptions(csrfMiddleware(webMux))
+
+ // --- Final Handler ---
+ finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ logging.AppLog.Info("Final handler received request", "path", r.URL.Path)
+ if strings.HasPrefix(r.URL.Path, "/api/") {
+ logging.AppLog.Info("Routing to API mux")
+ apiMux.ServeHTTP(w, r)
+ return
+ }
- csrfMiddleware := csrf.Protect(
- []byte(csrfSecret),
- csrfOptions...,
- )
+ // Check if the path is a web UI path
+ _, webPattern := webMux.Handler(r)
+ if webPattern != "" {
+ csrfProtectedWebMux.ServeHTTP(w, r)
+ return
+ }
+
+ // Fallback to proxy
+ if config.Paths.ProtectFrontend {
+ middleware.SessionAuth(proxyHandler).ServeHTTP(w, r)
+ } else {
+ proxyHandler.ServeHTTP(w, r)
+ }
+ })
+
+ // Get server port
+ port := os.Getenv("LISTEN_PORT")
+ if port == "" {
+ port = "8080"
+ }
+ listenAddr := ":" + port
// --- Start Server ---
logging.AppLog.Info("Server starting on " + listenAddr)
@@ -216,7 +202,7 @@ func main() {
// Start periodic background tasks
go startBackgroundTasks(pool, rateLimiter)
- if err := http.ListenAndServe(listenAddr, middleware.HandleOptions(csrfMiddleware(finalHandler))); err != nil {
+ if err := http.ListenAndServe(listenAddr, finalHandler); err != nil {
logging.AppLog.Error("Server failed to start", "error", err)
os.Exit(1)
}
@@ -320,4 +306,4 @@ func main() {
})
- } \ No newline at end of file
+ }