forked from mirrors/pronouns.cc
feat(backend): add /api/v2/users/@me/settings
This commit is contained in:
parent
0c2eeaf954
commit
d97b3f8da1
9 changed files with 136 additions and 1 deletions
|
@ -54,6 +54,7 @@ type User struct {
|
||||||
ListPrivate bool
|
ListPrivate bool
|
||||||
LastSIDReroll time.Time `db:"last_sid_reroll"`
|
LastSIDReroll time.Time `db:"last_sid_reroll"`
|
||||||
Timezone *string
|
Timezone *string
|
||||||
|
Settings UserSettings
|
||||||
|
|
||||||
DeletedAt *time.Time
|
DeletedAt *time.Time
|
||||||
SelfDelete *bool
|
SelfDelete *bool
|
||||||
|
|
26
backend/db/user_settings.go
Normal file
26
backend/db/user_settings.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"emperror.dev/errors"
|
||||||
|
"github.com/rs/xid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSettings struct {
|
||||||
|
ReadChangelog string `json:"read_changelog"`
|
||||||
|
ReadSettingsNotice string `json:"read_settings_notice"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) UpdateUserSettings(ctx context.Context, id xid.ID, us UserSettings) error {
|
||||||
|
sql, args, err := sq.Update("users").Set("settings", us).Where("id = ?", id).ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "building sql")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec(ctx, sql, args...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "executing query")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"codeberg.org/pronounscc/pronouns.cc/backend/routes/v1/meta"
|
"codeberg.org/pronounscc/pronouns.cc/backend/routes/v1/meta"
|
||||||
"codeberg.org/pronounscc/pronouns.cc/backend/routes/v1/mod"
|
"codeberg.org/pronounscc/pronouns.cc/backend/routes/v1/mod"
|
||||||
"codeberg.org/pronounscc/pronouns.cc/backend/routes/v1/user"
|
"codeberg.org/pronounscc/pronouns.cc/backend/routes/v1/user"
|
||||||
|
user2 "codeberg.org/pronounscc/pronouns.cc/backend/routes/v2/user"
|
||||||
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
|
@ -22,7 +23,6 @@ var openapi string
|
||||||
// mountRoutes mounts all API routes on the server's router.
|
// mountRoutes mounts all API routes on the server's router.
|
||||||
// they are all mounted under /v1/
|
// they are all mounted under /v1/
|
||||||
func mountRoutes(s *server.Server) {
|
func mountRoutes(s *server.Server) {
|
||||||
// future-proofing for API versions
|
|
||||||
s.Router.Route("/v1", func(r chi.Router) {
|
s.Router.Route("/v1", func(r chi.Router) {
|
||||||
auth.Mount(s, r)
|
auth.Mount(s, r)
|
||||||
user.Mount(s, r)
|
user.Mount(s, r)
|
||||||
|
@ -32,6 +32,10 @@ func mountRoutes(s *server.Server) {
|
||||||
mod.Mount(s, r)
|
mod.Mount(s, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
s.Router.Route("/v2", func(r chi.Router) {
|
||||||
|
user2.Mount(s, r)
|
||||||
|
})
|
||||||
|
|
||||||
// API docs
|
// API docs
|
||||||
s.Router.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
s.Router.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
render.HTML(w, r, openapi)
|
render.HTML(w, r, openapi)
|
||||||
|
|
21
backend/routes/v2/user/get_settings.go
Normal file
21
backend/routes/v2/user/get_settings.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"codeberg.org/pronounscc/pronouns.cc/backend/log"
|
||||||
|
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) GetSettings(w http.ResponseWriter, r *http.Request) (err error) {
|
||||||
|
claims, _ := server.ClaimsFromContext(r.Context())
|
||||||
|
u, err := s.DB.User(r.Context(), claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("getting user: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, r, u.Settings)
|
||||||
|
return nil
|
||||||
|
}
|
45
backend/routes/v2/user/patch_settings.go
Normal file
45
backend/routes/v2/user/patch_settings.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
||||||
|
"emperror.dev/errors"
|
||||||
|
"github.com/aarondl/opt/omitnull"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PatchSettingsRequest struct {
|
||||||
|
ReadChangelog omitnull.Val[string] `json:"read_changelog"`
|
||||||
|
ReadSettingsNotice omitnull.Val[string] `json:"read_settings_notice"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) PatchSettings(w http.ResponseWriter, r *http.Request) (err error) {
|
||||||
|
ctx := r.Context()
|
||||||
|
claims, _ := server.ClaimsFromContext(ctx)
|
||||||
|
u, err := s.DB.User(ctx, claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "getting user")
|
||||||
|
}
|
||||||
|
|
||||||
|
var req PatchSettingsRequest
|
||||||
|
err = render.Decode(r, &req)
|
||||||
|
if err != nil {
|
||||||
|
return server.APIError{Code: server.ErrBadRequest}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !req.ReadChangelog.IsUnset() {
|
||||||
|
u.Settings.ReadChangelog = req.ReadChangelog.GetOrZero()
|
||||||
|
}
|
||||||
|
if !req.ReadSettingsNotice.IsUnset() {
|
||||||
|
u.Settings.ReadSettingsNotice = req.ReadSettingsNotice.GetOrZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.DB.UpdateUserSettings(ctx, u.ID, u.Settings)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "updating user settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, r, u.Settings)
|
||||||
|
return nil
|
||||||
|
}
|
23
backend/routes/v2/user/routes.go
Normal file
23
backend/routes/v2/user/routes.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
*server.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mount(srv *server.Server, r chi.Router) {
|
||||||
|
s := &Server{
|
||||||
|
Server: srv,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Route("/users", func(r chi.Router) {
|
||||||
|
r.With(server.MustAuth).Group(func(r chi.Router) {
|
||||||
|
r.Get("/@me/settings", server.WrapHandler(s.GetSettings))
|
||||||
|
r.Patch("/@me/settings", server.WrapHandler(s.PatchSettings))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
2
go.mod
2
go.mod
|
@ -5,6 +5,7 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
emperror.dev/errors v0.8.1
|
emperror.dev/errors v0.8.1
|
||||||
github.com/Masterminds/squirrel v1.5.4
|
github.com/Masterminds/squirrel v1.5.4
|
||||||
|
github.com/aarondl/opt v0.0.0-20230313190023-85d93d668fec
|
||||||
github.com/bwmarrin/discordgo v0.27.1
|
github.com/bwmarrin/discordgo v0.27.1
|
||||||
github.com/davidbyttow/govips/v2 v2.13.0
|
github.com/davidbyttow/govips/v2 v2.13.0
|
||||||
github.com/georgysavva/scany/v2 v2.0.0
|
github.com/georgysavva/scany/v2 v2.0.0
|
||||||
|
@ -32,6 +33,7 @@ require (
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.19.0 // indirect
|
cloud.google.com/go/compute v1.19.0 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
|
github.com/aarondl/json v0.0.0-20221020222930-8b0db17ef1bf // indirect
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -54,6 +54,10 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8
|
||||||
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA=
|
github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA=
|
||||||
|
github.com/aarondl/json v0.0.0-20221020222930-8b0db17ef1bf h1:+edM69bH/X6JpYPmJYBRLanAMe1V5yRXYU3hHUovGcE=
|
||||||
|
github.com/aarondl/json v0.0.0-20221020222930-8b0db17ef1bf/go.mod h1:FZqLhJSj2tg0ZN48GB1zvj00+ZYcHPqgsC7yzcgCq6k=
|
||||||
|
github.com/aarondl/opt v0.0.0-20230313190023-85d93d668fec h1:2NFk5fe52cHyRcUnXSs4CSEAqm+rL/hr3AdflBE3VPU=
|
||||||
|
github.com/aarondl/opt v0.0.0-20230313190023-85d93d668fec/go.mod h1:l4/5NZtYd/SIohsFhaJQQe+sPOTG22furpZ5FvcYOzk=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
|
9
scripts/migrate/020_settings.sql
Normal file
9
scripts/migrate/020_settings.sql
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
-- 2023-08-16: Server-side user settings storage (for now, just whether specific notices are dismissed)
|
||||||
|
|
||||||
|
-- +migrate Up
|
||||||
|
|
||||||
|
alter table users add column settings jsonb not null default '{}';
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
|
||||||
|
alter table users drop column settings;
|
Loading…
Reference in a new issue