very rough updates to admin pages, reduced reliance on global.DB
This commit is contained in:
parent
ae254dd731
commit
7044f7344b
38
admin/accounthttp.go
Normal file
38
admin/accounthttp.go
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
41
admin/static/edit-account.css
Normal file
41
admin/static/edit-account.css
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
||||||
|
|
33
api/api.go
33
api/api.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
6
main.go
6
main.go
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue