133 lines
5.4 KiB
Go
133 lines
5.4 KiB
Go
package server
|
|
|
|
import (
|
|
"MiauInv/auth"
|
|
"MiauInv/config"
|
|
"MiauInv/frontend"
|
|
"MiauInv/handlers"
|
|
"MiauInv/models"
|
|
utils "MiauInv/util"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
type Server struct {
|
|
Port string
|
|
JWTSecret []byte
|
|
DatabasePath string
|
|
CertificatePath string
|
|
PrivateKeyPath string
|
|
AllowRegistration bool
|
|
}
|
|
|
|
func InitServer() *Server {
|
|
|
|
err := config.CheckIfExists()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return nil
|
|
}
|
|
|
|
cfg, err := config.LoadConfig()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return nil
|
|
}
|
|
|
|
jwtSecret := os.Getenv("JWT_SECRET")
|
|
if jwtSecret == "" {
|
|
log.Fatal("JWT_SECRET environment variable not set.")
|
|
return nil
|
|
}
|
|
if len(jwtSecret) < 32 {
|
|
log.Fatal("JWT_SECRET must be at least 32 characters long.")
|
|
return nil
|
|
}
|
|
|
|
models.JWTSecret = []byte(jwtSecret)
|
|
|
|
return &Server{
|
|
Port: cfg.Port,
|
|
JWTSecret: []byte(jwtSecret),
|
|
DatabasePath: cfg.DatabasePath,
|
|
CertificatePath: cfg.CertificatePath,
|
|
PrivateKeyPath: cfg.PrivateKeyPath,
|
|
AllowRegistration: cfg.AllowRegistration,
|
|
}
|
|
}
|
|
|
|
func (this *Server) Run() {
|
|
log.Println("Starting server...")
|
|
mux := http.NewServeMux()
|
|
|
|
//
|
|
// FRONTEND
|
|
//
|
|
mux.HandleFunc("/", frontend.Home)
|
|
mux.HandleFunc("/login", utils.RenderFile("frontend/htmx/login.html"))
|
|
mux.Handle("/dashboard", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(frontend.Dashboard)))
|
|
mux.Handle("/inventory", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(frontend.Inventory)))
|
|
mux.Handle("/items", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(frontend.Items)))
|
|
mux.Handle("/locations", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(frontend.Locations)))
|
|
mux.Handle("/projects", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(frontend.Projects)))
|
|
mux.Handle("/profile/settings", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(frontend.AccountSettings)))
|
|
mux.Handle("/profile/activity", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(frontend.Activity)))
|
|
mux.HandleFunc("/profile/", utils.RenderFile("frontend/htmx/under-construction.html"))
|
|
if this.AllowRegistration {
|
|
mux.HandleFunc("/register", utils.RenderFile("frontend/htmx/register.html"))
|
|
} else {
|
|
mux.HandleFunc("/register", utils.RenderFile("frontend/htmx/register-blocked.html"))
|
|
}
|
|
|
|
//
|
|
// API
|
|
//
|
|
loginLimiter := newRateLimiter(10, time.Minute, 5*time.Minute)
|
|
accountLimiter := newRateLimiter(20, time.Minute, 2*time.Minute)
|
|
activityLimiter := newRateLimiter(60, time.Minute, 2*time.Minute)
|
|
|
|
authed := func(handler http.HandlerFunc) http.Handler {
|
|
return auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(handler))
|
|
}
|
|
audited := func(entityType string, handler http.HandlerFunc) http.Handler {
|
|
return auth.AuthMiddleware(this.JWTSecret)(handlers.ActivityMiddleware(entityType, false)(http.HandlerFunc(handler)))
|
|
}
|
|
|
|
mux.Handle("/api/login", loginLimiter.Middleware(http.HandlerFunc(handlers.APILogin)))
|
|
mux.Handle("/api/login/2fa", loginLimiter.Middleware(http.HandlerFunc(handlers.APILoginTwoFactor)))
|
|
mux.Handle("/api/refresh", loginLimiter.Middleware(http.HandlerFunc(handlers.RefreshToken)))
|
|
mux.Handle("/api/logout", auth.AuthMiddleware(this.JWTSecret)(handlers.ActivityMiddleware("auth", true)(http.HandlerFunc(handlers.Logout))))
|
|
mux.Handle("/api/profile", authed(handlers.UserInfo))
|
|
mux.Handle("/api/activity", activityLimiter.Middleware(authed(handlers.ActivityLog)))
|
|
mux.Handle("/api/2fa/setup", accountLimiter.Middleware(audited("security", handlers.TwoFactorSetup)))
|
|
mux.Handle("/api/2fa/enable", loginLimiter.Middleware(audited("security", handlers.TwoFactorEnable)))
|
|
mux.Handle("/api/2fa/disable", loginLimiter.Middleware(audited("security", handlers.TwoFactorDisable)))
|
|
mux.Handle("/api/2fa/recovery-codes/regenerate", loginLimiter.Middleware(audited("security", handlers.TwoFactorRegenerateRecoveryCodes)))
|
|
mux.Handle("/api/userinfo", authed(handlers.UserInfo))
|
|
mux.Handle("/api/account/username", accountLimiter.Middleware(audited("account", handlers.AccountUpdateUsername)))
|
|
mux.Handle("/api/account/password", loginLimiter.Middleware(audited("account", handlers.AccountUpdatePassword)))
|
|
mux.Handle("/api/passkeys", accountLimiter.Middleware(audited("security", handlers.Passkeys)))
|
|
mux.Handle("/api/passkeys/register/options", accountLimiter.Middleware(audited("security", handlers.PasskeyRegisterOptions)))
|
|
mux.Handle("/api/passkeys/register/finish", accountLimiter.Middleware(audited("security", handlers.PasskeyRegisterFinish)))
|
|
mux.Handle("/api/passkeys/disable", loginLimiter.Middleware(audited("security", handlers.PasskeyDisable)))
|
|
mux.Handle("/api/passkeys/login/options", loginLimiter.Middleware(http.HandlerFunc(handlers.PasskeyLoginOptions)))
|
|
mux.Handle("/api/passkeys/login/finish", loginLimiter.Middleware(http.HandlerFunc(handlers.PasskeyLoginFinish)))
|
|
if this.AllowRegistration {
|
|
mux.Handle("/api/register", loginLimiter.Middleware(http.HandlerFunc(handlers.APIRegister)))
|
|
}
|
|
|
|
mux.Handle("/api/item", audited("item", handlers.Item))
|
|
mux.Handle("/api/location", audited("location", handlers.Location))
|
|
mux.Handle("/api/project", audited("project", handlers.Project))
|
|
mux.Handle("/api/stock", audited("stock", handlers.Stock))
|
|
mux.Handle("/api/association", audited("association", handlers.Associations))
|
|
|
|
// Assets
|
|
mux.HandleFunc("/assets/", frontend.Assets)
|
|
|
|
log.Printf("Listening on port %s", this.Port)
|
|
log.Fatal(http.ListenAndServeTLS(":"+this.Port, this.CertificatePath, this.PrivateKeyPath, mux))
|
|
}
|