@@ -43,6 +43,7 @@ func (db *DB) Migrate(ctx MigrationContext) error {
|
||||
construct(ctx, "202202241218", migratePublicPlaylist),
|
||||
construct(ctx, "202204270903", migratePodcastDropUserID),
|
||||
construct(ctx, "202206011628", migrateInternetRadioStations),
|
||||
construct(ctx, "202206101425", migrateUser),
|
||||
}
|
||||
|
||||
return gormigrate.
|
||||
@@ -363,3 +364,10 @@ func migrateInternetRadioStations(tx *gorm.DB, _ MigrationContext) error {
|
||||
).
|
||||
Error
|
||||
}
|
||||
|
||||
func migrateUser(tx *gorm.DB, _ MigrationContext) error {
|
||||
return tx.AutoMigrate(
|
||||
User{},
|
||||
).
|
||||
Error
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ type User struct {
|
||||
ListenBrainzURL string `sql:"default: null"`
|
||||
ListenBrainzToken string `sql:"default: null"`
|
||||
IsAdmin bool `sql:"default: null"`
|
||||
Avatar []byte `sql:"default: null"`
|
||||
}
|
||||
|
||||
type Setting struct {
|
||||
@@ -398,10 +399,10 @@ type Bookmark struct {
|
||||
}
|
||||
|
||||
type InternetRadioStation struct {
|
||||
ID int `gorm:"primary_key"`
|
||||
StreamURL string
|
||||
Name string
|
||||
HomepageURL string
|
||||
ID int `gorm:"primary_key"`
|
||||
StreamURL string
|
||||
Name string
|
||||
HomepageURL string
|
||||
}
|
||||
|
||||
func (ir *InternetRadioStation) SID() *specid.ID {
|
||||
|
||||
36
go.mod
36
go.mod
@@ -7,44 +7,58 @@ require (
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/PuerkitoBio/goquery v1.8.0 // indirect
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/faiface/beep v1.1.0
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.21.1 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2 // indirect
|
||||
github.com/hajimehoshi/go-mp3 v0.3.3 // indirect
|
||||
github.com/hajimehoshi/oto v1.0.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/icza/bitio v1.1.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/jinzhu/gorm v1.9.17-0.20211120011537-5c235b72a414
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.2 // indirect
|
||||
github.com/josephburnett/jd v0.0.0-20191228205456-aa1a7c66b42f
|
||||
github.com/josephburnett/jd v1.5.2
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lib/pq v1.3.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/mattn/go-sqlite3 v1.14.11
|
||||
github.com/mattn/go-sqlite3 v1.14.13
|
||||
github.com/mewkiz/flac v1.0.7 // indirect
|
||||
github.com/mewkiz/pkg v0.0.0-20211102230744-16a6ce8f1b77 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/mmcdole/gofeed v1.1.3
|
||||
github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/nicksellen/audiotags v0.0.0-20160226222119-94015fa599bd
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
|
||||
github.com/peterbourgon/ff v1.7.1
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be
|
||||
github.com/sentriz/gormstore v0.0.0-20220105134332-64e31f7f6981
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220209155544-dad33157f4bf // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20220407100705-7b9b53b0aca4 // indirect
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
||||
golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20220609121020-a51bd0440498 // indirect
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd // indirect
|
||||
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd // indirect
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/gormigrate.v1 v1.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
49
go.sum
49
go.sum
@@ -20,6 +20,7 @@ github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEq
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -40,6 +41,11 @@ github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9
|
||||
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
|
||||
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
|
||||
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
@@ -64,6 +70,8 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
||||
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2 h1:xSYNE2F3lxtOu9BRjCWHHceg7S91IHfXfXp5+LYQI7s=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.3 h1:cWnfRdpye2m9ElSoVqneYRcpt/l3ijttgjMeQh+r+FE=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.3/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
||||
github.com/hajimehoshi/oto v1.0.1 h1:8AMnq0Yr2YmzaiqTg/k1Yzd6IygUGk2we9nmjgbgPn4=
|
||||
@@ -77,6 +85,8 @@ github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lTo
|
||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/jezek/xgb v1.0.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
|
||||
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
|
||||
@@ -94,15 +104,28 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/josephburnett/jd v0.0.0-20191228205456-aa1a7c66b42f h1:ijUonnyvDekPD7lUF4oQ1LV+dKaTnchEzmenMFa6NL4=
|
||||
github.com/josephburnett/jd v0.0.0-20191228205456-aa1a7c66b42f/go.mod h1:aeV+6oc13ogwzcRNHBe4vbyLmoQxMfEDoqyqCU9oE30=
|
||||
github.com/josephburnett/jd v1.5.2 h1:RzE34nVV4kdTmFY5Cl7Wrwwo7J3AKlFMQk9x89GvIHE=
|
||||
github.com/josephburnett/jd v1.5.2/go.mod h1:2pSZGHitQCumXDDTxmJehndlsltrTeVAhrzP8WfFeuc=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jszwec/csvutil v1.5.1/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
@@ -110,6 +133,8 @@ github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I=
|
||||
github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mewkiz/flac v1.0.7 h1:uIXEjnuXqdRaZttmSFM5v5Ukp4U6orrZsnYGGR3yow8=
|
||||
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
|
||||
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
|
||||
@@ -131,8 +156,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/nicksellen/audiotags v0.0.0-20160226222119-94015fa599bd h1:xKn/gU8lZupoZt/HE7a/R3aH93iUO6JwyRsYelQUsRI=
|
||||
github.com/nicksellen/audiotags v0.0.0-20160226222119-94015fa599bd/go.mod h1:B6icauz2l4tkYQxmDtCH4qmNWz/evSW5CsOqp6IE5IE=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
@@ -155,6 +183,7 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
@@ -167,6 +196,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220209155544-dad33157f4bf h1:gdgmgieTI2lLaGI2N+xEiaCMUgo2XFmAS0rlF8HZoso=
|
||||
golang.org/x/crypto v0.0.0-20220209155544-dad33157f4bf/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
@@ -174,17 +205,23 @@ golang.org/x/exp/shiny v0.0.0-20220209042442-160e291fcf24 h1:jn6Q9FOmCn1Kk7ec3Qm
|
||||
golang.org/x/exp/shiny v0.0.0-20220209042442-160e291fcf24/go.mod h1:NtXcNtv5Wu0zUbBl574y/D5MMZvnQnV3sgjZxbs64Jo=
|
||||
golang.org/x/exp/shiny v0.0.0-20220407100705-7b9b53b0aca4 h1:ywNGLBFk8tKaiu+GYZeoXWzrFoJ/a1LHYKy1lb3R9cM=
|
||||
golang.org/x/exp/shiny v0.0.0-20220407100705-7b9b53b0aca4/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
|
||||
golang.org/x/exp/shiny v0.0.0-20220609121020-a51bd0440498 h1:mJjyic/dxHcz1W6IUE8zf6+RltuO8+9mS45tTtb4F6k=
|
||||
golang.org/x/exp/shiny v0.0.0-20220609121020-a51bd0440498/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
|
||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw=
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 h1:jhDgkcu3yQ4tasBZ+1YwDmK7eFmuVf1w1k+NGGGxfmE=
|
||||
golang.org/x/mobile v0.0.0-20220112015953-858099ff7816/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
||||
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd h1:x1GptNaTtxPAlTVIAJk61fuXg0y17h09DTxyb+VNC/k=
|
||||
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -203,6 +240,8 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -219,6 +258,9 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d h1:Bm7BNOQt2Qv7ZqysjeLjgCBanX+88Z/OtdvsrEv1Djc=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -237,11 +279,18 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI=
|
||||
gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
26
server/assets/pages/change_avatar.tmpl
Normal file
26
server/assets/pages/change_avatar.tmpl
Normal file
@@ -0,0 +1,26 @@
|
||||
{{ define "user" }}
|
||||
<div class="padded box">
|
||||
<div class="box-title">
|
||||
<i class="mdi mdi-account-key"></i> changing {{ .SelectedUser.Name }}'s avatar
|
||||
</div>
|
||||
{{ if ne (len .SelectedUser.Avatar) 0 }}
|
||||
<form class="block" action="{{ printf "/admin/delete_avatar_do?user=%s" .SelectedUser.Name | path }}" method="post">
|
||||
<input type="submit" value="delete avatar">
|
||||
</form>
|
||||
{{ end }}
|
||||
<form
|
||||
class="block file-upload"
|
||||
enctype="multipart/form-data"
|
||||
action="{{ printf "/admin/change_avatar_do?user=%s" .SelectedUser.Name | path }}"
|
||||
method="post"
|
||||
>
|
||||
<div style="position: relative;">
|
||||
<input style="position: absolute; opacity: 0;" name="avatar" type="file" accept="image/jpeg image/png image/gif" />
|
||||
<input type="button" value="upload avatar">
|
||||
</div>
|
||||
{{ if ne (len .SelectedUser.Avatar) 0 }}
|
||||
<p><img class="avatar-preview" src="data:image/jpg;base64,{{ .SelectedUser.Avatar | base64 }}"/></p>
|
||||
{{ end }}
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
26
server/assets/pages/change_own_avatar.tmpl
Normal file
26
server/assets/pages/change_own_avatar.tmpl
Normal file
@@ -0,0 +1,26 @@
|
||||
{{ define "user" }}
|
||||
<div class="padded box">
|
||||
<div class="box-title">
|
||||
<i class="mdi mdi-account-key"></i> changing account avatar
|
||||
</div>
|
||||
{{ if ne (len .SelectedUser.Avatar) 0 }}
|
||||
<form class="block" action="{{ path "/admin/delete_own_avatar_do" }}" method="post">
|
||||
<input type="submit" value="delete avatar">
|
||||
</form>
|
||||
{{ end }}
|
||||
<form
|
||||
class="block file-upload"
|
||||
enctype="multipart/form-data"
|
||||
action="{{ path "/admin/change_own_avatar_do" }}"
|
||||
method="post"
|
||||
>
|
||||
<div style="position: relative;">
|
||||
<input style="position: absolute; opacity: 0;" name="avatar" type="file" accept="image/jpeg image/png image/gif" />
|
||||
<input type="button" value="upload avatar">
|
||||
</div>
|
||||
{{ if ne (len .SelectedUser.Avatar) 0 }}
|
||||
<p><img class="avatar-preview" src="data:image/jpg;base64,{{ .SelectedUser.Avatar | base64 }}"/></p>
|
||||
{{ end }}
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -82,6 +82,8 @@
|
||||
<span class="text-light">|</span>
|
||||
<a href="{{ printf "/admin/change_password?user=%s" $user.Name | path }}">password…</a>
|
||||
<span class="text-light">|</span>
|
||||
<a href="{{ printf "/admin/change_avatar?user=%s" $user.Name | path }}">change avatar…</a>
|
||||
<span class="text-light">|</span>
|
||||
{{ if $user.IsAdmin }}
|
||||
<span class="text-light">delete…</span>
|
||||
{{ else }}
|
||||
@@ -100,6 +102,8 @@
|
||||
<a href="{{ path "/admin/change_own_username" }}" class="button">change username…</a>
|
||||
<span class="text-light">|</span>
|
||||
<a href="{{ path "/admin/change_own_password" }}" class="button">change password…</a>
|
||||
<span class="text-light">|</span>
|
||||
<a href="{{ path "/admin/change_own_avatar" }}" class="button">change avatar…</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
@@ -260,17 +264,16 @@
|
||||
{{ end }}
|
||||
</table>
|
||||
<form
|
||||
id="playlist-upload-form"
|
||||
class="file-upload"
|
||||
enctype="multipart/form-data"
|
||||
action="{{ path "/admin/upload_playlist_do" }}"
|
||||
method="post"
|
||||
>
|
||||
<div style="position: relative;">
|
||||
<input id="playlist-upload-input" style="position: absolute; opacity: 0;" name="playlist-files" type="file" multiple />
|
||||
<input style="position: absolute; opacity: 0;" name="playlist-files" type="file" multiple />
|
||||
<input type="button" value="upload m3u8">
|
||||
</div>
|
||||
</form>
|
||||
<script src="{{ path "/admin/static/playlist-upload.js" }}"></script>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
@@ -5,4 +5,5 @@
|
||||
<link rel="shortcut icon" href="{{ path "/admin/static/favicon.ico" }}" type="image/x-icon">
|
||||
<link rel="icon" href="{{ path "/admin/static/favicon.ico" }}" type="image/x-icon">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<script src="{{ path "/admin/static/main.js" }}" defer></script>
|
||||
{{ end }}
|
||||
|
||||
@@ -178,3 +178,9 @@ a:hover {
|
||||
.angry {
|
||||
background-color: #f4433669;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
5
server/assets/static/main.js
Normal file
5
server/assets/static/main.js
Normal file
@@ -0,0 +1,5 @@
|
||||
for (const form of document.querySelectorAll("form.file-upload") || []) {
|
||||
const input = form.querySelector("input[type=file]");
|
||||
if (!input) continue;
|
||||
input.onchange = (e) => form.submit();
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
document.getElementById("playlist-upload-input").onchange = e => {
|
||||
document.getElementById("playlist-upload-form").submit();
|
||||
};
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ctrlsubsonic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -297,3 +298,18 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) ServeGetAvatar(w http.ResponseWriter, r *http.Request) *spec.Response {
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
username, err := params.Get("username")
|
||||
if err != nil {
|
||||
return spec.NewError(10, "please provide an `username` parameter")
|
||||
}
|
||||
reqUser := c.DB.GetUserByName(username)
|
||||
if (user != reqUser) && !user.IsAdmin {
|
||||
return spec.NewError(50, "user not admin")
|
||||
}
|
||||
http.ServeContent(w, r, "", time.Now(), bytes.NewReader(reqUser.Avatar))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -161,6 +161,9 @@ func setupAdmin(r *mux.Router, ctrl *ctrladmin.Controller) {
|
||||
routUser.Handle("/change_own_username_do", ctrl.H(ctrl.ServeChangeOwnUsernameDo))
|
||||
routUser.Handle("/change_own_password", ctrl.H(ctrl.ServeChangeOwnPassword))
|
||||
routUser.Handle("/change_own_password_do", ctrl.H(ctrl.ServeChangeOwnPasswordDo))
|
||||
routUser.Handle("/change_own_avatar", ctrl.H(ctrl.ServeChangeOwnAvatar))
|
||||
routUser.Handle("/change_own_avatar_do", ctrl.H(ctrl.ServeChangeOwnAvatarDo))
|
||||
routUser.Handle("/delete_own_avatar_do", ctrl.H(ctrl.ServeDeleteOwnAvatarDo))
|
||||
routUser.Handle("/link_lastfm_do", ctrl.H(ctrl.ServeLinkLastFMDo))
|
||||
routUser.Handle("/unlink_lastfm_do", ctrl.H(ctrl.ServeUnlinkLastFMDo))
|
||||
routUser.Handle("/link_listenbrainz_do", ctrl.H(ctrl.ServeLinkListenBrainzDo))
|
||||
@@ -177,6 +180,9 @@ func setupAdmin(r *mux.Router, ctrl *ctrladmin.Controller) {
|
||||
routAdmin.Handle("/change_username_do", ctrl.H(ctrl.ServeChangeUsernameDo))
|
||||
routAdmin.Handle("/change_password", ctrl.H(ctrl.ServeChangePassword))
|
||||
routAdmin.Handle("/change_password_do", ctrl.H(ctrl.ServeChangePasswordDo))
|
||||
routAdmin.Handle("/change_avatar", ctrl.H(ctrl.ServeChangeAvatar))
|
||||
routAdmin.Handle("/change_avatar_do", ctrl.H(ctrl.ServeChangeAvatarDo))
|
||||
routAdmin.Handle("/delete_avatar_do", ctrl.H(ctrl.ServeDeleteAvatarDo))
|
||||
routAdmin.Handle("/delete_user", ctrl.H(ctrl.ServeDeleteUser))
|
||||
routAdmin.Handle("/delete_user_do", ctrl.H(ctrl.ServeDeleteUserDo))
|
||||
routAdmin.Handle("/create_user", ctrl.H(ctrl.ServeCreateUser))
|
||||
@@ -235,6 +241,7 @@ func setupSubsonic(r *mux.Router, ctrl *ctrlsubsonic.Controller) {
|
||||
r.Handle("/getCoverArt{_:(?:\\.view)?}", ctrl.HR(ctrl.ServeGetCoverArt))
|
||||
r.Handle("/stream{_:(?:\\.view)?}", ctrl.HR(ctrl.ServeStream))
|
||||
r.Handle("/download{_:(?:\\.view)?}", ctrl.HR(ctrl.ServeStream))
|
||||
r.Handle("/getAvatar{_:(?:\\.view)?}", ctrl.HR(ctrl.ServeGetAvatar))
|
||||
|
||||
// browse by tag
|
||||
r.Handle("/getAlbum{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetAlbum))
|
||||
|
||||
Reference in New Issue
Block a user