feat(subsonic): add avatar support

closes: #228
This commit is contained in:
Brian Doherty
2022-07-20 23:16:13 +01:00
committed by sentriz
parent 7ab378accb
commit 5e66261f0c
15 changed files with 288 additions and 28 deletions

View File

@@ -2,6 +2,7 @@
package ctrladmin
import (
"encoding/base64"
"encoding/gob"
"errors"
"fmt"
@@ -21,10 +22,10 @@ import (
"github.com/sentriz/gormstore"
"go.senan.xyz/gonic"
"go.senan.xyz/gonic/server/assets"
"go.senan.xyz/gonic/server/ctrlbase"
"go.senan.xyz/gonic/db"
"go.senan.xyz/gonic/podcasts"
"go.senan.xyz/gonic/server/assets"
"go.senan.xyz/gonic/server/ctrlbase"
)
type CtxKey int
@@ -47,6 +48,7 @@ func funcMap() template.FuncMap {
return strings.ToLower(in.Format("Jan 02, 2006"))
},
"dateHuman": humanize.Time,
"base64": base64.StdEncoding.EncodeToString,
}
}
@@ -122,8 +124,11 @@ type templateData struct {
DefaultListenBrainzURL string
SelectedUser *db.User
Podcasts []*db.Podcast
Podcasts []*db.Podcast
InternetRadioStations []*db.InternetRadioStation
// avatar
Avatar []byte
}
type Response struct {

View File

@@ -1,7 +1,12 @@
package ctrladmin
import (
"bytes"
"fmt"
"image"
_ "image/gif" // to decode uploaded GIF avatars
"image/jpeg"
_ "image/png" // to decode uploaded PNG avatars
"log"
"net/http"
"net/url"
@@ -9,6 +14,7 @@ import (
"time"
"github.com/mmcdole/gofeed"
"github.com/nfnt/resize"
"go.senan.xyz/gonic/db"
"go.senan.xyz/gonic/scanner"
@@ -120,6 +126,37 @@ func (c *Controller) ServeChangeOwnPasswordDo(r *http.Request) *Response {
return &Response{redirect: "/admin/home"}
}
func (c *Controller) ServeChangeOwnAvatar(r *http.Request) *Response {
data := &templateData{}
user := r.Context().Value(CtxUser).(*db.User)
data.SelectedUser = user
return &Response{
template: "change_own_avatar.tmpl",
data: data,
}
}
func (c *Controller) ServeChangeOwnAvatarDo(r *http.Request) *Response {
user := r.Context().Value(CtxUser).(*db.User)
avatar, err := getAvatarFile(r)
if err != nil {
return &Response{
redirect: r.Referer(),
flashW: []string{err.Error()},
}
}
user.Avatar = avatar
c.DB.Save(user)
return &Response{redirect: "/admin/home"}
}
func (c *Controller) ServeDeleteOwnAvatarDo(r *http.Request) *Response {
user := r.Context().Value(CtxUser).(*db.User)
user.Avatar = nil
c.DB.Save(user)
return &Response{redirect: "/admin/home"}
}
func (c *Controller) ServeLinkLastFMDo(r *http.Request) *Response {
token := r.URL.Query().Get("token")
if token == "" {
@@ -242,6 +279,46 @@ func (c *Controller) ServeChangePasswordDo(r *http.Request) *Response {
return &Response{redirect: "/admin/home"}
}
func (c *Controller) ServeChangeAvatar(r *http.Request) *Response {
username := r.URL.Query().Get("user")
if username == "" {
return &Response{code: 400, err: "please provide a username"}
}
user := c.DB.GetUserByName(username)
if user == nil {
return &Response{code: 400, err: "couldn't find a user with that name"}
}
data := &templateData{}
data.SelectedUser = user
return &Response{
template: "change_avatar.tmpl",
data: data,
}
}
func (c *Controller) ServeChangeAvatarDo(r *http.Request) *Response {
username := r.URL.Query().Get("user")
user := c.DB.GetUserByName(username)
avatar, err := getAvatarFile(r)
if err != nil {
return &Response{
redirect: r.Referer(),
flashW: []string{err.Error()},
}
}
user.Avatar = avatar
c.DB.Save(user)
return &Response{redirect: "/admin/home"}
}
func (c *Controller) ServeDeleteAvatarDo(r *http.Request) *Response {
username := r.URL.Query().Get("user")
user := c.DB.GetUserByName(username)
user.Avatar = nil
c.DB.Save(user)
return &Response{redirect: "/admin/home"}
}
func (c *Controller) ServeDeleteUser(r *http.Request) *Response {
username := r.URL.Query().Get("user")
if username == "" {
@@ -278,8 +355,7 @@ func (c *Controller) ServeCreateUser(r *http.Request) *Response {
func (c *Controller) ServeCreateUserDo(r *http.Request) *Response {
username := r.FormValue("username")
err := validateUsername(username)
if err != nil {
if err := validateUsername(username); err != nil {
return &Response{
redirect: r.Referer(),
flashW: []string{err.Error()},
@@ -287,8 +363,7 @@ func (c *Controller) ServeCreateUserDo(r *http.Request) *Response {
}
passwordOne := r.FormValue("password_one")
passwordTwo := r.FormValue("password_two")
err = validatePasswords(passwordOne, passwordTwo)
if err != nil {
if err := validatePasswords(passwordOne, passwordTwo); err != nil {
return &Response{
redirect: r.Referer(),
flashW: []string{err.Error()},
@@ -570,3 +645,24 @@ func (c *Controller) ServeInternetRadioStationDeleteDo(r *http.Request) *Respons
redirect: "/admin/home",
}
}
func getAvatarFile(r *http.Request) ([]byte, error) {
err := r.ParseMultipartForm(10 << 20) // keep up to 10MB in memory
if err != nil {
return nil, err
}
file, _, err := r.FormFile("avatar")
if err != nil {
return nil, fmt.Errorf("read form file: %w", err)
}
i, _, err := image.Decode(file)
if err != nil {
return nil, fmt.Errorf("decode image: %w", err)
}
resized := resize.Resize(64, 64, i, resize.Lanczos3)
var buff bytes.Buffer
if err := jpeg.Encode(&buff, resized, nil); err != nil {
return nil, err
}
return buff.Bytes(), nil
}