mirror of
https://github.com/Unkn0wnCat/matrix-veles.git
synced 2025-04-28 17:56:49 +02:00
1371 lines
31 KiB
Go
1371 lines
31 KiB
Go
package graph
|
|
|
|
// This file will be automatically regenerated based on the schema, any resolver implementations
|
|
// will be copied through when generating and any unknown code will be moved to the end.
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"math/big"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Unkn0wnCat/matrix-veles/graph/generated"
|
|
"github.com/Unkn0wnCat/matrix-veles/graph/model"
|
|
"github.com/Unkn0wnCat/matrix-veles/internal/config"
|
|
"github.com/Unkn0wnCat/matrix-veles/internal/db"
|
|
model2 "github.com/Unkn0wnCat/matrix-veles/internal/db/model"
|
|
jwt "github.com/golang-jwt/jwt/v4"
|
|
"github.com/spf13/viper"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
// Author is the resolver for the author field.
|
|
func (r *commentResolver) Author(ctx context.Context, obj *model.Comment) (*model.User, error) {
|
|
user, err := db.GetUserByID(*obj.AuthorID)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeUser(user), nil
|
|
}
|
|
|
|
// PartOf is the resolver for the partOf field.
|
|
func (r *entryResolver) PartOf(ctx context.Context, obj *model.Entry, first *int, after *string) (*model.ListConnection, error) {
|
|
ids := obj.PartOfIDs
|
|
|
|
if len(ids) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
startIndex := 0
|
|
|
|
if after != nil {
|
|
afterInt := new(big.Int)
|
|
afterInt.SetString(*after, 16)
|
|
|
|
idInt := new(big.Int)
|
|
|
|
set := false
|
|
|
|
for i, id := range obj.PartOfIDs {
|
|
idInt.SetString(id.Hex(), 16)
|
|
|
|
if idInt.Cmp(afterInt) > 0 {
|
|
startIndex = i
|
|
set = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !set {
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
if startIndex >= len(ids) {
|
|
return nil, nil
|
|
}
|
|
|
|
ids = ids[startIndex:]
|
|
|
|
length := 25
|
|
|
|
if first != nil {
|
|
length = *first
|
|
}
|
|
|
|
cut := false
|
|
|
|
if len(ids) > length {
|
|
cut = true
|
|
ids = ids[:length]
|
|
}
|
|
|
|
var edges []*model.ListEdge
|
|
|
|
for _, id := range ids {
|
|
dbList, err := db.GetListByID(*id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
edges = append(edges, &model.ListEdge{
|
|
Node: model.MakeList(dbList),
|
|
Cursor: dbList.ID.Hex(),
|
|
})
|
|
}
|
|
|
|
return &model.ListConnection{
|
|
PageInfo: &model.PageInfo{
|
|
HasPreviousPage: startIndex > 0,
|
|
HasNextPage: cut,
|
|
StartCursor: edges[0].Cursor,
|
|
EndCursor: edges[len(edges)-1].Cursor,
|
|
},
|
|
Edges: edges,
|
|
}, nil
|
|
}
|
|
|
|
// AddedBy is the resolver for the addedBy field.
|
|
func (r *entryResolver) AddedBy(ctx context.Context, obj *model.Entry) (*model.User, error) {
|
|
user, err := db.GetUserByID(obj.AddedByID)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeUser(user), nil
|
|
}
|
|
|
|
// Comments is the resolver for the comments field.
|
|
func (r *entryResolver) Comments(ctx context.Context, obj *model.Entry, first *int, after *string) (*model.CommentConnection, error) {
|
|
comments := obj.RawComments
|
|
|
|
return ResolveComments(comments, first, after)
|
|
}
|
|
|
|
// Creator is the resolver for the creator field.
|
|
func (r *listResolver) Creator(ctx context.Context, obj *model.List) (*model.User, error) {
|
|
user, err := db.GetUserByID(obj.CreatorID)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeUser(user), nil
|
|
}
|
|
|
|
// Comments is the resolver for the comments field.
|
|
func (r *listResolver) Comments(ctx context.Context, obj *model.List, first *int, after *string) (*model.CommentConnection, error) {
|
|
comments := obj.RawComments
|
|
|
|
return ResolveComments(comments, first, after)
|
|
}
|
|
|
|
// Maintainers is the resolver for the maintainers field.
|
|
func (r *listResolver) Maintainers(ctx context.Context, obj *model.List, first *int, after *string) (*model.UserConnection, error) {
|
|
ids := obj.MaintainerIDs
|
|
|
|
if len(ids) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
startIndex := 0
|
|
|
|
if after != nil {
|
|
afterInt := new(big.Int)
|
|
afterInt.SetString(*after, 16)
|
|
|
|
idInt := new(big.Int)
|
|
|
|
set := false
|
|
|
|
for i, id := range ids {
|
|
idInt.SetString(id.Hex(), 16)
|
|
|
|
if idInt.Cmp(afterInt) > 0 {
|
|
startIndex = i
|
|
set = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !set {
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
if startIndex >= len(ids) {
|
|
return nil, nil
|
|
}
|
|
|
|
ids = ids[startIndex:]
|
|
|
|
length := 25
|
|
|
|
if first != nil {
|
|
length = *first
|
|
}
|
|
|
|
cut := false
|
|
|
|
if len(ids) > length {
|
|
cut = true
|
|
ids = ids[:length]
|
|
}
|
|
|
|
var edges []*model.UserEdge
|
|
|
|
for _, id := range ids {
|
|
dbUser, err := db.GetUserByID(*id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
edges = append(edges, &model.UserEdge{
|
|
Node: model.MakeUser(dbUser),
|
|
Cursor: dbUser.ID.Hex(),
|
|
})
|
|
}
|
|
|
|
return &model.UserConnection{
|
|
PageInfo: &model.PageInfo{
|
|
HasPreviousPage: startIndex > 0,
|
|
HasNextPage: cut,
|
|
StartCursor: edges[0].Cursor,
|
|
EndCursor: edges[len(edges)-1].Cursor,
|
|
},
|
|
Edges: edges,
|
|
}, nil
|
|
}
|
|
|
|
// Entries is the resolver for the entries field.
|
|
func (r *listResolver) Entries(ctx context.Context, obj *model.List, first *int, after *string) (*model.EntryConnection, error) {
|
|
coll := db.Db.Collection(viper.GetString("bot.mongo.collection.entries"))
|
|
|
|
dbFilter, _, dbLimit, err := buildDBEntryFilter(first, after, &model.EntryFilter{
|
|
PartOf: &model.IDArrayFilter{
|
|
ContainsAll: []*string{&obj.ID},
|
|
},
|
|
}, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newLimit := *dbLimit + 1
|
|
|
|
findOpts := options.FindOptions{
|
|
Limit: &newLimit,
|
|
}
|
|
|
|
res, err := coll.Find(ctx, *dbFilter, &findOpts)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
var rawEntries []model2.DBEntry
|
|
|
|
err = res.All(ctx, &rawEntries)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if len(rawEntries) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
lastEntryI := len(rawEntries) - 1
|
|
if lastEntryI > 0 {
|
|
lastEntryI--
|
|
}
|
|
|
|
firstEntry := rawEntries[0]
|
|
lastEntry := rawEntries[lastEntryI]
|
|
|
|
isAfter := false
|
|
|
|
if after != nil {
|
|
isAfter = true
|
|
}
|
|
|
|
hasMore := false
|
|
if int64(len(rawEntries)) > *dbLimit {
|
|
hasMore = true
|
|
}
|
|
|
|
var edges []*model.EntryEdge
|
|
|
|
for i, rawEntry := range rawEntries {
|
|
if int64(i) == *dbLimit {
|
|
continue
|
|
}
|
|
|
|
edges = append(edges, &model.EntryEdge{
|
|
Node: model.MakeEntry(&rawEntry),
|
|
Cursor: rawEntry.ID.Hex(),
|
|
})
|
|
}
|
|
|
|
return &model.EntryConnection{
|
|
PageInfo: &model.PageInfo{
|
|
HasPreviousPage: isAfter,
|
|
HasNextPage: hasMore,
|
|
StartCursor: firstEntry.ID.Hex(),
|
|
EndCursor: lastEntry.ID.Hex(),
|
|
},
|
|
Edges: edges,
|
|
}, nil
|
|
}
|
|
|
|
// Login is the resolver for the login field.
|
|
func (r *mutationResolver) Login(ctx context.Context, input model.Login) (string, error) {
|
|
user, err := db.GetUserByUsername(input.Username)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return "", errors.New("invalid credentials")
|
|
}
|
|
|
|
return "", errors.New("database error")
|
|
}
|
|
|
|
err = user.CheckPassword(input.Password)
|
|
if err != nil {
|
|
return "", errors.New("invalid credentials")
|
|
}
|
|
|
|
jwtSigningKey := []byte(viper.GetString("bot.web.secret"))
|
|
|
|
claims := model2.JwtClaims{
|
|
Username: user.Username,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 365 * 100)),
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
|
Issuer: "veles-api",
|
|
Subject: user.ID.Hex(),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
ss, err := token.SignedString(jwtSigningKey)
|
|
if err != nil {
|
|
return "", errors.New("unable to create token")
|
|
}
|
|
|
|
return ss, nil
|
|
}
|
|
|
|
// Register is the resolver for the register field.
|
|
func (r *mutationResolver) Register(ctx context.Context, input model.Register) (string, error) {
|
|
_, err := db.GetUserByUsername(input.Username)
|
|
if !errors.Is(err, mongo.ErrNoDocuments) {
|
|
return "", errors.New("username taken")
|
|
}
|
|
|
|
user := model2.DBUser{
|
|
ID: primitive.NewObjectID(),
|
|
Username: input.Username,
|
|
PendingMatrixLinks: []*string{&input.MxID},
|
|
Password: &input.Password,
|
|
}
|
|
|
|
err = user.HashPassword()
|
|
if err != nil {
|
|
return "", errors.New("server error")
|
|
}
|
|
|
|
err = db.SaveUser(&user)
|
|
if err != nil {
|
|
return "", errors.New("database error")
|
|
}
|
|
|
|
jwtSigningKey := []byte(viper.GetString("bot.web.secret"))
|
|
|
|
claims := model2.JwtClaims{
|
|
Username: user.Username,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 365 * 100)),
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
|
Issuer: "veles-api",
|
|
Subject: user.ID.Hex(),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
ss, err := token.SignedString(jwtSigningKey)
|
|
if err != nil {
|
|
return "", errors.New("unable to create token")
|
|
}
|
|
|
|
return ss, nil
|
|
}
|
|
|
|
// AddMxid is the resolver for the addMXID field.
|
|
func (r *mutationResolver) AddMxid(ctx context.Context, input model.AddMxid) (*model.User, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, mxid := range append(user.MatrixLinks, user.PendingMatrixLinks...) {
|
|
if strings.EqualFold(*mxid, input.Mxid) {
|
|
return model.MakeUser(user), nil
|
|
}
|
|
}
|
|
|
|
user.PendingMatrixLinks = append(user.PendingMatrixLinks, &input.Mxid)
|
|
|
|
err = db.SaveUser(user)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeUser(user), nil
|
|
}
|
|
|
|
// RemoveMxid is the resolver for the removeMXID field.
|
|
func (r *mutationResolver) RemoveMxid(ctx context.Context, input model.RemoveMxid) (*model.User, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i, mxid := range user.MatrixLinks {
|
|
if strings.EqualFold(*mxid, input.Mxid) {
|
|
user.MatrixLinks = append(user.MatrixLinks[:i], user.MatrixLinks[i+1:]...)
|
|
}
|
|
}
|
|
|
|
for i, mxid := range user.PendingMatrixLinks {
|
|
if strings.EqualFold(*mxid, input.Mxid) {
|
|
user.PendingMatrixLinks = append(user.PendingMatrixLinks[:i], user.PendingMatrixLinks[i+1:]...)
|
|
}
|
|
}
|
|
|
|
err = db.SaveUser(user)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeUser(user), nil
|
|
}
|
|
|
|
// ReconfigureRoom is the resolver for the reconfigureRoom field.
|
|
func (r *mutationResolver) ReconfigureRoom(ctx context.Context, input model.RoomConfigUpdate) (*model.Room, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := primitive.ObjectIDFromHex(input.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rConfig, err := config.GetRoomConfigByObjectID(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
match := false
|
|
|
|
for _, admin := range rConfig.Admins {
|
|
for _, mxid := range user.MatrixLinks {
|
|
if *mxid == admin {
|
|
match = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if !match {
|
|
return nil, errors.New("unauthorized")
|
|
}
|
|
|
|
if input.Debug != nil {
|
|
rConfig.Debug = *input.Debug
|
|
}
|
|
|
|
if input.HashChecker != nil {
|
|
if input.HashChecker.HashCheckMode != nil {
|
|
newMode := uint8(0)
|
|
switch *input.HashChecker.HashCheckMode {
|
|
case model.HashCheckerModeNotice:
|
|
newMode = 0
|
|
case model.HashCheckerModeDelete:
|
|
newMode = 1
|
|
case model.HashCheckerModeMute:
|
|
newMode = 2
|
|
case model.HashCheckerModeBan:
|
|
newMode = 3
|
|
default:
|
|
return nil, errors.New("malformed hash check mode")
|
|
}
|
|
|
|
rConfig.HashChecker.HashCheckMode = newMode
|
|
}
|
|
|
|
if input.HashChecker.ChatNotice != nil {
|
|
rConfig.HashChecker.NoticeToChat = *input.HashChecker.ChatNotice
|
|
}
|
|
}
|
|
|
|
if input.AdminPowerLevel != nil {
|
|
if *input.AdminPowerLevel > 100 {
|
|
return nil, errors.New("REFUSING TO SET ADMIN POWER LEVEL > 100")
|
|
}
|
|
rConfig.AdminPowerLevel = *input.AdminPowerLevel
|
|
}
|
|
|
|
err = config.SaveRoomConfig(rConfig)
|
|
|
|
return model.MakeRoom(rConfig), nil
|
|
}
|
|
|
|
// SubscribeToList is the resolver for the subscribeToList field.
|
|
func (r *mutationResolver) SubscribeToList(ctx context.Context, input model.ListSubscriptionUpdate) (*model.Room, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := primitive.ObjectIDFromHex(input.RoomID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rConfig, err := config.GetRoomConfigByObjectID(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
match := false
|
|
|
|
for _, admin := range rConfig.Admins {
|
|
for _, mxid := range user.MatrixLinks {
|
|
if *mxid == admin {
|
|
match = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if !match {
|
|
return nil, errors.New("unauthorized")
|
|
}
|
|
|
|
listIdP, err := primitive.ObjectIDFromHex(input.ListID)
|
|
if err != nil {
|
|
return nil, errors.New("unknown list")
|
|
}
|
|
|
|
_, err = db.GetListByID(listIdP)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("unknown list")
|
|
}
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
for _, list := range rConfig.HashChecker.SubscribedLists {
|
|
if list.Hex() == input.ListID {
|
|
return model.MakeRoom(rConfig), nil
|
|
}
|
|
}
|
|
|
|
rConfig.HashChecker.SubscribedLists = append(rConfig.HashChecker.SubscribedLists, &listIdP)
|
|
|
|
err = config.SaveRoomConfig(rConfig)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeRoom(rConfig), nil
|
|
}
|
|
|
|
// UnsubscribeFromList is the resolver for the unsubscribeFromList field.
|
|
func (r *mutationResolver) UnsubscribeFromList(ctx context.Context, input model.ListSubscriptionUpdate) (*model.Room, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := primitive.ObjectIDFromHex(input.RoomID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rConfig, err := config.GetRoomConfigByObjectID(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
match := false
|
|
|
|
for _, admin := range rConfig.Admins {
|
|
for _, mxid := range user.MatrixLinks {
|
|
if *mxid == admin {
|
|
match = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if !match {
|
|
return nil, errors.New("unauthorized")
|
|
}
|
|
|
|
listIdP, err := primitive.ObjectIDFromHex(input.ListID)
|
|
if err != nil {
|
|
return nil, errors.New("unknown list")
|
|
}
|
|
|
|
_, err = db.GetListByID(listIdP)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("unknown list")
|
|
}
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
for i, list := range rConfig.HashChecker.SubscribedLists {
|
|
if list.Hex() == input.ListID {
|
|
rConfig.HashChecker.SubscribedLists = append(rConfig.HashChecker.SubscribedLists[:i], rConfig.HashChecker.SubscribedLists[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
|
|
err = config.SaveRoomConfig(rConfig)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeRoom(rConfig), nil
|
|
}
|
|
|
|
// CreateEntry is the resolver for the createEntry field.
|
|
func (r *mutationResolver) CreateEntry(ctx context.Context, input model.CreateEntry) (*model.Entry, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entry, err := db.GetEntryByHash(input.HashValue)
|
|
if err != nil && !errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if entry == nil {
|
|
entry = &model2.DBEntry{
|
|
ID: primitive.NewObjectID(),
|
|
Tags: input.Tags,
|
|
HashValue: input.HashValue,
|
|
Timestamp: time.Now(),
|
|
AddedBy: &user.ID,
|
|
Comments: nil,
|
|
}
|
|
}
|
|
|
|
if len(input.PartOf) > 0 {
|
|
for _, partOfId := range input.PartOf {
|
|
err = PerformListMaintainerCheck(partOfId, user.ID.Hex())
|
|
if err != nil {
|
|
return nil, errors.New("error adding to lists")
|
|
}
|
|
|
|
partOf, _ := primitive.ObjectIDFromHex(partOfId) // This can't fail, it worked in PerformListMaintainerCheck
|
|
entry.AddTo(&partOf)
|
|
}
|
|
}
|
|
|
|
if input.Comment != nil {
|
|
entry.Comments = append(entry.Comments, &model2.DBComment{
|
|
Timestamp: time.Now(),
|
|
CommentedBy: &user.ID,
|
|
Content: *input.Comment,
|
|
})
|
|
}
|
|
|
|
err = db.SaveEntry(entry)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeEntry(entry), nil
|
|
}
|
|
|
|
// CommentEntry is the resolver for the commentEntry field.
|
|
func (r *mutationResolver) CommentEntry(ctx context.Context, input model.CommentEntry) (*model.Entry, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := primitive.ObjectIDFromHex(input.Entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entry, err := db.GetEntryByID(id)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
entry.Comments = append(entry.Comments, &model2.DBComment{
|
|
Timestamp: time.Now(),
|
|
CommentedBy: &user.ID,
|
|
Content: input.Comment,
|
|
})
|
|
|
|
err = db.SaveEntry(entry)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeEntry(entry), nil
|
|
}
|
|
|
|
// AddToLists is the resolver for the addToLists field.
|
|
func (r *mutationResolver) AddToLists(ctx context.Context, input model.AddToLists) (*model.Entry, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := primitive.ObjectIDFromHex(input.Entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entry, err := db.GetEntryByID(id)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if len(input.Lists) > 0 {
|
|
for _, partOfId := range input.Lists {
|
|
err = PerformListMaintainerCheck(partOfId, user.ID.Hex())
|
|
if err != nil {
|
|
return nil, errors.New("error adding to lists")
|
|
}
|
|
|
|
partOf, _ := primitive.ObjectIDFromHex(partOfId) // This can't fail, it worked in PerformListMaintainerCheck
|
|
entry.AddTo(&partOf)
|
|
}
|
|
}
|
|
|
|
err = db.SaveEntry(entry)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeEntry(entry), nil
|
|
}
|
|
|
|
// RemoveFromLists is the resolver for the removeFromLists field.
|
|
func (r *mutationResolver) RemoveFromLists(ctx context.Context, input model.RemoveFromLists) (*model.Entry, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := primitive.ObjectIDFromHex(input.Entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entry, err := db.GetEntryByID(id)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if len(input.Lists) > 0 {
|
|
for _, partOfId := range input.Lists {
|
|
err = PerformListMaintainerCheck(partOfId, user.ID.Hex())
|
|
if err != nil {
|
|
return nil, errors.New("error adding to lists")
|
|
}
|
|
|
|
partOf, _ := primitive.ObjectIDFromHex(partOfId) // This can't fail, it worked in PerformListMaintainerCheck
|
|
entry.RemoveFrom(&partOf)
|
|
}
|
|
}
|
|
|
|
err = db.SaveEntry(entry)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeEntry(entry), nil
|
|
}
|
|
|
|
// CreateList is the resolver for the createList field.
|
|
func (r *mutationResolver) CreateList(ctx context.Context, input model.CreateList) (*model.List, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = db.GetListByName(input.Name)
|
|
if !errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("name taken")
|
|
}
|
|
|
|
list := model2.DBHashList{
|
|
ID: primitive.NewObjectID(),
|
|
Name: input.Name,
|
|
Tags: input.Tags,
|
|
Creator: user.ID,
|
|
}
|
|
|
|
for _, maintainer := range input.Maintainers {
|
|
id, err := primitive.ObjectIDFromHex(maintainer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
maintainerUser, err := db.GetUserByID(id)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("maintainer not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
list.Maintainers = append(list.Maintainers, &maintainerUser.ID)
|
|
}
|
|
|
|
if input.Comment != nil {
|
|
list.Comments = append(list.Comments, &model2.DBComment{
|
|
Timestamp: time.Now(),
|
|
CommentedBy: &user.ID,
|
|
Content: *input.Comment,
|
|
})
|
|
}
|
|
|
|
err = db.SaveList(&list)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeList(&list), nil
|
|
}
|
|
|
|
// CommentList is the resolver for the commentList field.
|
|
func (r *mutationResolver) CommentList(ctx context.Context, input model.CommentList) (*model.List, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := primitive.ObjectIDFromHex(input.List)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
list, err := db.GetListByID(id)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
list.Comments = append(list.Comments, &model2.DBComment{
|
|
Timestamp: time.Now(),
|
|
CommentedBy: &user.ID,
|
|
Content: input.Comment,
|
|
})
|
|
|
|
err = db.SaveList(list)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeList(list), nil
|
|
}
|
|
|
|
// DeleteList is the resolver for the deleteList field.
|
|
func (r *mutationResolver) DeleteList(ctx context.Context, input string) (bool, error) {
|
|
panic(fmt.Errorf("not implemented"))
|
|
}
|
|
|
|
// Users is the resolver for the users field.
|
|
func (r *queryResolver) Users(ctx context.Context, first *int, after *string, filter *model.UserFilter, sort *model.UserSort) (*model.UserConnection, error) {
|
|
dbFilter, dbSort, dbLimit, err := buildDBUserFilter(first, after, filter, sort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coll := db.Db.Collection(viper.GetString("bot.mongo.collection.users"))
|
|
|
|
newLimit := *dbLimit + 1
|
|
|
|
findOpts := options.FindOptions{
|
|
Limit: &newLimit,
|
|
Sort: *dbSort,
|
|
}
|
|
|
|
res, err := coll.Find(ctx, *dbFilter, &findOpts)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
var rawUsers []model2.DBUser
|
|
|
|
err = res.All(ctx, &rawUsers)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if len(rawUsers) == 0 {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
lastUserI := len(rawUsers) - 1
|
|
if lastUserI > 0 {
|
|
lastUserI--
|
|
}
|
|
|
|
firstUser := rawUsers[0]
|
|
lastUser := rawUsers[lastUserI]
|
|
|
|
isAfter := false
|
|
|
|
if after != nil {
|
|
isAfter = true
|
|
}
|
|
|
|
hasMore := false
|
|
if int64(len(rawUsers)) > *dbLimit {
|
|
hasMore = true
|
|
}
|
|
|
|
var edges []*model.UserEdge
|
|
|
|
for i, rawUser := range rawUsers {
|
|
if int64(i) == *dbLimit {
|
|
continue
|
|
}
|
|
|
|
edges = append(edges, &model.UserEdge{
|
|
Node: model.MakeUser(&rawUser),
|
|
Cursor: rawUser.ID.Hex(),
|
|
})
|
|
}
|
|
|
|
return &model.UserConnection{
|
|
PageInfo: &model.PageInfo{
|
|
HasPreviousPage: isAfter,
|
|
HasNextPage: hasMore,
|
|
StartCursor: firstUser.ID.Hex(),
|
|
EndCursor: lastUser.ID.Hex(),
|
|
},
|
|
Edges: edges,
|
|
}, nil
|
|
}
|
|
|
|
// Lists is the resolver for the lists field.
|
|
func (r *queryResolver) Lists(ctx context.Context, first *int, after *string, filter *model.ListFilter, sort *model.ListSort) (*model.ListConnection, error) {
|
|
dbFilter, dbSort, dbLimit, err := buildDBListFilter(first, after, filter, sort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coll := db.Db.Collection(viper.GetString("bot.mongo.collection.lists"))
|
|
|
|
newLimit := *dbLimit + 1
|
|
|
|
findOpts := options.FindOptions{
|
|
Limit: &newLimit,
|
|
Sort: *dbSort,
|
|
}
|
|
|
|
res, err := coll.Find(ctx, *dbFilter, &findOpts)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
var rawLists []model2.DBHashList
|
|
|
|
err = res.All(ctx, &rawLists)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if len(rawLists) == 0 {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
lastListI := len(rawLists) - 1
|
|
if lastListI > 0 {
|
|
lastListI--
|
|
}
|
|
|
|
firstList := rawLists[0]
|
|
lastList := rawLists[lastListI]
|
|
|
|
isAfter := false
|
|
|
|
if after != nil {
|
|
isAfter = true
|
|
}
|
|
|
|
hasMore := false
|
|
if int64(len(rawLists)) > *dbLimit {
|
|
hasMore = true
|
|
}
|
|
|
|
var edges []*model.ListEdge
|
|
|
|
for i, rawList := range rawLists {
|
|
if int64(i) == *dbLimit {
|
|
continue
|
|
}
|
|
|
|
edges = append(edges, &model.ListEdge{
|
|
Node: model.MakeList(&rawList),
|
|
Cursor: rawList.ID.Hex(),
|
|
})
|
|
}
|
|
|
|
return &model.ListConnection{
|
|
PageInfo: &model.PageInfo{
|
|
HasPreviousPage: isAfter,
|
|
HasNextPage: hasMore,
|
|
StartCursor: firstList.ID.Hex(),
|
|
EndCursor: lastList.ID.Hex(),
|
|
},
|
|
Edges: edges,
|
|
}, nil
|
|
}
|
|
|
|
// Entries is the resolver for the entries field.
|
|
func (r *queryResolver) Entries(ctx context.Context, first *int, after *string, filter *model.EntryFilter, sort *model.EntrySort) (*model.EntryConnection, error) {
|
|
dbFilter, dbSort, dbLimit, err := buildDBEntryFilter(first, after, filter, sort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coll := db.Db.Collection(viper.GetString("bot.mongo.collection.entries"))
|
|
|
|
newLimit := *dbLimit + 1
|
|
|
|
findOpts := options.FindOptions{
|
|
Limit: &newLimit,
|
|
Sort: *dbSort,
|
|
}
|
|
|
|
res, err := coll.Find(ctx, *dbFilter, &findOpts)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
var rawEntries []model2.DBEntry
|
|
|
|
err = res.All(ctx, &rawEntries)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if len(rawEntries) == 0 {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
lastEntryI := len(rawEntries) - 1
|
|
if lastEntryI > 0 {
|
|
lastEntryI--
|
|
}
|
|
|
|
firstEntry := rawEntries[0]
|
|
lastEntry := rawEntries[lastEntryI]
|
|
|
|
isAfter := false
|
|
|
|
if after != nil {
|
|
isAfter = true
|
|
}
|
|
|
|
hasMore := false
|
|
if int64(len(rawEntries)) > *dbLimit {
|
|
hasMore = true
|
|
}
|
|
|
|
var edges []*model.EntryEdge
|
|
|
|
for i, rawEntry := range rawEntries {
|
|
if int64(i) == *dbLimit {
|
|
continue
|
|
}
|
|
|
|
edges = append(edges, &model.EntryEdge{
|
|
Node: model.MakeEntry(&rawEntry),
|
|
Cursor: rawEntry.ID.Hex(),
|
|
})
|
|
}
|
|
|
|
return &model.EntryConnection{
|
|
PageInfo: &model.PageInfo{
|
|
HasPreviousPage: isAfter,
|
|
HasNextPage: hasMore,
|
|
StartCursor: firstEntry.ID.Hex(),
|
|
EndCursor: lastEntry.ID.Hex(),
|
|
},
|
|
Edges: edges,
|
|
}, nil
|
|
}
|
|
|
|
// Rooms is the resolver for the rooms field.
|
|
func (r *queryResolver) Rooms(ctx context.Context, first *int, after *string, filter *model.RoomFilter) (*model.RoomConnection, error) {
|
|
user, _ := GetUserFromContext(ctx)
|
|
|
|
var userMxids []string
|
|
|
|
if user != nil {
|
|
for _, mxid := range user.MatrixLinks {
|
|
userMxids = append(userMxids, *mxid)
|
|
}
|
|
}
|
|
|
|
dbFilter, dbSort, dbLimit, err := buildDBRoomFilter(first, after, filter, userMxids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coll := db.Db.Collection(viper.GetString("bot.mongo.collection.rooms"))
|
|
|
|
newLimit := *dbLimit + 1
|
|
|
|
findOpts := options.FindOptions{
|
|
Limit: &newLimit,
|
|
Sort: *dbSort,
|
|
}
|
|
|
|
res, err := coll.Find(ctx, *dbFilter, &findOpts)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
var rawEntries []config.RoomConfig
|
|
|
|
err = res.All(ctx, &rawEntries)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
if len(rawEntries) == 0 {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
lastEntryI := len(rawEntries) - 1
|
|
if lastEntryI > 0 {
|
|
lastEntryI--
|
|
}
|
|
|
|
firstEntry := rawEntries[0]
|
|
lastEntry := rawEntries[lastEntryI]
|
|
|
|
isAfter := false
|
|
|
|
if after != nil {
|
|
isAfter = true
|
|
}
|
|
|
|
hasMore := false
|
|
if int64(len(rawEntries)) > *dbLimit {
|
|
hasMore = true
|
|
}
|
|
|
|
var edges []*model.RoomEdge
|
|
|
|
for i, rawRoom := range rawEntries {
|
|
if int64(i) == *dbLimit {
|
|
continue
|
|
}
|
|
|
|
edges = append(edges, &model.RoomEdge{
|
|
Node: model.MakeRoom(&rawRoom),
|
|
Cursor: rawRoom.ID.Hex(),
|
|
})
|
|
}
|
|
|
|
return &model.RoomConnection{
|
|
PageInfo: &model.PageInfo{
|
|
HasPreviousPage: isAfter,
|
|
HasNextPage: hasMore,
|
|
StartCursor: firstEntry.ID.Hex(),
|
|
EndCursor: lastEntry.ID.Hex(),
|
|
},
|
|
Edges: edges,
|
|
}, nil
|
|
}
|
|
|
|
// Room is the resolver for the room field.
|
|
func (r *queryResolver) Room(ctx context.Context, id *string) (*model.Room, error) {
|
|
if id != nil {
|
|
dbId, err := primitive.ObjectIDFromHex(*id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coll := db.Db.Collection(viper.GetString("bot.mongo.collection.rooms"))
|
|
|
|
res := coll.FindOne(ctx, bson.M{"_id": dbId})
|
|
if res.Err() != nil {
|
|
if errors.Is(res.Err(), mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
var room config.RoomConfig
|
|
|
|
err = res.Decode(&room)
|
|
if err != nil {
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeRoom(&room), nil
|
|
}
|
|
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
// User is the resolver for the user field.
|
|
func (r *queryResolver) User(ctx context.Context, id *string, username *string) (*model.User, error) {
|
|
if id != nil {
|
|
dbId, err := primitive.ObjectIDFromHex(*id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rawUser, err := db.GetUserByID(dbId)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeUser(rawUser), nil
|
|
}
|
|
|
|
if username != nil {
|
|
rawUser, err := db.GetUserByUsername(*username)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeUser(rawUser), nil
|
|
}
|
|
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
// Entry is the resolver for the entry field.
|
|
func (r *queryResolver) Entry(ctx context.Context, id *string, hashValue *string) (*model.Entry, error) {
|
|
if id != nil {
|
|
dbId, err := primitive.ObjectIDFromHex(*id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rawEntry, err := db.GetEntryByID(dbId)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeEntry(rawEntry), nil
|
|
}
|
|
|
|
if hashValue != nil {
|
|
rawEntry, err := db.GetEntryByHash(*hashValue)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeEntry(rawEntry), nil
|
|
}
|
|
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
// List is the resolver for the list field.
|
|
func (r *queryResolver) List(ctx context.Context, id *string, name *string) (*model.List, error) {
|
|
if id != nil {
|
|
dbId, err := primitive.ObjectIDFromHex(*id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rawList, err := db.GetListByID(dbId)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeList(rawList), nil
|
|
}
|
|
|
|
if name != nil {
|
|
rawList, err := db.GetListByName(*name)
|
|
if err != nil {
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
return nil, errors.New("database error")
|
|
}
|
|
|
|
return model.MakeList(rawList), nil
|
|
}
|
|
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
// Self is the resolver for the self field.
|
|
func (r *queryResolver) Self(ctx context.Context) (*model.User, error) {
|
|
user, err := GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, errors.New("invalid session")
|
|
}
|
|
|
|
return model.MakeUser(user), nil
|
|
}
|
|
|
|
// Comment returns generated.CommentResolver implementation.
|
|
func (r *Resolver) Comment() generated.CommentResolver { return &commentResolver{r} }
|
|
|
|
// Entry returns generated.EntryResolver implementation.
|
|
func (r *Resolver) Entry() generated.EntryResolver { return &entryResolver{r} }
|
|
|
|
// List returns generated.ListResolver implementation.
|
|
func (r *Resolver) List() generated.ListResolver { return &listResolver{r} }
|
|
|
|
// Mutation returns generated.MutationResolver implementation.
|
|
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
|
|
|
|
// Query returns generated.QueryResolver implementation.
|
|
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
|
|
|
type commentResolver struct{ *Resolver }
|
|
type entryResolver struct{ *Resolver }
|
|
type listResolver struct{ *Resolver }
|
|
type mutationResolver struct{ *Resolver }
|
|
type queryResolver struct{ *Resolver }
|