Files
vibesonic/internal/web/router.go
T

106 lines
4.5 KiB
Go

package web
import (
"net/http"
"git.noroutes.net/NoRoutes/vibetunes/internal/database"
assets "git.noroutes.net/NoRoutes/vibetunes/web"
)
// NewRouter builds VibeSonic's full HTTP handler: static assets, health
// checks, first-run setup, authentication, and the authenticated
// application (library browsing, playback, playlists, favorites,
// settings, admin).
func (h *Web) NewRouter() http.Handler {
mux := http.NewServeMux()
mux.Handle("GET /static/", http.FileServer(http.FS(assets.FS)))
mux.HandleFunc("GET /healthz", h.handleHealthz)
mux.HandleFunc("GET /readyz", h.handleReadyz)
mux.HandleFunc("GET /setup", h.handleSetupPage)
mux.HandleFunc("POST /setup", h.handleSetup)
mux.HandleFunc("GET /login", h.handleLoginPage)
mux.HandleFunc("POST /login", h.handleLogin)
mux.HandleFunc("POST /logout", h.handleLogout)
mux.HandleFunc("GET /{$}", h.requireUser(h.handleHome))
mux.HandleFunc("GET /search", h.requireUser(h.handleSearch))
mux.HandleFunc("GET /artists", h.requireUser(h.handleArtists))
mux.HandleFunc("GET /artists/{id}", h.requireUser(h.handleArtist))
mux.HandleFunc("GET /albums", h.requireUser(h.handleAlbums))
mux.HandleFunc("GET /albums/{id}", h.requireUser(h.handleAlbum))
mux.HandleFunc("GET /songs", h.requireUser(h.handleSongs))
mux.HandleFunc("GET /genres", h.requireUser(h.handleGenres))
mux.HandleFunc("GET /playlists", h.requireUser(h.handlePlaylists))
mux.HandleFunc("POST /playlists", h.requireUser(h.handlePlaylistCreate))
mux.HandleFunc("GET /playlists/{id}", h.requireUser(h.handlePlaylist))
mux.HandleFunc("POST /playlists/{id}/delete", h.requireUser(h.handlePlaylistDelete))
mux.HandleFunc("GET /favorites", h.requireUser(h.handleFavorites))
mux.HandleFunc("POST /favorites/star", h.requireUser(h.handleFavoriteStar))
mux.HandleFunc("POST /favorites/unstar", h.requireUser(h.handleFavoriteUnstar))
mux.HandleFunc("GET /recent", h.requireUser(h.handleRecent))
mux.HandleFunc("GET /settings", h.requireUser(h.handleSettings))
mux.HandleFunc("POST /settings/password", h.requireUser(h.handlePasswordChange))
mux.HandleFunc("POST /settings/api-keys", h.requireUser(h.handleAPIKeyCreate))
mux.HandleFunc("POST /settings/api-keys/{id}/revoke", h.requireUser(h.handleAPIKeyRevoke))
mux.HandleFunc("POST /settings/sessions/{id}/revoke", h.requireUser(h.handleSessionRevoke))
mux.HandleFunc("GET /stream/{id}", h.requireUser(h.handleStream))
mux.HandleFunc("GET /cover-art/{id}", h.requireUser(h.handleCoverArt))
mux.HandleFunc("GET /admin", h.requireAdmin(h.handleAdminRoot))
mux.HandleFunc("GET /admin/users", h.requireAdmin(h.handleAdminUsers))
mux.HandleFunc("POST /admin/users", h.requireAdmin(h.handleAdminUserCreate))
mux.HandleFunc("POST /admin/users/{id}/toggle-disabled", h.requireAdmin(h.handleAdminUserToggleDisabled))
mux.HandleFunc("POST /admin/users/{id}/delete", h.requireAdmin(h.handleAdminUserDelete))
mux.HandleFunc("GET /admin/folders", h.requireAdmin(h.handleAdminFolders))
mux.HandleFunc("POST /admin/folders", h.requireAdmin(h.handleAdminFolderCreate))
mux.HandleFunc("POST /admin/folders/{id}/toggle", h.requireAdmin(h.handleAdminFolderToggle))
mux.HandleFunc("POST /admin/folders/{id}/delete", h.requireAdmin(h.handleAdminFolderDelete))
mux.HandleFunc("POST /admin/folders/{id}/scan", h.requireAdmin(h.handleAdminFolderScan))
mux.HandleFunc("GET /admin/scanner", h.requireAdmin(h.handleAdminScanner))
mux.HandleFunc("GET /admin/settings", h.requireAdmin(h.handleAdminSettings))
mux.HandleFunc("POST /admin/settings", h.requireAdmin(h.handleAdminSettingsUpdate))
var handler http.Handler = mux
handler = h.CSRF.Protect(handler)
handler = h.setupGate(handler)
handler = h.Middleware.Authenticate(handler)
return handler
}
func (h *Web) requireUser(fn http.HandlerFunc) http.HandlerFunc {
wrapped := h.Middleware.RequireUser(fn)
return wrapped.ServeHTTP
}
func (h *Web) requireAdmin(fn http.HandlerFunc) http.HandlerFunc {
wrapped := h.Middleware.RequireAdmin(fn)
return wrapped.ServeHTTP
}
// setupGate sends every request to /setup until an administrator
// account exists, so a fresh install can never be used half-configured.
func (h *Web) setupGate(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/setup" || r.URL.Path == "/healthz" || r.URL.Path == "/readyz" ||
len(r.URL.Path) >= 8 && r.URL.Path[:8] == "/static/" {
next.ServeHTTP(w, r)
return
}
n, err := database.CountUsers(h.DB)
if err == nil && n == 0 {
http.Redirect(w, r, "/setup", http.StatusFound)
return
}
next.ServeHTTP(w, r)
})
}