very rough updates to admin pages, reduced reliance on global.DB

This commit is contained in:
ari melody 2025-01-21 00:20:07 +00:00
parent ae254dd731
commit 7044f7344b
Signed by: ari
GPG key ID: CF99829C92678188
15 changed files with 192 additions and 106 deletions

38
admin/accounthttp.go Normal file
View file

@ -0,0 +1,38 @@
package admin
import (
"fmt"
"net/http"
"arimelody-web/controller"
"arimelody-web/model"
"github.com/jmoiron/sqlx"
)
func AccountHandler(db *sqlx.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
account := r.Context().Value("account").(*model.Account)
totps, err := controller.GetTOTPsForAccount(db, account.ID)
if err != nil {
fmt.Printf("WARN: Failed to fetch TOTPs: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
type AccountResponse struct {
Account *model.Account
TOTPs []model.TOTP
}
err = pages["account"].Execute(w, AccountResponse{
Account: account,
TOTPs: totps,
})
if err != nil {
fmt.Printf("WARN: Failed to render admin account page: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
})
}

View file

@ -22,24 +22,24 @@ type TemplateData struct {
Token string Token string
} }
func Handler() http.Handler { func Handler(db *sqlx.DB) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/login", LoginHandler()) mux.Handle("/login", LoginHandler(db))
mux.Handle("/register", createAccountHandler()) mux.Handle("/register", createAccountHandler(db))
mux.Handle("/logout", RequireAccount(global.DB, LogoutHandler())) mux.Handle("/logout", RequireAccount(db, LogoutHandler(db)))
// TODO: /admin/account mux.Handle("/account", RequireAccount(db, AccountHandler(db)))
mux.Handle("/static/", http.StripPrefix("/static", staticHandler())) mux.Handle("/static/", http.StripPrefix("/static", staticHandler()))
mux.Handle("/release/", RequireAccount(global.DB, http.StripPrefix("/release", serveRelease()))) mux.Handle("/release/", RequireAccount(db, http.StripPrefix("/release", serveRelease())))
mux.Handle("/artist/", RequireAccount(global.DB, http.StripPrefix("/artist", serveArtist()))) mux.Handle("/artist/", RequireAccount(db, http.StripPrefix("/artist", serveArtist())))
mux.Handle("/track/", RequireAccount(global.DB, http.StripPrefix("/track", serveTrack()))) mux.Handle("/track/", RequireAccount(db, http.StripPrefix("/track", serveTrack())))
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" { if r.URL.Path != "/" {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
account, err := controller.GetAccountByRequest(global.DB, r) account, err := controller.GetAccountByRequest(db, r)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %s\n", err)
} }
@ -48,21 +48,21 @@ func Handler() http.Handler {
return return
} }
releases, err := controller.GetAllReleases(global.DB, false, 0, true) releases, err := controller.GetAllReleases(db, false, 0, true)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
artists, err := controller.GetAllArtists(global.DB) artists, err := controller.GetAllArtists(db)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull artists: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull artists: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
tracks, err := controller.GetOrphanTracks(global.DB) tracks, err := controller.GetOrphanTracks(db)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull orphan tracks: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull orphan tracks: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -112,10 +112,10 @@ func RequireAccount(db *sqlx.DB, next http.Handler) http.HandlerFunc {
}) })
} }
func LoginHandler() http.Handler { func LoginHandler(db *sqlx.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet { if r.Method == http.MethodGet {
account, err := controller.GetAccountByRequest(global.DB, r) account, err := controller.GetAccountByRequest(db, r)
if err != nil { if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %v\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %v\n", err)
@ -157,7 +157,7 @@ func LoginHandler() http.Handler {
TOTP: r.Form.Get("totp"), TOTP: r.Form.Get("totp"),
} }
account, err := controller.GetAccount(global.DB, credentials.Username) account, err := controller.GetAccount(db, credentials.Username)
if err != nil { if err != nil {
http.Error(w, "Invalid username or password", http.StatusBadRequest) http.Error(w, "Invalid username or password", http.StatusBadRequest)
return return
@ -176,7 +176,7 @@ func LoginHandler() http.Handler {
// TODO: check TOTP // TODO: check TOTP
// login success! // login success!
token, err := controller.CreateToken(global.DB, account.ID, r.UserAgent()) token, err := controller.CreateToken(db, account.ID, r.UserAgent())
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -206,17 +206,17 @@ func LoginHandler() http.Handler {
}) })
} }
func LogoutHandler() http.Handler { func LogoutHandler(db *sqlx.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
tokenStr := controller.GetTokenFromRequest(global.DB, r) tokenStr := controller.GetTokenFromRequest(db, r)
if len(tokenStr) > 0 { if len(tokenStr) > 0 {
err := controller.DeleteToken(global.DB, tokenStr) err := controller.DeleteToken(db, tokenStr)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to revoke token: %v\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to revoke token: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -238,9 +238,9 @@ func LogoutHandler() http.Handler {
}) })
} }
func createAccountHandler() http.Handler { func createAccountHandler(db *sqlx.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
checkAccount, err := controller.GetAccountByRequest(global.DB, r) checkAccount, err := controller.GetAccountByRequest(db, r)
if err != nil { if err != nil {
fmt.Printf("WARN: Failed to fetch account: %s\n", err) fmt.Printf("WARN: Failed to fetch account: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -297,7 +297,7 @@ func createAccountHandler() http.Handler {
} }
// make sure code exists in DB // make sure code exists in DB
invite, err := controller.GetInvite(global.DB, credentials.Invite) invite, err := controller.GetInvite(db, credentials.Invite)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve invite: %v\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve invite: %v\n", err)
render(CreateAccountResponse{ render(CreateAccountResponse{
@ -307,7 +307,7 @@ func createAccountHandler() http.Handler {
} }
if invite == nil || time.Now().After(invite.ExpiresAt) { if invite == nil || time.Now().After(invite.ExpiresAt) {
if invite != nil { if invite != nil {
err := controller.DeleteInvite(global.DB, invite.Code) err := controller.DeleteInvite(db, invite.Code)
if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) } if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) }
} }
render(CreateAccountResponse{ render(CreateAccountResponse{
@ -331,7 +331,7 @@ func createAccountHandler() http.Handler {
Email: credentials.Email, Email: credentials.Email,
AvatarURL: "/img/default-avatar.png", AvatarURL: "/img/default-avatar.png",
} }
err = controller.CreateAccount(global.DB, &account) err = controller.CreateAccount(db, &account)
if err != nil { if err != nil {
if strings.HasPrefix(err.Error(), "pq: duplicate key") { if strings.HasPrefix(err.Error(), "pq: duplicate key") {
render(CreateAccountResponse{ render(CreateAccountResponse{
@ -346,11 +346,11 @@ func createAccountHandler() http.Handler {
return return
} }
err = controller.DeleteInvite(global.DB, invite.Code) err = controller.DeleteInvite(db, invite.Code)
if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) } if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) }
// registration success! // registration success!
token, err := controller.CreateToken(global.DB, account.ID, r.UserAgent()) token, err := controller.CreateToken(db, account.ID, r.UserAgent())
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err)
// gracefully redirect user to login page // gracefully redirect user to login page

View file

@ -0,0 +1,41 @@
@import url("/admin/static/index.css");
form#change-password {
width: 100%;
display: flex;
flex-direction: column;
align-items: start;
}
form div {
width: 20rem;
}
form button {
margin-top: 1rem;
}
label {
width: 100%;
margin: 1rem 0 .5rem 0;
display: block;
color: #10101080;
}
input {
width: 100%;
margin: .5rem 0;
padding: .3rem .5rem;
display: block;
border-radius: 4px;
border: 1px solid #808080;
font-size: inherit;
font-family: inherit;
color: inherit;
}
#error {
background: #ffa9b8;
border: 1px solid #dc5959;
padding: 1em;
border-radius: 4px;
}

View file

@ -99,3 +99,48 @@
opacity: 0.75; opacity: 0.75;
} }
button, .button {
padding: .5em .8em;
font-family: inherit;
font-size: inherit;
border-radius: .5em;
border: 1px solid #a0a0a0;
background: #f0f0f0;
color: inherit;
}
button:hover, .button:hover {
background: #fff;
border-color: #d0d0d0;
}
button:active, .button:active {
background: #d0d0d0;
border-color: #808080;
}
button {
color: inherit;
}
button.save {
background: #6fd7ff;
border-color: #6f9eb0;
}
button.delete {
background: #ff7171;
border-color: #7d3535;
}
button:hover {
background: #fff;
border-color: #d0d0d0;
}
button:active {
background: #d0d0d0;
border-color: #808080;
}
button[disabled] {
background: #d0d0d0 !important;
border-color: #808080 !important;
opacity: .5;
cursor: not-allowed !important;
}

View file

@ -28,6 +28,11 @@ var pages = map[string]*template.Template{
filepath.Join("views", "prideflag.html"), filepath.Join("views", "prideflag.html"),
filepath.Join("admin", "views", "logout.html"), filepath.Join("admin", "views", "logout.html"),
)), )),
"account": template.Must(template.ParseFiles(
filepath.Join("admin", "views", "layout.html"),
filepath.Join("views", "prideflag.html"),
filepath.Join("admin", "views", "edit-account.html"),
)),
"release": template.Must(template.ParseFiles( "release": template.Must(template.ParseFiles(
filepath.Join("admin", "views", "layout.html"), filepath.Join("admin", "views", "layout.html"),

View file

@ -1,7 +1,7 @@
{{define "head"}} {{define "head"}}
<title>Register - ari melody 💫</title> <title>Register - ari melody 💫</title>
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon"> <link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
<link rel="stylesheet" href="/admin/static/index.css">
<style> <style>
p a { p a {
color: #2a67c8; color: #2a67c8;
@ -44,28 +44,6 @@ input {
color: inherit; color: inherit;
} }
button {
padding: .5em .8em;
font-family: inherit;
font-size: inherit;
border-radius: .5em;
border: 1px solid #a0a0a0;
background: #f0f0f0;
color: inherit;
}
button.new {
background: #c4ff6a;
border-color: #84b141;
}
button:hover {
background: #fff;
border-color: #d0d0d0;
}
button:active {
background: #d0d0d0;
border-color: #808080;
}
#error { #error {
background: #ffa9b8; background: #ffa9b8;
border: 1px solid #dc5959; border: 1px solid #dc5959;

View file

@ -1,7 +1,7 @@
{{define "head"}} {{define "head"}}
<title>Account Settings - ari melody 💫</title> <title>Account Settings - ari melody 💫</title>
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon"> <link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
<link rel="stylesheet" href="/admin/static/index.css"> <link rel="stylesheet" href="/admin/static/edit-account.css">
{{end}} {{end}}
{{define "content"}} {{define "content"}}
@ -10,7 +10,8 @@
<div class="card-title"> <div class="card-title">
<h2>Change Password</h2> <h2>Change Password</h2>
</div>
<div class="card">
<form action="/api/v1/change-password" method="POST" id="change-password"> <form action="/api/v1/change-password" method="POST" id="change-password">
<div> <div>
<label for="current-password">Current Password</label> <label for="current-password">Current Password</label>

View file

@ -1,7 +1,6 @@
{{define "head"}} {{define "head"}}
<title>Editing {{.Artist.Name}} - ari melody 💫</title> <title>Editing {{.Artist.Name}} - ari melody 💫</title>
<link rel="shortcut icon" href="{{.Artist.GetAvatar}}" type="image/x-icon"> <link rel="shortcut icon" href="{{.Artist.GetAvatar}}" type="image/x-icon">
<link rel="stylesheet" href="/admin/static/edit-artist.css"> <link rel="stylesheet" href="/admin/static/edit-artist.css">
{{end}} {{end}}

View file

@ -1,7 +1,6 @@
{{define "head"}} {{define "head"}}
<title>Editing {{.Release.Title}} - ari melody 💫</title> <title>Editing {{.Release.Title}} - ari melody 💫</title>
<link rel="shortcut icon" href="{{.Release.GetArtwork}}" type="image/x-icon"> <link rel="shortcut icon" href="{{.Release.GetArtwork}}" type="image/x-icon">
<link rel="stylesheet" href="/admin/static/edit-release.css"> <link rel="stylesheet" href="/admin/static/edit-release.css">
{{end}} {{end}}

View file

@ -1,6 +1,6 @@
{{define "head"}} {{define "head"}}
<title>Editing Track - ari melody 💫</title> <title>Editing Track - ari melody 💫</title>
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
<link rel="stylesheet" href="/admin/static/edit-track.css"> <link rel="stylesheet" href="/admin/static/edit-track.css">
{{end}} {{end}}

View file

@ -47,28 +47,6 @@ input[disabled] {
opacity: .5; opacity: .5;
cursor: not-allowed; cursor: not-allowed;
} }
button {
padding: .5em .8em;
font-family: inherit;
font-size: inherit;
border-radius: .5em;
border: 1px solid #a0a0a0;
background: #f0f0f0;
color: inherit;
}
button.save {
background: #6fd7ff;
border-color: #6f9eb0;
}
button:hover {
background: #fff;
border-color: #d0d0d0;
}
button:active {
background: #d0d0d0;
border-color: #808080;
}
</style> </style>
{{end}} {{end}}

View file

@ -6,11 +6,12 @@ import (
"strings" "strings"
"arimelody-web/admin" "arimelody-web/admin"
"arimelody-web/global"
"arimelody-web/controller" "arimelody-web/controller"
"github.com/jmoiron/sqlx"
) )
func Handler() http.Handler { func Handler(db *sqlx.DB) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
// ACCOUNT ENDPOINTS // ACCOUNT ENDPOINTS
@ -31,7 +32,7 @@ func Handler() http.Handler {
mux.Handle("/v1/artist/", http.StripPrefix("/v1/artist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.Handle("/v1/artist/", http.StripPrefix("/v1/artist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var artistID = strings.Split(r.URL.Path[1:], "/")[0] var artistID = strings.Split(r.URL.Path[1:], "/")[0]
artist, err := controller.GetArtist(global.DB, artistID) artist, err := controller.GetArtist(db, artistID)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "no rows") { if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r) http.NotFound(w, r)
@ -48,10 +49,10 @@ func Handler() http.Handler {
ServeArtist(artist).ServeHTTP(w, r) ServeArtist(artist).ServeHTTP(w, r)
case http.MethodPut: case http.MethodPut:
// PUT /api/v1/artist/{id} (admin) // PUT /api/v1/artist/{id} (admin)
admin.RequireAccount(global.DB, UpdateArtist(artist)).ServeHTTP(w, r) admin.RequireAccount(db, UpdateArtist(artist)).ServeHTTP(w, r)
case http.MethodDelete: case http.MethodDelete:
// DELETE /api/v1/artist/{id} (admin) // DELETE /api/v1/artist/{id} (admin)
admin.RequireAccount(global.DB, DeleteArtist(artist)).ServeHTTP(w, r) admin.RequireAccount(db, DeleteArtist(artist)).ServeHTTP(w, r)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }
@ -63,7 +64,7 @@ func Handler() http.Handler {
ServeAllArtists().ServeHTTP(w, r) ServeAllArtists().ServeHTTP(w, r)
case http.MethodPost: case http.MethodPost:
// POST /api/v1/artist (admin) // POST /api/v1/artist (admin)
admin.RequireAccount(global.DB, CreateArtist()).ServeHTTP(w, r) admin.RequireAccount(db, CreateArtist()).ServeHTTP(w, r)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }
@ -73,7 +74,7 @@ func Handler() http.Handler {
mux.Handle("/v1/music/", http.StripPrefix("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.Handle("/v1/music/", http.StripPrefix("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var releaseID = strings.Split(r.URL.Path[1:], "/")[0] var releaseID = strings.Split(r.URL.Path[1:], "/")[0]
release, err := controller.GetRelease(global.DB, releaseID, true) release, err := controller.GetRelease(db, releaseID, true)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "no rows") { if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r) http.NotFound(w, r)
@ -90,10 +91,10 @@ func Handler() http.Handler {
ServeRelease(release).ServeHTTP(w, r) ServeRelease(release).ServeHTTP(w, r)
case http.MethodPut: case http.MethodPut:
// PUT /api/v1/music/{id} (admin) // PUT /api/v1/music/{id} (admin)
admin.RequireAccount(global.DB, UpdateRelease(release)).ServeHTTP(w, r) admin.RequireAccount(db, UpdateRelease(release)).ServeHTTP(w, r)
case http.MethodDelete: case http.MethodDelete:
// DELETE /api/v1/music/{id} (admin) // DELETE /api/v1/music/{id} (admin)
admin.RequireAccount(global.DB, DeleteRelease(release)).ServeHTTP(w, r) admin.RequireAccount(db, DeleteRelease(release)).ServeHTTP(w, r)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }
@ -105,7 +106,7 @@ func Handler() http.Handler {
ServeCatalog().ServeHTTP(w, r) ServeCatalog().ServeHTTP(w, r)
case http.MethodPost: case http.MethodPost:
// POST /api/v1/music (admin) // POST /api/v1/music (admin)
admin.RequireAccount(global.DB, CreateRelease()).ServeHTTP(w, r) admin.RequireAccount(db, CreateRelease()).ServeHTTP(w, r)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }
@ -115,7 +116,7 @@ func Handler() http.Handler {
mux.Handle("/v1/track/", http.StripPrefix("/v1/track", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.Handle("/v1/track/", http.StripPrefix("/v1/track", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var trackID = strings.Split(r.URL.Path[1:], "/")[0] var trackID = strings.Split(r.URL.Path[1:], "/")[0]
track, err := controller.GetTrack(global.DB, trackID) track, err := controller.GetTrack(db, trackID)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "no rows") { if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r) http.NotFound(w, r)
@ -129,13 +130,13 @@ func Handler() http.Handler {
switch r.Method { switch r.Method {
case http.MethodGet: case http.MethodGet:
// GET /api/v1/track/{id} (admin) // GET /api/v1/track/{id} (admin)
admin.RequireAccount(global.DB, ServeTrack(track)).ServeHTTP(w, r) admin.RequireAccount(db, ServeTrack(track)).ServeHTTP(w, r)
case http.MethodPut: case http.MethodPut:
// PUT /api/v1/track/{id} (admin) // PUT /api/v1/track/{id} (admin)
admin.RequireAccount(global.DB, UpdateTrack(track)).ServeHTTP(w, r) admin.RequireAccount(db, UpdateTrack(track)).ServeHTTP(w, r)
case http.MethodDelete: case http.MethodDelete:
// DELETE /api/v1/track/{id} (admin) // DELETE /api/v1/track/{id} (admin)
admin.RequireAccount(global.DB, DeleteTrack(track)).ServeHTTP(w, r) admin.RequireAccount(db, DeleteTrack(track)).ServeHTTP(w, r)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }
@ -144,10 +145,10 @@ func Handler() http.Handler {
switch r.Method { switch r.Method {
case http.MethodGet: case http.MethodGet:
// GET /api/v1/track (admin) // GET /api/v1/track (admin)
admin.RequireAccount(global.DB, ServeAllTracks()).ServeHTTP(w, r) admin.RequireAccount(db, ServeAllTracks()).ServeHTTP(w, r)
case http.MethodPost: case http.MethodPost:
// POST /api/v1/track (admin) // POST /api/v1/track (admin)
admin.RequireAccount(global.DB, CreateTrack()).ServeHTTP(w, r) admin.RequireAccount(db, CreateTrack()).ServeHTTP(w, r)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }

View file

@ -126,7 +126,7 @@ func UpdateTrack(track *model.Track) http.Handler {
err = controller.UpdateTrack(global.DB, track) err = controller.UpdateTrack(global.DB, track)
if err != nil { if err != nil {
fmt.Printf("Failed to update track %s: %s\n", track.ID, err) fmt.Printf("WARN: Failed to update track %s: %s\n", track.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
@ -151,7 +151,7 @@ func DeleteTrack(track *model.Track) http.Handler {
var trackID = r.URL.Path[1:] var trackID = r.URL.Path[1:]
err := controller.DeleteTrack(global.DB, trackID) err := controller.DeleteTrack(global.DB, trackID)
if err != nil { if err != nil {
fmt.Printf("Failed to delete track %s: %s\n", trackID, err) fmt.Printf("WARN: Failed to delete track %s: %s\n", trackID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
}) })

View file

@ -341,9 +341,9 @@ func main() {
func createServeMux() *http.ServeMux { func createServeMux() *http.ServeMux {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/admin/", http.StripPrefix("/admin", admin.Handler())) mux.Handle("/admin/", http.StripPrefix("/admin", admin.Handler(global.DB)))
mux.Handle("/api/", http.StripPrefix("/api", api.Handler())) mux.Handle("/api/", http.StripPrefix("/api", api.Handler(global.DB)))
mux.Handle("/music/", http.StripPrefix("/music", view.MusicHandler())) mux.Handle("/music/", http.StripPrefix("/music", view.MusicHandler(global.DB)))
mux.Handle("/uploads/", http.StripPrefix("/uploads", staticHandler(filepath.Join(global.Config.DataDirectory, "uploads")))) mux.Handle("/uploads/", http.StripPrefix("/uploads", staticHandler(filepath.Join(global.Config.DataDirectory, "uploads"))))
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodHead { if r.Method == http.MethodHead {

View file

@ -6,37 +6,38 @@ import (
"os" "os"
"arimelody-web/controller" "arimelody-web/controller"
"arimelody-web/global"
"arimelody-web/model" "arimelody-web/model"
"arimelody-web/templates" "arimelody-web/templates"
"github.com/jmoiron/sqlx"
) )
// HTTP HANDLER METHODS // HTTP HANDLER METHODS
func MusicHandler() http.Handler { func MusicHandler(db *sqlx.DB) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" { if r.URL.Path == "/" {
ServeCatalog().ServeHTTP(w, r) ServeCatalog(db).ServeHTTP(w, r)
return return
} }
release, err := controller.GetRelease(global.DB, r.URL.Path[1:], true) release, err := controller.GetRelease(db, r.URL.Path[1:], true)
if err != nil { if err != nil {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
ServeGateway(release).ServeHTTP(w, r) ServeGateway(db, release).ServeHTTP(w, r)
})) }))
return mux return mux
} }
func ServeCatalog() http.Handler { func ServeCatalog(db *sqlx.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
releases, err := controller.GetAllReleases(global.DB, true, 0, true) releases, err := controller.GetAllReleases(db, true, 0, true)
if err != nil { if err != nil {
fmt.Printf("FATAL: Failed to pull releases for catalog: %s\n", err) fmt.Printf("FATAL: Failed to pull releases for catalog: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -56,12 +57,12 @@ func ServeCatalog() http.Handler {
}) })
} }
func ServeGateway(release *model.Release) http.Handler { func ServeGateway(db *sqlx.DB, release *model.Release) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// only allow authorised users to view hidden releases // only allow authorised users to view hidden releases
privileged := false privileged := false
if !release.Visible { if !release.Visible {
account, err := controller.GetAccountByRequest(global.DB, r) account, err := controller.GetAccountByRequest(db, r)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %v\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)