ci / api / api (push) Successful in 1m11s
ci / db / db (push) Failing after 23s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m29s
ci / ts / ts (packages + web) (push) Successful in 3m15s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m27s
131 lines
3.7 KiB
Go
131 lines
3.7 KiB
Go
// Package social owns friendships, private conversations and the persistent inbox.
|
|
package social
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"pipu/api/internal/api/gen"
|
|
"pipu/api/internal/auth"
|
|
"pipu/api/internal/platform/apperr"
|
|
)
|
|
|
|
type ActivityPlaylistReader interface {
|
|
ReadSocialPlaylist(context.Context, uuid.UUID) (gen.Playlist, error)
|
|
}
|
|
|
|
type Handler struct {
|
|
store *Store
|
|
playlists ActivityPlaylistReader
|
|
}
|
|
|
|
func NewHandler(pool *pgxpool.Pool, playlists ActivityPlaylistReader) *Handler {
|
|
return &Handler{store: &Store{pool: pool}, playlists: playlists}
|
|
}
|
|
func userID(ctx context.Context) (uuid.UUID, error) {
|
|
p, ok := auth.PrincipalFrom(ctx)
|
|
if !ok {
|
|
return uuid.Nil, apperr.Unauthorized("auth.unauthorized", "sign in required")
|
|
}
|
|
return p.UserID, nil
|
|
}
|
|
func invalid() error {
|
|
return apperr.Validation("social", "social.invalid", "invalid social operation", nil)
|
|
}
|
|
func denied() error {
|
|
return apperr.Forbidden("social.unavailable", "friendship required or person unavailable")
|
|
}
|
|
|
|
func (h *Handler) GetSocial(ctx context.Context, _ gen.GetSocialRequestObject) (gen.GetSocialResponseObject, error) {
|
|
id, e := userID(ctx)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if e = h.store.markPresent(ctx, id); e != nil {
|
|
return nil, e
|
|
}
|
|
v, e := h.store.inbox(ctx, id)
|
|
return gen.GetSocial200JSONResponse(v), e
|
|
}
|
|
func (h *Handler) SearchPeople(ctx context.Context, r gen.SearchPeopleRequestObject) (gen.SearchPeopleResponseObject, error) {
|
|
id, e := userID(ctx)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
q := strings.TrimSpace(r.Params.Q)
|
|
if utf8.RuneCountInString(q) < 2 || utf8.RuneCountInString(q) > 80 {
|
|
return nil, invalid()
|
|
}
|
|
v, e := h.store.people(ctx, id, q)
|
|
return gen.SearchPeople200JSONResponse(v), e
|
|
}
|
|
func (h *Handler) SocialAction(ctx context.Context, r gen.SocialActionRequestObject) (gen.SocialActionResponseObject, error) {
|
|
id, e := userID(ctx)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if r.Body == nil || r.Body.TargetId == uuid.Nil || r.Body.TargetId == id {
|
|
return nil, invalid()
|
|
}
|
|
if e = h.store.action(ctx, id, *r.Body); e != nil {
|
|
return nil, e
|
|
}
|
|
return gen.SocialAction204Response{}, nil
|
|
}
|
|
func (h *Handler) GetDirectMessages(ctx context.Context, r gen.GetDirectMessagesRequestObject) (gen.GetDirectMessagesResponseObject, error) {
|
|
id, e := userID(ctx)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if r.PeerId == uuid.Nil || r.PeerId == id || (r.Params.Before != nil && *r.Params.Before < 1) {
|
|
return nil, invalid()
|
|
}
|
|
q := ""
|
|
if r.Params.Q != nil {
|
|
q = strings.TrimSpace(*r.Params.Q)
|
|
}
|
|
if utf8.RuneCountInString(q) > 100 {
|
|
return nil, invalid()
|
|
}
|
|
v, e := h.store.filteredMessages(ctx, id, r.PeerId, r.Params.Before, q, r.Params.Music != nil && *r.Params.Music)
|
|
return gen.GetDirectMessages200JSONResponse(v), e
|
|
}
|
|
func validateMessage(m gen.SendDirectMessage) error {
|
|
if m.ReplyTo != nil && *m.ReplyTo == uuid.Nil {
|
|
return invalid()
|
|
}
|
|
if m.Id == uuid.Nil || utf8.RuneCountInString(m.Text) > 4000 {
|
|
return invalid()
|
|
}
|
|
switch m.AttachmentKind {
|
|
case "none":
|
|
if strings.TrimSpace(m.Text) == "" || m.AttachmentId != nil {
|
|
return invalid()
|
|
}
|
|
case "track", "playlist":
|
|
if m.AttachmentId == nil || *m.AttachmentId == uuid.Nil {
|
|
return invalid()
|
|
}
|
|
default:
|
|
return invalid()
|
|
}
|
|
return nil
|
|
}
|
|
func (h *Handler) SendDirectMessage(ctx context.Context, r gen.SendDirectMessageRequestObject) (gen.SendDirectMessageResponseObject, error) {
|
|
id, e := userID(ctx)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if r.Body == nil || r.PeerId == id || r.PeerId == uuid.Nil {
|
|
return nil, invalid()
|
|
}
|
|
if e = validateMessage(*r.Body); e != nil {
|
|
return nil, e
|
|
}
|
|
v, e := h.store.send(ctx, id, r.PeerId, *r.Body)
|
|
return gen.SendDirectMessage200JSONResponse(v), e
|
|
}
|