110 lines
4.7 KiB
Go
110 lines
4.7 KiB
Go
package web
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"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 /share/{token}", h.handleSharePage)
|
|
mux.HandleFunc("GET /share/{token}/stream/{songID}", h.handleShareStream)
|
|
|
|
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" ||
|
|
strings.HasPrefix(r.URL.Path, "/static/") || strings.HasPrefix(r.URL.Path, "/share/") {
|
|
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)
|
|
})
|
|
}
|