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" "math/big" "strings" "time" "github.com/Unkn0wnCat/matrix-veles/graph/generated" "github.com/Unkn0wnCat/matrix-veles/graph/model" "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" ) 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 } 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 } 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 } 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) } 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 } 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) } 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 } 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 } 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 } 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 } 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 } 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 } 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 } 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 } 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 } 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 } 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 } 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 } func (r *mutationResolver) DeleteList(ctx context.Context, input string) (bool, error) { panic(fmt.Errorf("not implemented")) } 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 } 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 } 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 } 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") } 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") } 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") } 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 }