very rough updates to admin pages, reduced reliance on global.DB
This commit is contained in:
parent
ae254dd731
commit
7044f7344b
15 changed files with 192 additions and 106 deletions
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
|
||||
}
|
||||
|
||||
func Handler() http.Handler {
|
||||
func Handler(db *sqlx.DB) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle("/login", LoginHandler())
|
||||
mux.Handle("/register", createAccountHandler())
|
||||
mux.Handle("/logout", RequireAccount(global.DB, LogoutHandler()))
|
||||
// TODO: /admin/account
|
||||
mux.Handle("/login", LoginHandler(db))
|
||||
mux.Handle("/register", createAccountHandler(db))
|
||||
mux.Handle("/logout", RequireAccount(db, LogoutHandler(db)))
|
||||
mux.Handle("/account", RequireAccount(db, AccountHandler(db)))
|
||||
mux.Handle("/static/", http.StripPrefix("/static", staticHandler()))
|
||||
mux.Handle("/release/", RequireAccount(global.DB, http.StripPrefix("/release", serveRelease())))
|
||||
mux.Handle("/artist/", RequireAccount(global.DB, http.StripPrefix("/artist", serveArtist())))
|
||||
mux.Handle("/track/", RequireAccount(global.DB, http.StripPrefix("/track", serveTrack())))
|
||||
mux.Handle("/release/", RequireAccount(db, http.StripPrefix("/release", serveRelease())))
|
||||
mux.Handle("/artist/", RequireAccount(db, http.StripPrefix("/artist", serveArtist())))
|
||||
mux.Handle("/track/", RequireAccount(db, http.StripPrefix("/track", serveTrack())))
|
||||
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
account, err := controller.GetAccountByRequest(global.DB, r)
|
||||
account, err := controller.GetAccountByRequest(db, r)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %s\n", err)
|
||||
}
|
||||
|
@ -48,21 +48,21 @@ func Handler() http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
releases, err := controller.GetAllReleases(global.DB, false, 0, true)
|
||||
releases, err := controller.GetAllReleases(db, false, 0, true)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
artists, err := controller.GetAllArtists(global.DB)
|
||||
artists, err := controller.GetAllArtists(db)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to pull artists: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
tracks, err := controller.GetOrphanTracks(global.DB)
|
||||
tracks, err := controller.GetOrphanTracks(db)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to pull orphan tracks: %s\n", err)
|
||||
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) {
|
||||
if r.Method == http.MethodGet {
|
||||
account, err := controller.GetAccountByRequest(global.DB, r)
|
||||
account, err := controller.GetAccountByRequest(db, r)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
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"),
|
||||
}
|
||||
|
||||
account, err := controller.GetAccount(global.DB, credentials.Username)
|
||||
account, err := controller.GetAccount(db, credentials.Username)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid username or password", http.StatusBadRequest)
|
||||
return
|
||||
|
@ -176,7 +176,7 @@ func LoginHandler() http.Handler {
|
|||
// TODO: check TOTP
|
||||
|
||||
// login success!
|
||||
token, err := controller.CreateToken(global.DB, account.ID, r.UserAgent())
|
||||
token, err := controller.CreateToken(db, account.ID, r.UserAgent())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err)
|
||||
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) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
tokenStr := controller.GetTokenFromRequest(global.DB, r)
|
||||
tokenStr := controller.GetTokenFromRequest(db, r)
|
||||
|
||||
if len(tokenStr) > 0 {
|
||||
err := controller.DeleteToken(global.DB, tokenStr)
|
||||
err := controller.DeleteToken(db, tokenStr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to revoke token: %v\n", err)
|
||||
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) {
|
||||
checkAccount, err := controller.GetAccountByRequest(global.DB, r)
|
||||
checkAccount, err := controller.GetAccountByRequest(db, r)
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Failed to fetch account: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
|
@ -297,7 +297,7 @@ func createAccountHandler() http.Handler {
|
|||
}
|
||||
|
||||
// make sure code exists in DB
|
||||
invite, err := controller.GetInvite(global.DB, credentials.Invite)
|
||||
invite, err := controller.GetInvite(db, credentials.Invite)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve invite: %v\n", err)
|
||||
render(CreateAccountResponse{
|
||||
|
@ -307,7 +307,7 @@ func createAccountHandler() http.Handler {
|
|||
}
|
||||
if invite == nil || time.Now().After(invite.ExpiresAt) {
|
||||
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) }
|
||||
}
|
||||
render(CreateAccountResponse{
|
||||
|
@ -331,7 +331,7 @@ func createAccountHandler() http.Handler {
|
|||
Email: credentials.Email,
|
||||
AvatarURL: "/img/default-avatar.png",
|
||||
}
|
||||
err = controller.CreateAccount(global.DB, &account)
|
||||
err = controller.CreateAccount(db, &account)
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "pq: duplicate key") {
|
||||
render(CreateAccountResponse{
|
||||
|
@ -346,11 +346,11 @@ func createAccountHandler() http.Handler {
|
|||
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) }
|
||||
|
||||
// registration success!
|
||||
token, err := controller.CreateToken(global.DB, account.ID, r.UserAgent())
|
||||
token, err := controller.CreateToken(db, account.ID, r.UserAgent())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err)
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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("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(
|
||||
filepath.Join("admin", "views", "layout.html"),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{define "head"}}
|
||||
<title>Register - ari melody 💫</title>
|
||||
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
|
||||
|
||||
<link rel="stylesheet" href="/admin/static/index.css">
|
||||
<style>
|
||||
p a {
|
||||
color: #2a67c8;
|
||||
|
@ -44,28 +44,6 @@ input {
|
|||
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 {
|
||||
background: #ffa9b8;
|
||||
border: 1px solid #dc5959;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{define "head"}}
|
||||
<title>Account Settings - ari melody 💫</title>
|
||||
<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}}
|
||||
|
||||
{{define "content"}}
|
||||
|
@ -10,7 +10,8 @@
|
|||
|
||||
<div class="card-title">
|
||||
<h2>Change Password</h2>
|
||||
|
||||
</div>
|
||||
<div class="card">
|
||||
<form action="/api/v1/change-password" method="POST" id="change-password">
|
||||
<div>
|
||||
<label for="current-password">Current Password</label>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{{define "head"}}
|
||||
<title>Editing {{.Artist.Name}} - ari melody 💫</title>
|
||||
<link rel="shortcut icon" href="{{.Artist.GetAvatar}}" type="image/x-icon">
|
||||
|
||||
<link rel="stylesheet" href="/admin/static/edit-artist.css">
|
||||
{{end}}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{{define "head"}}
|
||||
<title>Editing {{.Release.Title}} - ari melody 💫</title>
|
||||
<link rel="shortcut icon" href="{{.Release.GetArtwork}}" type="image/x-icon">
|
||||
|
||||
<link rel="stylesheet" href="/admin/static/edit-release.css">
|
||||
{{end}}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{define "head"}}
|
||||
<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">
|
||||
{{end}}
|
||||
|
||||
|
|
|
@ -47,28 +47,6 @@ input[disabled] {
|
|||
opacity: .5;
|
||||
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>
|
||||
{{end}}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue