feat: add short IDs + link shortener
This commit is contained in:
parent
7c94c088e0
commit
10dc59d3d4
18 changed files with 510 additions and 31 deletions
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
type GetMemberResponse struct {
|
||||
ID xid.ID `json:"id"`
|
||||
SID string `json:"sid"`
|
||||
Name string `json:"name"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
Bio *string `json:"bio"`
|
||||
|
@ -33,6 +34,7 @@ type GetMemberResponse struct {
|
|||
func dbMemberToMember(u db.User, m db.Member, fields []db.Field, flags []db.MemberFlag, isOwnMember bool) GetMemberResponse {
|
||||
r := GetMemberResponse{
|
||||
ID: m.ID,
|
||||
SID: m.SID,
|
||||
Name: m.Name,
|
||||
DisplayName: m.DisplayName,
|
||||
Bio: m.Bio,
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
type memberListResponse struct {
|
||||
ID xid.ID `json:"id"`
|
||||
SID string `json:"sid"`
|
||||
Name string `json:"name"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
Bio *string `json:"bio"`
|
||||
|
@ -27,6 +28,7 @@ func membersToMemberList(ms []db.Member, isSelf bool) []memberListResponse {
|
|||
for i := range ms {
|
||||
resps[i] = memberListResponse{
|
||||
ID: ms[i].ID,
|
||||
SID: ms[i].SID,
|
||||
Name: ms[i].Name,
|
||||
DisplayName: ms[i].DisplayName,
|
||||
Bio: ms[i].Bio,
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"codeberg.org/u1f320/pronouns.cc/backend/common"
|
||||
"codeberg.org/u1f320/pronouns.cc/backend/db"
|
||||
|
@ -319,3 +320,49 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
|
|||
render.JSON(w, r, dbMemberToMember(u, m, fields, flags, true))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) rerollMemberSID(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := r.Context()
|
||||
|
||||
claims, _ := server.ClaimsFromContext(ctx)
|
||||
|
||||
if !claims.TokenWrite {
|
||||
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)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting user")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
if m.UserID != claims.UserID {
|
||||
return server.APIError{Code: server.ErrNotOwnMember}
|
||||
}
|
||||
|
||||
if time.Now().Add(-time.Hour).Before(u.LastSIDReroll) {
|
||||
return server.APIError{Code: server.ErrRerollingTooQuickly}
|
||||
}
|
||||
|
||||
newID, err := s.DB.RerollMemberSID(ctx, u.ID, m.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "updating member SID")
|
||||
}
|
||||
|
||||
m.SID = newID
|
||||
render.JSON(w, r, dbMemberToMember(u, m, nil, nil, true))
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,5 +29,8 @@ func Mount(srv *server.Server, r chi.Router) {
|
|||
r.With(server.MustAuth).Post("/", server.WrapHandler(s.createMember))
|
||||
r.With(server.MustAuth).Patch("/{memberRef}", server.WrapHandler(s.patchMember))
|
||||
r.With(server.MustAuth).Delete("/{memberRef}", server.WrapHandler(s.deleteMember))
|
||||
|
||||
// reroll member SID
|
||||
r.With(server.MustAuth).Get("/{memberRef}/reroll", server.WrapHandler(s.rerollMemberSID))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
type GetUserResponse struct {
|
||||
ID xid.ID `json:"id"`
|
||||
SID string `json:"sid"`
|
||||
Username string `json:"name"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
Bio *string `json:"bio"`
|
||||
|
@ -33,9 +34,10 @@ type GetMeResponse struct {
|
|||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
MaxInvites int `json:"max_invites"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
ListPrivate bool `json:"list_private"`
|
||||
MaxInvites int `json:"max_invites"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
ListPrivate bool `json:"list_private"`
|
||||
LastSIDReroll time.Time `json:"last_sid_reroll"`
|
||||
|
||||
Discord *string `json:"discord"`
|
||||
DiscordUsername *string `json:"discord_username"`
|
||||
|
@ -53,6 +55,7 @@ type GetMeResponse struct {
|
|||
|
||||
type PartialMember struct {
|
||||
ID xid.ID `json:"id"`
|
||||
SID string `json:"sid"`
|
||||
Name string `json:"name"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
Bio *string `json:"bio"`
|
||||
|
@ -65,6 +68,7 @@ type PartialMember struct {
|
|||
func dbUserToResponse(u db.User, fields []db.Field, members []db.Member, flags []db.UserFlag) GetUserResponse {
|
||||
resp := GetUserResponse{
|
||||
ID: u.ID,
|
||||
SID: u.SID,
|
||||
Username: u.Username,
|
||||
DisplayName: u.DisplayName,
|
||||
Bio: u.Bio,
|
||||
|
@ -82,6 +86,7 @@ func dbUserToResponse(u db.User, fields []db.Field, members []db.Member, flags [
|
|||
for i := range members {
|
||||
resp.Members[i] = PartialMember{
|
||||
ID: members[i].ID,
|
||||
SID: members[i].SID,
|
||||
Name: members[i].Name,
|
||||
DisplayName: members[i].DisplayName,
|
||||
Bio: members[i].Bio,
|
||||
|
@ -188,6 +193,7 @@ func (s *Server) getMeUser(w http.ResponseWriter, r *http.Request) error {
|
|||
MaxInvites: u.MaxInvites,
|
||||
IsAdmin: u.IsAdmin,
|
||||
ListPrivate: u.ListPrivate,
|
||||
LastSIDReroll: u.LastSIDReroll,
|
||||
Discord: u.Discord,
|
||||
DiscordUsername: u.DiscordUsername,
|
||||
Tumblr: u.Tumblr,
|
||||
|
|
|
@ -3,6 +3,7 @@ package user
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"codeberg.org/u1f320/pronouns.cc/backend/common"
|
||||
"codeberg.org/u1f320/pronouns.cc/backend/db"
|
||||
|
@ -313,6 +314,7 @@ func (s *Server) patchUser(w http.ResponseWriter, r *http.Request) error {
|
|||
MaxInvites: u.MaxInvites,
|
||||
IsAdmin: u.IsAdmin,
|
||||
ListPrivate: u.ListPrivate,
|
||||
LastSIDReroll: u.LastSIDReroll,
|
||||
Discord: u.Discord,
|
||||
DiscordUsername: u.DiscordUsername,
|
||||
Tumblr: u.Tumblr,
|
||||
|
@ -362,3 +364,31 @@ func validateSlicePtr[T validator](typ string, slice *[]T, custom db.CustomPrefe
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) rerollUserSID(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := r.Context()
|
||||
|
||||
claims, _ := server.ClaimsFromContext(ctx)
|
||||
|
||||
if !claims.TokenWrite {
|
||||
return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"}
|
||||
}
|
||||
|
||||
u, err := s.DB.User(ctx, claims.UserID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting existing user")
|
||||
}
|
||||
|
||||
if time.Now().Add(-time.Hour).Before(u.LastSIDReroll) {
|
||||
return server.APIError{Code: server.ErrRerollingTooQuickly}
|
||||
}
|
||||
|
||||
newID, err := s.DB.RerollUserSID(ctx, u.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "updating user SID")
|
||||
}
|
||||
|
||||
u.SID = newID
|
||||
render.JSON(w, r, dbUserToResponse(u, nil, nil, nil))
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ func Mount(srv *server.Server, r chi.Router) {
|
|||
r.Post("/@me/flags", server.WrapHandler(s.postUserFlag))
|
||||
r.Patch("/@me/flags/{flagID}", server.WrapHandler(s.patchUserFlag))
|
||||
r.Delete("/@me/flags/{flagID}", server.WrapHandler(s.deleteUserFlag))
|
||||
|
||||
r.Get("/@me/reroll", server.WrapHandler(s.rerollUserSID))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue