forked from mirrors/pronouns.cc
make more member routes accept snowflakes + make flag routes accept snowflakes
This commit is contained in:
parent
0171f54592
commit
1b9a5deb78
9 changed files with 2605 additions and 56 deletions
43
.air.toml
Normal file
43
.air.toml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
root = "."
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = ["web"]
|
||||||
|
bin = "./tmp/main"
|
||||||
|
cmd = "go build -o ./tmp/main ."
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["docs", "frontend", "prns", "pronounslib", "tmp", "target"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,3 +12,4 @@ package
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
target
|
target
|
||||||
|
tmp
|
||||||
|
|
|
@ -62,9 +62,8 @@ func run(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
case err := <-e:
|
case err := <-e:
|
||||||
log.Fatalf("Error running server: %v", err)
|
log.Fatalf("Error running server: %v", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MaxContentLength = 2 * 1024 * 1024
|
const MaxContentLength = 2 * 1024 * 1024
|
||||||
|
|
|
@ -8,12 +8,13 @@ import (
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
|
|
||||||
|
"codeberg.org/pronounscc/pronouns.cc/backend/common"
|
||||||
"codeberg.org/pronounscc/pronouns.cc/backend/db"
|
"codeberg.org/pronounscc/pronouns.cc/backend/db"
|
||||||
"codeberg.org/pronounscc/pronouns.cc/backend/log"
|
"codeberg.org/pronounscc/pronouns.cc/backend/log"
|
||||||
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
"codeberg.org/pronounscc/pronouns.cc/backend/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) deleteMember(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) deleteMember(w http.ResponseWriter, r *http.Request) (err error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
claims, _ := server.ClaimsFromContext(ctx)
|
claims, _ := server.ClaimsFromContext(ctx)
|
||||||
|
@ -22,18 +23,27 @@ func (s *Server) deleteMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
return server.APIError{Code: server.ErrMissingPermissions, Details: "this token is read-only"}
|
return server.APIError{Code: server.ErrMissingPermissions, Details: "this token is read-only"}
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := xid.FromString(chi.URLParam(r, "memberRef"))
|
var m db.Member
|
||||||
if err != nil {
|
if id, err := xid.FromString(chi.URLParam(r, "memberRef")); err == nil {
|
||||||
return server.APIError{Code: server.ErrMemberNotFound}
|
m, err = s.DB.Member(ctx, id)
|
||||||
}
|
if err != nil {
|
||||||
|
if err == db.ErrMemberNotFound {
|
||||||
|
return server.APIError{Code: server.ErrMemberNotFound}
|
||||||
|
}
|
||||||
|
|
||||||
m, err := s.DB.Member(ctx, id)
|
return errors.Wrap(err, "getting member")
|
||||||
if err != nil {
|
|
||||||
if err == db.ErrMemberNotFound {
|
|
||||||
return server.APIError{Code: server.ErrMemberNotFound}
|
|
||||||
}
|
}
|
||||||
|
} else if id, err := common.ParseSnowflake(chi.URLParam(r, "memberRef")); err == nil {
|
||||||
|
m, err = s.DB.MemberBySnowflake(ctx, common.MemberID(id))
|
||||||
|
if err != nil {
|
||||||
|
if err == db.ErrMemberNotFound {
|
||||||
|
return server.APIError{Code: server.ErrMemberNotFound}
|
||||||
|
}
|
||||||
|
|
||||||
return errors.Wrap(err, "getting member")
|
return errors.Wrap(err, "getting member")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return server.APIError{Code: server.ErrMemberNotFound}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.UserID != claims.UserID {
|
if m.UserID != claims.UserID {
|
||||||
|
|
|
@ -38,23 +38,41 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"}
|
return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"}
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := xid.FromString(chi.URLParam(r, "memberRef"))
|
|
||||||
if err != nil {
|
|
||||||
return server.APIError{Code: server.ErrMemberNotFound}
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := s.DB.User(ctx, claims.UserID)
|
u, err := s.DB.User(ctx, claims.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "getting user")
|
return errors.Wrap(err, "getting user")
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := s.DB.Member(ctx, id)
|
var m db.Member
|
||||||
if err != nil {
|
if id, err := xid.FromString(chi.URLParam(r, "memberRef")); err == nil {
|
||||||
if err == db.ErrMemberNotFound {
|
log.Debugf("%v/%v is xid", chi.URLParam(r, "memberRef"), id)
|
||||||
|
|
||||||
|
m, err = s.DB.Member(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
if err == db.ErrMemberNotFound {
|
||||||
|
return server.APIError{Code: server.ErrMemberNotFound}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Wrap(err, "getting member")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
id, err := common.ParseSnowflake(chi.URLParam(r, "memberRef"))
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("%v/%v is not valid snowflake", chi.URLParam(r, "memberRef"), id)
|
||||||
|
|
||||||
return server.APIError{Code: server.ErrMemberNotFound}
|
return server.APIError{Code: server.ErrMemberNotFound}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(err, "getting member")
|
log.Debugf("%v/%v is valid snowflake", chi.URLParam(r, "memberRef"), id)
|
||||||
|
|
||||||
|
m, err = s.DB.MemberBySnowflake(ctx, common.MemberID(id))
|
||||||
|
if err != nil {
|
||||||
|
if err == db.ErrMemberNotFound {
|
||||||
|
return server.APIError{Code: server.ErrMemberNotFound}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Wrap(err, "getting member")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.UserID != claims.UserID {
|
if m.UserID != claims.UserID {
|
||||||
|
@ -234,7 +252,7 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
defer tx.Rollback(ctx)
|
defer tx.Rollback(ctx)
|
||||||
|
|
||||||
m, err = s.DB.UpdateMember(ctx, tx, id, req.Name, req.DisplayName, req.Bio, req.Unlisted, req.Links, avatarHash)
|
m, err = s.DB.UpdateMember(ctx, tx, m.ID, req.Name, req.DisplayName, req.Bio, req.Unlisted, req.Links, avatarHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch errors.Cause(err) {
|
switch errors.Cause(err) {
|
||||||
case db.ErrNothingToUpdate:
|
case db.ErrNothingToUpdate:
|
||||||
|
@ -258,9 +276,9 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
pronouns = *req.Pronouns
|
pronouns = *req.Pronouns
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.DB.SetMemberNamesPronouns(ctx, tx, id, names, pronouns)
|
err = s.DB.SetMemberNamesPronouns(ctx, tx, m.ID, names, pronouns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("setting names for member %v: %v", id, err)
|
log.Errorf("setting names for member %v: %v", m.ID, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.Names = names
|
m.Names = names
|
||||||
|
@ -269,16 +287,16 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
||||||
var fields []db.Field
|
var fields []db.Field
|
||||||
if req.Fields != nil {
|
if req.Fields != nil {
|
||||||
err = s.DB.SetMemberFields(ctx, tx, id, *req.Fields)
|
err = s.DB.SetMemberFields(ctx, tx, m.ID, *req.Fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("setting fields for member %v: %v", id, err)
|
log.Errorf("setting fields for member %v: %v", m.ID, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fields = *req.Fields
|
fields = *req.Fields
|
||||||
} else {
|
} else {
|
||||||
fields, err = s.DB.MemberFields(ctx, id)
|
fields, err = s.DB.MemberFields(ctx, m.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("getting fields for member %v: %v", id, err)
|
log.Errorf("getting fields for member %v: %v", m.ID, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -121,6 +122,24 @@ type patchUserFlagRequest struct {
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) parseFlag(ctx context.Context, flags []db.PrideFlag, flagRef string) (db.PrideFlag, bool) {
|
||||||
|
if id, err := xid.FromString(flagRef); err == nil {
|
||||||
|
for _, f := range flags {
|
||||||
|
if f.ID == id {
|
||||||
|
return f, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if id, err := common.ParseSnowflake(flagRef); err == nil {
|
||||||
|
for _, f := range flags {
|
||||||
|
if f.SnowflakeID == common.FlagID(id) {
|
||||||
|
return f, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return db.PrideFlag{}, false
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
claims, _ := server.ClaimsFromContext(ctx)
|
claims, _ := server.ClaimsFromContext(ctx)
|
||||||
|
@ -129,28 +148,13 @@ func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error {
|
||||||
return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"}
|
return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"}
|
||||||
}
|
}
|
||||||
|
|
||||||
flagID, err := xid.FromString(chi.URLParam(r, "flagID"))
|
|
||||||
if err != nil {
|
|
||||||
return server.APIError{Code: server.ErrNotFound, Details: "Invalid flag ID"}
|
|
||||||
}
|
|
||||||
|
|
||||||
flags, err := s.DB.AccountFlags(ctx, claims.UserID)
|
flags, err := s.DB.AccountFlags(ctx, claims.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "getting current user flags")
|
return errors.Wrap(err, "getting current user flags")
|
||||||
}
|
}
|
||||||
if len(flags) >= db.MaxPrideFlags {
|
|
||||||
return server.APIError{
|
flag, ok := s.parseFlag(ctx, flags, chi.URLParam(r, "flagID"))
|
||||||
Code: server.ErrFlagLimitReached,
|
if !ok {
|
||||||
}
|
|
||||||
}
|
|
||||||
var found bool
|
|
||||||
for _, flag := range flags {
|
|
||||||
if flag.ID == flagID {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return server.APIError{Code: server.ErrNotFound, Details: "No flag with that ID found"}
|
return server.APIError{Code: server.ErrNotFound, Details: "No flag with that ID found"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +194,7 @@ func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error {
|
||||||
}
|
}
|
||||||
defer tx.Rollback(ctx)
|
defer tx.Rollback(ctx)
|
||||||
|
|
||||||
flag, err := s.DB.EditFlag(ctx, tx, flagID, req.Name, req.Description, nil)
|
flag, err = s.DB.EditFlag(ctx, tx, flag.ID, req.Name, req.Description, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "updating flag")
|
return errors.Wrap(err, "updating flag")
|
||||||
}
|
}
|
||||||
|
@ -212,19 +216,16 @@ func (s *Server) deleteUserFlag(w http.ResponseWriter, r *http.Request) error {
|
||||||
return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"}
|
return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"}
|
||||||
}
|
}
|
||||||
|
|
||||||
flagID, err := xid.FromString(chi.URLParam(r, "flagID"))
|
flags, err := s.DB.AccountFlags(ctx, claims.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return server.APIError{Code: server.ErrNotFound, Details: "Invalid flag ID"}
|
return errors.Wrap(err, "getting current user flags")
|
||||||
}
|
}
|
||||||
|
|
||||||
flag, err := s.DB.UserFlag(ctx, flagID)
|
flag, ok := s.parseFlag(ctx, flags, chi.URLParam(r, "flagID"))
|
||||||
if err != nil {
|
if !ok {
|
||||||
if err == db.ErrFlagNotFound {
|
return server.APIError{Code: server.ErrNotFound, Details: "No flag with that ID found"}
|
||||||
return server.APIError{Code: server.ErrNotFound, Details: "Flag not found"}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Wrap(err, "getting flag object")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if flag.UserID != claims.UserID {
|
if flag.UserID != claims.UserID {
|
||||||
return server.APIError{Code: server.ErrNotFound, Details: "Flag not found"}
|
return server.APIError{Code: server.ErrNotFound, Details: "Flag not found"}
|
||||||
}
|
}
|
||||||
|
|
12
package.json
Normal file
12
package.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "pronouns",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "concurrently \"air\" \"make dev\""
|
||||||
|
},
|
||||||
|
"author": "sam <sam@sleepycat.moe>",
|
||||||
|
"license": "AGPL-3.0-only",
|
||||||
|
"devDependencies": {
|
||||||
|
"concurrently": "^8.2.1"
|
||||||
|
}
|
||||||
|
}
|
2462
pnpm-lock.yaml
generated
Normal file
2462
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
3
pnpm-workspace.yaml
Normal file
3
pnpm-workspace.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
packages:
|
||||||
|
- docs
|
||||||
|
- frontend
|
Loading…
Reference in a new issue