This commit is contained in:
@@ -11,7 +11,6 @@ import (
|
|||||||
"git.aiterp.net/rpdata/api/internal/auth"
|
"git.aiterp.net/rpdata/api/internal/auth"
|
||||||
"git.aiterp.net/rpdata/api/internal/loader"
|
"git.aiterp.net/rpdata/api/internal/loader"
|
||||||
"git.aiterp.net/rpdata/api/internal/store"
|
"git.aiterp.net/rpdata/api/internal/store"
|
||||||
logModel "git.aiterp.net/rpdata/api/model/log"
|
|
||||||
"github.com/99designs/gqlgen/handler"
|
"github.com/99designs/gqlgen/handler"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,8 +23,6 @@ func main() {
|
|||||||
http.Handle("/", handler.Playground("RPData API", "/graphql"))
|
http.Handle("/", handler.Playground("RPData API", "/graphql"))
|
||||||
http.Handle("/graphql", queryHandler())
|
http.Handle("/graphql", queryHandler())
|
||||||
|
|
||||||
go updateCharacters()
|
|
||||||
|
|
||||||
log.Fatal(http.ListenAndServe(":8081", nil))
|
log.Fatal(http.ListenAndServe(":8081", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,12 +47,3 @@ func queryHandler() http.HandlerFunc {
|
|||||||
handler.ServeHTTP(w, r)
|
handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCharacters() {
|
|
||||||
n, err := logModel.UpdateAllCharacters()
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Charcter updated stopped:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Updated characters on", n, "logs")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,10 +4,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models/logs"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/graph2/input"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/auth"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/models/posts"
|
"git.aiterp.net/rpdata/api/models/posts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Queries
|
||||||
|
|
||||||
func (r *resolver) Post(ctx context.Context, id string) (models.Post, error) {
|
func (r *resolver) Post(ctx context.Context, id string) (models.Post, error) {
|
||||||
return posts.FindID(id)
|
return posts.FindID(id)
|
||||||
}
|
}
|
||||||
@@ -32,3 +38,61 @@ func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]models.Po
|
|||||||
|
|
||||||
return posts.List(filter)
|
return posts.List(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mutation
|
||||||
|
|
||||||
|
func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput) (models.Post, error) {
|
||||||
|
token := auth.TokenFromContext(ctx)
|
||||||
|
if !token.Authenticated() || !token.Permitted("post.add") {
|
||||||
|
return models.Post{}, errors.New("You are not permitted to edit logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
log, err := logs.FindID(input.LogID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts.Add(log, input.Time, input.Kind, input.Nick, input.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (models.Post, error) {
|
||||||
|
token := auth.TokenFromContext(ctx)
|
||||||
|
if !token.Authenticated() || !token.Permitted("post.edit") {
|
||||||
|
return models.Post{}, errors.New("You are not permitted to edit logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
post, err := posts.FindID(input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, errors.New("Post not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInput) ([]models.Post, error) {
|
||||||
|
token := auth.TokenFromContext(ctx)
|
||||||
|
if !token.Authenticated() || !token.Permitted("post.move") {
|
||||||
|
return nil, errors.New("You are not permitted to edit logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
post, err := posts.FindID(input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Post not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts.Move(post, input.ToPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemoveInput) (models.Post, error) {
|
||||||
|
token := auth.TokenFromContext(ctx)
|
||||||
|
if !token.Authenticated() || !token.Permitted("post.remove") {
|
||||||
|
return models.Post{}, errors.New("You are not permitted to edit logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
post, err := posts.FindID(input.ID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, errors.New("Post not found (before removing, of course)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts.Remove(post)
|
||||||
|
}
|
||||||
|
|||||||
@@ -83,6 +83,19 @@ type Mutation {
|
|||||||
|
|
||||||
# Remove a chapter
|
# Remove a chapter
|
||||||
removeChapter(input: ChapterRemoveInput!): Chapter!
|
removeChapter(input: ChapterRemoveInput!): Chapter!
|
||||||
|
|
||||||
|
|
||||||
|
# Add a post
|
||||||
|
addPost(input: PostAddInput!): Post!
|
||||||
|
|
||||||
|
# Edit a post
|
||||||
|
editPost(input: PostEditInput!): Post!
|
||||||
|
|
||||||
|
# Move a post. All affected posts will be returned.
|
||||||
|
movePost(input: PostMoveInput!): [Post!]!
|
||||||
|
|
||||||
|
# Remove a post
|
||||||
|
removePost(input: PostRemoveInput!): Post!
|
||||||
}
|
}
|
||||||
|
|
||||||
# A Date represents a RFC3339 encoded date with up to millisecond precision.
|
# A Date represents a RFC3339 encoded date with up to millisecond precision.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type Post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Input for the addPost mutation
|
# Input for the addPost mutation
|
||||||
input AddPostInput {
|
input PostAddInput {
|
||||||
# The log's ID that this post should be a part of
|
# The log's ID that this post should be a part of
|
||||||
logId: String!
|
logId: String!
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ input AddPostInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Input for the editPost mutation
|
# Input for the editPost mutation
|
||||||
input EditPostInput {
|
input PostEditInput {
|
||||||
# The Post ID
|
# The Post ID
|
||||||
id: String!
|
id: String!
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ input EditPostInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Input for the movePost mutation
|
# Input for the movePost mutation
|
||||||
input MovePostInput {
|
input PostMoveInput {
|
||||||
# The Post ID
|
# The Post ID
|
||||||
id: String!
|
id: String!
|
||||||
|
|
||||||
@@ -67,6 +67,12 @@ input MovePostInput {
|
|||||||
toPosition: Int!
|
toPosition: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Input for the removePost mutation
|
||||||
|
input PostRemoveInput {
|
||||||
|
# The Post ID
|
||||||
|
id: String!
|
||||||
|
}
|
||||||
|
|
||||||
# Filter for posts query
|
# Filter for posts query
|
||||||
input PostsFilter {
|
input PostsFilter {
|
||||||
id: [String!]
|
id: [String!]
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/model/character"
|
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
"git.aiterp.net/rpdata/api/models/characters"
|
"git.aiterp.net/rpdata/api/models/characters"
|
||||||
"github.com/graph-gophers/dataloader"
|
"github.com/graph-gophers/dataloader"
|
||||||
@@ -118,7 +117,7 @@ func characterNickBatch(ctx context.Context, keys dataloader.Keys) []*dataloader
|
|||||||
var results []*dataloader.Result
|
var results []*dataloader.Result
|
||||||
nicks := keys.Keys()
|
nicks := keys.Keys()
|
||||||
|
|
||||||
characters, err := character.ListNicks(nicks...)
|
characters, err := characters.List(&characters.Filter{Nicks: nicks})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for range nicks {
|
for range nicks {
|
||||||
results = append(results, &dataloader.Result{Error: err})
|
results = append(results, &dataloader.Result{Error: err})
|
||||||
|
|||||||
@@ -1,345 +0,0 @@
|
|||||||
package character
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/internal/store"
|
|
||||||
"git.aiterp.net/rpdata/api/model/counter"
|
|
||||||
"github.com/globalsign/mgo"
|
|
||||||
"github.com/globalsign/mgo/bson"
|
|
||||||
)
|
|
||||||
|
|
||||||
var collection *mgo.Collection
|
|
||||||
|
|
||||||
var logsCollection *mgo.Collection
|
|
||||||
|
|
||||||
// Character is a common data model representing an RP character or NPC.
|
|
||||||
type Character struct {
|
|
||||||
ID string `json:"id" bson:"_id"`
|
|
||||||
Nicks []string `json:"nicks" bson:"nicks"`
|
|
||||||
Name string `json:"name" bson:"name"`
|
|
||||||
ShortName string `json:"shortName" bson:"shortName"`
|
|
||||||
Author string `json:"author" bson:"author"`
|
|
||||||
Description string `json:"description" bson:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter is used to filter the list of characters
|
|
||||||
type Filter struct {
|
|
||||||
IDs []string `json:"ids"`
|
|
||||||
Nicks []string `json:"nicks"`
|
|
||||||
Names []string `json:"names"`
|
|
||||||
Author *string `json:"author"`
|
|
||||||
Search *string `json:"search"`
|
|
||||||
Logged *bool `json:"logged"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nick gets the character's nick.
|
|
||||||
func (character *Character) Nick() *string {
|
|
||||||
if len(character.Nicks[0]) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &character.Nicks[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasNick returns true if the character has that nick
|
|
||||||
func (character *Character) HasNick(nick string) bool {
|
|
||||||
for i := range character.Nicks {
|
|
||||||
if strings.EqualFold(character.Nicks[i], nick) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNick adds a nick to the character. It will return an error
|
|
||||||
// if the nick already exists.
|
|
||||||
func (character *Character) AddNick(nick string) error {
|
|
||||||
for i := range character.Nicks {
|
|
||||||
if strings.EqualFold(character.Nicks[i], nick) {
|
|
||||||
return errors.New("Nick already exists")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := collection.UpdateId(character.ID, bson.M{"$push": bson.M{"nicks": nick}})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
character.Nicks = append(character.Nicks, nick)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveNick removes the nick from the character. It will raise
|
|
||||||
// an error if the nick does not exist; even if that kind of is
|
|
||||||
// the end goal.
|
|
||||||
func (character *Character) RemoveNick(nick string) error {
|
|
||||||
index := -1
|
|
||||||
for i := range character.Nicks {
|
|
||||||
if strings.EqualFold(character.Nicks[i], nick) {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if index == -1 {
|
|
||||||
return errors.New("Nick does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := collection.UpdateId(character.ID, bson.M{"$pull": bson.M{"nicks": nick}})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
character.Nicks = append(character.Nicks[:index], character.Nicks[index+1:]...)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Edit sets the fields of metadata. Only non-empty and different fields will be set in the
|
|
||||||
// database, preventing out of order edits to two fields from conflicting
|
|
||||||
func (character *Character) Edit(name, shortName, description string) error {
|
|
||||||
changes := bson.M{}
|
|
||||||
if len(name) > 0 && name != character.Name {
|
|
||||||
changes["name"] = name
|
|
||||||
}
|
|
||||||
if len(shortName) > 0 && shortName != character.ShortName {
|
|
||||||
changes["shortName"] = shortName
|
|
||||||
}
|
|
||||||
if len(description) > 0 && description != character.Description {
|
|
||||||
changes["description"] = description
|
|
||||||
}
|
|
||||||
|
|
||||||
err := collection.UpdateId(character.ID, changes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if changes["name"] != nil {
|
|
||||||
character.Name = name
|
|
||||||
}
|
|
||||||
if changes["shortName"] != nil {
|
|
||||||
character.ShortName = shortName
|
|
||||||
}
|
|
||||||
if changes["description"] != nil {
|
|
||||||
character.Description = description
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the character from the database. The reason this is an instance method
|
|
||||||
// is that it should only be done after an authorization check.
|
|
||||||
func (character *Character) Remove() error {
|
|
||||||
return collection.RemoveId(character.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindID finds Character by ID
|
|
||||||
func FindID(id string) (Character, error) {
|
|
||||||
return find(bson.M{"_id": id})
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindNick finds Character by nick
|
|
||||||
func FindNick(nick string) (Character, error) {
|
|
||||||
return find(bson.M{"nicks": nick})
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindName finds Character by either full name or
|
|
||||||
// short name.
|
|
||||||
func FindName(name string) (Character, error) {
|
|
||||||
return find(bson.M{"$or": []bson.M{bson.M{"name": name}, bson.M{"shortName": name}}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// List lists all characters
|
|
||||||
func List(filter *Filter) ([]Character, error) {
|
|
||||||
query := bson.M{}
|
|
||||||
|
|
||||||
if filter != nil {
|
|
||||||
if len(filter.IDs) > 1 {
|
|
||||||
query["id"] = bson.M{"$in": filter.IDs}
|
|
||||||
} else if len(filter.IDs) == 1 {
|
|
||||||
query["id"] = filter.IDs[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(filter.Nicks) > 1 {
|
|
||||||
query["nicks"] = bson.M{"$in": filter.Nicks}
|
|
||||||
} else if len(filter.Nicks) == 1 {
|
|
||||||
query["nicks"] = filter.Nicks[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(filter.Names) > 1 {
|
|
||||||
query["$or"] = bson.M{
|
|
||||||
"name": bson.M{"$in": filter.Names},
|
|
||||||
"shortName": bson.M{"$in": filter.Names},
|
|
||||||
}
|
|
||||||
} else if len(filter.Names) == 1 {
|
|
||||||
query["$or"] = bson.M{
|
|
||||||
"name": filter.Names[0],
|
|
||||||
"shortName": filter.Names[0],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Logged != nil {
|
|
||||||
query["logged"] = *filter.Logged
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Author != nil {
|
|
||||||
query["author"] = *filter.Author
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Search != nil {
|
|
||||||
query["$text"] = bson.M{"$search": *filter.Search}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListAuthor lists all characters by author
|
|
||||||
func ListAuthor(author string) ([]Character, error) {
|
|
||||||
return list(bson.M{"author": author})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNicks lists all characters with either of these nicks. This was made with
|
|
||||||
// the logbot in mind, to batch an order for characters.
|
|
||||||
func ListNicks(nicks ...string) ([]Character, error) {
|
|
||||||
return list(bson.M{"nicks": bson.M{"$in": nicks}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListIDs lists all characters with either of these IDs.
|
|
||||||
func ListIDs(ids ...string) ([]Character, error) {
|
|
||||||
return list(bson.M{"_id": bson.M{"$in": ids}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListFilter lists all logs matching the filters.
|
|
||||||
func ListFilter(ids []string, nicks []string, names []string, author *string, search *string, logged *bool) ([]Character, error) {
|
|
||||||
query := bson.M{}
|
|
||||||
|
|
||||||
if logged != nil {
|
|
||||||
loggedIDs := make([]string, 0, 64)
|
|
||||||
err := logsCollection.Find(bson.M{"characterIds": bson.M{"$ne": nil}}).Distinct("characterIds", &loggedIDs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ids) > 0 {
|
|
||||||
newIds := make([]string, 0, len(ids))
|
|
||||||
for _, id := range ids {
|
|
||||||
for _, loggedID := range loggedIDs {
|
|
||||||
if id == loggedID {
|
|
||||||
newIds = append(newIds, id)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ids = newIds
|
|
||||||
} else {
|
|
||||||
ids = loggedIDs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(ids) > 0 {
|
|
||||||
query["_id"] = bson.M{"$in": ids}
|
|
||||||
}
|
|
||||||
if len(nicks) > 0 {
|
|
||||||
query["nicks"] = bson.M{"$in": nicks}
|
|
||||||
}
|
|
||||||
if len(names) > 0 {
|
|
||||||
query["name"] = bson.M{"$in": names}
|
|
||||||
}
|
|
||||||
if author != nil {
|
|
||||||
query["author"] = *author
|
|
||||||
}
|
|
||||||
if search != nil {
|
|
||||||
query["$text"] = bson.M{"$search": *search}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a Character and pushes it to the database. It does some validation
|
|
||||||
// on nick, name, shortName and author. Leave the shortname blank to have it be the
|
|
||||||
// first name.
|
|
||||||
func New(nick, name, shortName, author, description string) (Character, error) {
|
|
||||||
if len(nick) < 1 || len(name) < 1 || len(author) < 1 {
|
|
||||||
return Character{}, errors.New("Nick, name, or author name too short or empty")
|
|
||||||
}
|
|
||||||
if shortName == "" {
|
|
||||||
shortName = strings.SplitN(name, " ", 2)[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
char, err := FindNick(nick)
|
|
||||||
if err == nil && char.ID != "" {
|
|
||||||
return Character{}, errors.New("Nick is occupied")
|
|
||||||
}
|
|
||||||
|
|
||||||
nextID, err := counter.Next("auto_increment", "Character")
|
|
||||||
if err != nil {
|
|
||||||
return Character{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
character := Character{
|
|
||||||
ID: "C" + strconv.Itoa(nextID),
|
|
||||||
Nicks: []string{nick},
|
|
||||||
Name: name,
|
|
||||||
ShortName: shortName,
|
|
||||||
Author: author,
|
|
||||||
Description: description,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = collection.Insert(character)
|
|
||||||
if err != nil {
|
|
||||||
return Character{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return character, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func find(query interface{}) (Character, error) {
|
|
||||||
character := Character{}
|
|
||||||
err := collection.Find(query).One(&character)
|
|
||||||
if err != nil {
|
|
||||||
return Character{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return character, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func list(query interface{}) ([]Character, error) {
|
|
||||||
characters := make([]Character, 0, 64)
|
|
||||||
err := collection.Find(query).All(&characters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return characters, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
store.HandleInit(func(db *mgo.Database) {
|
|
||||||
collection = db.C("common.characters")
|
|
||||||
|
|
||||||
collection.EnsureIndexKey("name")
|
|
||||||
collection.EnsureIndexKey("shortName")
|
|
||||||
collection.EnsureIndexKey("author")
|
|
||||||
err := collection.EnsureIndex(mgo.Index{
|
|
||||||
Key: []string{"nicks"},
|
|
||||||
Unique: true,
|
|
||||||
DropDups: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("init common.characters:", err)
|
|
||||||
}
|
|
||||||
err = collection.EnsureIndex(mgo.Index{
|
|
||||||
Key: []string{"$text:description"},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("init common.characters:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logsCollection = db.C("logbot3.logs")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -18,3 +18,14 @@ func (character *Character) Nick() *string {
|
|||||||
|
|
||||||
return &character.Nicks[0]
|
return &character.Nicks[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasNick gets whether the character has the nick.
|
||||||
|
func (character *Character) HasNick(nick string) bool {
|
||||||
|
for i := range character.Nicks {
|
||||||
|
if nick == character.Nicks[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.aiterp.net/rpdata/api/model/counter"
|
"git.aiterp.net/rpdata/api/internal/counter"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package posts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/internal/counter"
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add creates a new post.
|
||||||
|
func Add(log models.Log, time time.Time, kind, nick, text string) (models.Post, error) {
|
||||||
|
if kind == "" || nick == "" || text == "" {
|
||||||
|
return models.Post{}, errors.New("Missing/empty parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.RLock()
|
||||||
|
defer mutex.RUnlock()
|
||||||
|
|
||||||
|
position, err := counter.Next("next_post_id", log.ShortID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
post := models.Post{
|
||||||
|
ID: generateID(time),
|
||||||
|
Position: position,
|
||||||
|
LogID: log.ShortID,
|
||||||
|
Time: time,
|
||||||
|
Kind: kind,
|
||||||
|
Nick: nick,
|
||||||
|
Text: text,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.Insert(post)
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return post, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateID(time time.Time) string {
|
||||||
|
data := make([]byte, 4)
|
||||||
|
rand.Read(data)
|
||||||
|
|
||||||
|
return "P" + strconv.FormatInt(time.UnixNano(), 36) + strconv.FormatInt(int64(binary.LittleEndian.Uint32(data)), 36)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package posts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Edit edits a post and returns the result if the edit succeeded.
|
||||||
|
func Edit(post models.Post, time *time.Time, kind *string, nick *string, text *string) (models.Post, error) {
|
||||||
|
mutex.RLock()
|
||||||
|
defer mutex.RUnlock()
|
||||||
|
|
||||||
|
changes := bson.M{}
|
||||||
|
|
||||||
|
if time != nil && !time.IsZero() && !time.Equal(post.Time) {
|
||||||
|
changes["time"] = *time
|
||||||
|
post.Time = *time
|
||||||
|
}
|
||||||
|
if kind != nil && *kind != "" && *kind != post.Kind {
|
||||||
|
changes["kind"] = *kind
|
||||||
|
post.Kind = *kind
|
||||||
|
}
|
||||||
|
if nick != nil && *nick != "" && *nick != post.Nick {
|
||||||
|
changes["nick"] = *nick
|
||||||
|
post.Nick = *nick
|
||||||
|
}
|
||||||
|
if text != nil && *text != "" && *text != post.Text {
|
||||||
|
changes["text"] = *text
|
||||||
|
post.Text = *text
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return post, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := collection.UpdateId(post.ID, bson.M{"$set": changes})
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return post, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package posts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Move the post
|
||||||
|
func Move(post models.Post, toPosition int) ([]models.Post, error) {
|
||||||
|
if toPosition < 1 {
|
||||||
|
return nil, errors.New("Invalid position")
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
// To avoid problems, only allow target indices that are allowed. If it's 1, then there is bound to
|
||||||
|
// be a post at the position.
|
||||||
|
if toPosition > 1 {
|
||||||
|
existing := models.Post{}
|
||||||
|
err := collection.Find(bson.M{"logId": post.LogID, "position": toPosition}).One(&existing)
|
||||||
|
|
||||||
|
if err != nil || existing.Position != toPosition {
|
||||||
|
return nil, errors.New("No post found at the position")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query := bson.M{"logId": post.LogID}
|
||||||
|
operation := bson.M{"$inc": bson.M{"position": 1}}
|
||||||
|
|
||||||
|
if toPosition < post.Position {
|
||||||
|
query["$and"] = []bson.M{
|
||||||
|
bson.M{"position": bson.M{"$gte": toPosition}},
|
||||||
|
bson.M{"position": bson.M{"$lt": post.Position}},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query["$and"] = []bson.M{
|
||||||
|
bson.M{"position": bson.M{"$gt": post.Position}},
|
||||||
|
bson.M{"position": bson.M{"$lte": toPosition}},
|
||||||
|
}
|
||||||
|
|
||||||
|
operation["$inc"] = bson.M{"position": -1}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := collection.UpdateAll(query, operation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Moving others failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.UpdateId(post.ID, bson.M{"$set": bson.M{"position": toPosition}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Moving failed: " + err.Error() + " (If you see this on the page, please let me know ASAP)")
|
||||||
|
}
|
||||||
|
|
||||||
|
from, to := post.Position, toPosition
|
||||||
|
if to < from {
|
||||||
|
from, to = to, from
|
||||||
|
}
|
||||||
|
|
||||||
|
posts := make([]models.Post, 0, (to-from)+1)
|
||||||
|
err = collection.Find(bson.M{"logId": post.LogID, "position": bson.M{"$gte": from, "$lte": to}}).Sort("position").All(&posts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("The move completed successfully, but finding the moved posts failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return posts, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package posts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"github.com/globalsign/mgo/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Remove removes a post, moving all subsequent post up one position
|
||||||
|
func Remove(post models.Post) (models.Post, error) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
err := collection.RemoveId(post.ID)
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = collection.UpdateAll(bson.M{"logId": post.LogID, "position": bson.M{"$gt": post.Position}}, bson.M{"$inc": bson.M{"position": -1}})
|
||||||
|
if err != nil {
|
||||||
|
return models.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return post, nil
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
package posts
|
|
||||||
Reference in New Issue
Block a user