diff --git a/cmd/rpdata-server/main.go b/cmd/rpdata-server/main.go index 057837c..40ddc36 100644 --- a/cmd/rpdata-server/main.go +++ b/cmd/rpdata-server/main.go @@ -13,7 +13,6 @@ import ( "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/internal/config" "git.aiterp.net/rpdata/api/internal/instrumentation" - "git.aiterp.net/rpdata/api/internal/loader" "git.aiterp.net/rpdata/api/internal/store" "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/services" @@ -102,9 +101,6 @@ func queryHandler(services *services.Bundle) http.HandlerFunc { handler.ResolverMiddleware(instrumentation.ResolverMiddleware()), ) return func(w http.ResponseWriter, r *http.Request) { - l := loader.New() - r = r.WithContext(l.ToContext(r.Context())) - // >_> if strings.HasPrefix(r.Header.Get("Authorization"), "Bearer of the curse") { w.Header().Set("X-Emerald-Herald", "Seek souls. Larger, more powerful souls.") diff --git a/models/changes/db.go b/models/changes/db.go deleted file mode 100644 index 53eff29..0000000 --- a/models/changes/db.go +++ /dev/null @@ -1,25 +0,0 @@ -package changes - -import ( - "git.aiterp.net/rpdata/api/internal/store" - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo" - "github.com/globalsign/mgo/bson" - "sync" -) - -var collection *mgo.Collection -var submitMutex sync.Mutex - -func list(query bson.M, limit int) ([]models.Change, error) { - changes := make([]models.Change, 0, 64) - err := collection.Find(query).Limit(limit).Sort("-date").All(&changes) - - return changes, err -} - -func init() { - store.HandleInit(func(db *mgo.Database) { - collection = db.C("common.changes") - }) -} diff --git a/models/changes/list.go b/models/changes/list.go deleted file mode 100644 index 2bf34ae..0000000 --- a/models/changes/list.go +++ /dev/null @@ -1,42 +0,0 @@ -package changes - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Filter is a filter for changes.List. -type Filter struct { - Keys []models.ChangeKey - EarliestDate *time.Time - Limit *int -} - -// List lists changes. -func List(filter *Filter) ([]models.Change, error) { - query := bson.M{} - limit := 0 - - if filter != nil { - if filter.Limit != nil { - limit = *filter.Limit - } - - if filter.Keys != nil { - query["keys"] = bson.M{"$in": filter.Keys} - } else { - query["listed"] = true - } - - if filter.EarliestDate != nil { - query["date"] = bson.M{"$gte": *filter.EarliestDate} - } - } else { - query["listed"] = true - limit = 256 - } - - return list(query, limit) -} diff --git a/models/changes/submit.go b/models/changes/submit.go deleted file mode 100644 index 2abd23e..0000000 --- a/models/changes/submit.go +++ /dev/null @@ -1,125 +0,0 @@ -package changes - -import ( - "log" - "strconv" - "time" - - "git.aiterp.net/rpdata/api/internal/counter" - "git.aiterp.net/rpdata/api/models" -) - -// Submit a change to the database. The objects may be any supported model, or arrays. -func Submit(model, op, author string, listed bool, keys []models.ChangeKey, objects ...interface{}) (models.Change, error) { - submitMutex.Lock() - defer submitMutex.Unlock() - - id, err := counter.Next("auto_increment", "Change") - if err != nil { - return models.Change{}, err - } - - if !models.ChangeModel(model).IsValid() { - panic("Invalid model") - } - - // Silently discard * keys on unlisted changes. - if !listed { - keysCopy := make([]models.ChangeKey, len(keys)) - copy(keysCopy, keys) - keys = keysCopy - - deletes := make([]int, 0, 1) - for i, key := range keys { - if key.ID == "*" { - deletes = append(deletes, i-len(deletes)) - } - } - - for _, index := range deletes { - keys = append(keys[:index], keys[index+1:]...) - } - } - - change := models.Change{ - ID: "Change_" + strconv.Itoa(id), - Model: models.ChangeModel(model), - Date: time.Now(), - Op: op, - Author: author, - Keys: keys, - Listed: listed, - } - - for _, object := range objects { - switch object := object.(type) { - case models.Log: - change.Logs = append(change.Logs, &object) - case *models.Log: - change.Logs = append(change.Logs, object) - case []models.Log: - for _, obj := range object { - change.Logs = append(change.Logs, &obj) - } - case models.Character: - change.Characters = append(change.Characters, &object) - case *models.Character: - change.Characters = append(change.Characters, object) - case []models.Character: - for _, obj := range object { - change.Characters = append(change.Characters, &obj) - } - case models.Channel: - change.Channels = append(change.Channels, &object) - case *models.Channel: - change.Channels = append(change.Channels, object) - case []models.Channel: - for _, obj := range object { - change.Channels = append(change.Channels, &obj) - } - case models.Post: - change.Posts = append(change.Posts, &object) - case *models.Post: - change.Posts = append(change.Posts, object) - case []models.Post: - for _, obj := range object { - change.Posts = append(change.Posts, &obj) - } - case models.Story: - change.Stories = append(change.Stories, &object) - case *models.Story: - change.Stories = append(change.Stories, object) - case []models.Story: - for _, obj := range object { - change.Stories = append(change.Stories, &obj) - } - case models.Chapter: - change.Chapters = append(change.Chapters, &object) - case *models.Chapter: - change.Chapters = append(change.Chapters, object) - case []models.Chapter: - for _, obj := range object { - change.Chapters = append(change.Chapters, &obj) - } - case models.Comment: - change.Comments = append(change.Comments, &object) - case *models.Comment: - change.Comments = append(change.Comments, object) - case []models.Comment: - for _, obj := range object { - change.Comments = append(change.Comments, &obj) - } - default: - log.Printf("Warning: unrecognized object in change: %#+v", object) - } - } - - pushToSubscribers(change) - - err = collection.Insert(&change) - if err != nil { - return models.Change{}, err - } - - return change, nil -} diff --git a/models/changes/subscribe.go b/models/changes/subscribe.go deleted file mode 100644 index ffe96a3..0000000 --- a/models/changes/subscribe.go +++ /dev/null @@ -1,90 +0,0 @@ -package changes - -import ( - "context" - "sync" - - "git.aiterp.net/rpdata/api/models" -) - -var subMutex sync.Mutex -var subList []*subscription - -type subscription struct { - Keys map[string]bool - Channel chan<- *models.Change - WildCard bool -} - -// Subscribe subscribes to all changes. -func Subscribe(ctx context.Context, keys []models.ChangeKey, wildcard bool) <-chan *models.Change { - channel := make(chan *models.Change, 64) - sub := &subscription{ - Keys: make(map[string]bool, len(keys)), - Channel: channel, - WildCard: wildcard, - } - - for _, key := range keys { - sub.Keys[mapKey(key)] = true - } - - subMutex.Lock() - subList = append(subList, sub) - subMutex.Unlock() - - go func() { - <-ctx.Done() - - subMutex.Lock() - for i := range subList { - if subList[i] == sub { - subList = append(subList[:i], subList[i+1:]...) - break - } - } - subMutex.Unlock() - }() - - return channel -} - -func pushToSubscribers(change models.Change) { - keys := make([]string, len(change.Keys)) - for i := range change.Keys { - keys[i] = mapKey(change.Keys[i]) - } - - subMutex.Lock() -SubLoop: - for _, sub := range subList { - changeCopy := change - - if sub.WildCard && change.Listed { - select { - case sub.Channel <- &changeCopy: - default: - } - } else { - for _, key := range keys { - if sub.Keys[key] { - select { - case sub.Channel <- &changeCopy: - default: - } - - continue SubLoop - } - } - } - } - subMutex.Unlock() -} - -func mapKey(ck models.ChangeKey) string { - return ck.Model.String() + "." + ck.ID -} - -func init() { - subList = make([]*subscription, 0, 16) -} diff --git a/models/chapters/add.go b/models/chapters/add.go deleted file mode 100644 index 37c7dfd..0000000 --- a/models/chapters/add.go +++ /dev/null @@ -1,40 +0,0 @@ -package chapters - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Add adds a new chapter. -func Add(story models.Story, title, author, source string, createdDate time.Time, finctionalDate *time.Time, commentMode models.ChapterCommentMode) (models.Chapter, error) { - chapter := models.Chapter{ - ID: makeChapterID(), - StoryID: story.ID, - Title: title, - Author: author, - Source: source, - CreatedDate: createdDate, - EditedDate: createdDate, - CommentMode: commentMode, - CommentsLocked: false, - } - - if finctionalDate != nil { - chapter.FictionalDate = *finctionalDate - } - - err := collection.Insert(chapter) - if err != nil { - return models.Chapter{}, err - } - - if createdDate.After(story.UpdatedDate) { - if err := storyCollection.UpdateId(story.ID, bson.M{"$set": bson.M{"updatedDate": createdDate}}); err == nil { - story.UpdatedDate = createdDate - } - } - - return chapter, nil -} diff --git a/models/chapters/db.go b/models/chapters/db.go deleted file mode 100644 index 71d791a..0000000 --- a/models/chapters/db.go +++ /dev/null @@ -1,61 +0,0 @@ -package chapters - -import ( - "crypto/rand" - "encoding/binary" - "strconv" - - "git.aiterp.net/rpdata/api/internal/store" - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo" -) - -var collection *mgo.Collection -var storyCollection *mgo.Collection - -func find(query interface{}) (models.Chapter, error) { - chapter := models.Chapter{} - err := collection.Find(query).One(&chapter) - - return chapter, err -} - -func list(query interface{}) ([]models.Chapter, error) { - chapters := make([]models.Chapter, 0, 8) - err := collection.Find(query).Sort("createdDate").All(&chapters) - if err != nil { - return nil, err - } - - return chapters, nil -} - -func makeChapterID() string { - result := "SC" - offset := 0 - data := make([]byte, 32) - - rand.Read(data) - for len(result) < 24 { - result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36) - offset += 8 - - if offset >= 32 { - rand.Read(data) - offset = 0 - } - } - - return result[:24] -} - -func init() { - store.HandleInit(func(db *mgo.Database) { - collection = db.C("story.chapters") - storyCollection = db.C("story.stories") - - collection.EnsureIndexKey("storyId") - collection.EnsureIndexKey("author") - collection.EnsureIndexKey("createdDate") - }) -} diff --git a/models/chapters/edit.go b/models/chapters/edit.go deleted file mode 100644 index fddefb7..0000000 --- a/models/chapters/edit.go +++ /dev/null @@ -1,46 +0,0 @@ -package chapters - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Edit edits a chapter, and updates EditedDate. While many Edit functions cheat if there's nothing to -// change, this functill will due to EditedDate. -func Edit(chapter models.Chapter, title, source *string, fictionalDate *time.Time, commentMode *models.ChapterCommentMode, commentsLocked *bool) (models.Chapter, error) { - now := time.Now() - changes := bson.M{"editedDate": now} - - edited := chapter - edited.EditedDate = now - - if title != nil && *title != chapter.Title { - changes["title"] = *title - edited.Title = *title - } - if source != nil && *source != chapter.Source { - changes["source"] = *source - edited.Source = *source - } - if fictionalDate != nil && !fictionalDate.Equal(chapter.FictionalDate) { - changes["fictionalDate"] = *fictionalDate - edited.FictionalDate = *fictionalDate - } - if commentMode != nil && *commentMode != chapter.CommentMode { - changes["commentMode"] = *commentMode - edited.CommentMode = *commentMode - } - if commentsLocked != nil && *commentsLocked != chapter.CommentsLocked { - changes["commentsLocked"] = *commentsLocked - edited.CommentsLocked = *commentsLocked - } - - err := collection.UpdateId(chapter.ID, bson.M{"$set": changes}) - if err != nil { - return chapter, err - } - - return edited, nil -} diff --git a/models/chapters/find.go b/models/chapters/find.go deleted file mode 100644 index f326fb0..0000000 --- a/models/chapters/find.go +++ /dev/null @@ -1,11 +0,0 @@ -package chapters - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// FindID finds a chapter by ID -func FindID(id string) (models.Chapter, error) { - return find(bson.M{"_id": id}) -} diff --git a/models/chapters/list.go b/models/chapters/list.go deleted file mode 100644 index a5fa8d4..0000000 --- a/models/chapters/list.go +++ /dev/null @@ -1,11 +0,0 @@ -package chapters - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// ListStoryID lists all chapters for the story ID -func ListStoryID(storyID string) ([]models.Chapter, error) { - return list(bson.M{"storyId": storyID}) -} diff --git a/models/chapters/move.go b/models/chapters/move.go deleted file mode 100644 index c60328f..0000000 --- a/models/chapters/move.go +++ /dev/null @@ -1,28 +0,0 @@ -package chapters - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Move updates the chapter, moving it to the given story. -func Move(chapter models.Chapter, story models.Story) (models.Chapter, error) { - now := time.Now() - - err := collection.UpdateId(chapter.ID, bson.M{"$set": bson.M{"editedDate": now, "storyId": story.ID}}) - if err != nil { - return models.Chapter{}, err - } - - chapter.EditedDate = now - - if chapter.CreatedDate.After(story.UpdatedDate) { - if err := storyCollection.UpdateId(story.ID, bson.M{"$set": bson.M{"updatedDate": chapter.CreatedDate}}); err == nil { - story.UpdatedDate = chapter.CreatedDate - } - } - - return chapter, nil -} diff --git a/models/chapters/remove.go b/models/chapters/remove.go deleted file mode 100644 index dd300b6..0000000 --- a/models/chapters/remove.go +++ /dev/null @@ -1,24 +0,0 @@ -package chapters - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Remove removes a chapter. -func Remove(chapter models.Chapter) (models.Chapter, error) { - if err := collection.RemoveId(chapter.ID); err != nil { - return models.Chapter{}, err - } - - return chapter, nil -} - -// RemoveStory removes all chapters belonging to a story -func RemoveStory(story models.Story) error { - if _, err := collection.RemoveAll(bson.M{"storyId": story.ID}); err != nil { - return err - } - - return nil -} diff --git a/models/characters/add-nick.go b/models/characters/add-nick.go deleted file mode 100644 index c08c8f5..0000000 --- a/models/characters/add-nick.go +++ /dev/null @@ -1,27 +0,0 @@ -package characters - -import ( - "errors" - "strings" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// AddNick adds a nick to a characters -func AddNick(character models.Character, nick string) (models.Character, error) { - for i := range character.Nicks { - if strings.EqualFold(character.Nicks[i], nick) { - return models.Character{}, errors.New("Nick already exists") - } - } - - err := collection.UpdateId(character.ID, bson.M{"$push": bson.M{"nicks": nick}}) - if err != nil { - return models.Character{}, err - } - - character.Nicks = append(character.Nicks, nick) - - return character, nil -} diff --git a/models/characters/add.go b/models/characters/add.go deleted file mode 100644 index f4c4a44..0000000 --- a/models/characters/add.go +++ /dev/null @@ -1,48 +0,0 @@ -package characters - -import ( - "errors" - "strconv" - "strings" - - "git.aiterp.net/rpdata/api/internal/counter" - "git.aiterp.net/rpdata/api/models" -) - -// Add creates a Character and pushes it to the database. It does some validation -// on nick, name, shortName and author. It will generate a shortname from the first -// name if a blank one is provided. -func Add(nick, name, shortName, author, description string) (models.Character, error) { - if len(nick) < 1 || len(name) < 1 || len(author) < 1 { - return models.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 models.Character{}, errors.New("Nick is occupied") - } - - nextID, err := counter.Next("auto_increment", "Character") - if err != nil { - return models.Character{}, err - } - - character := models.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 models.Character{}, err - } - - return character, nil -} diff --git a/models/characters/db.go b/models/characters/db.go deleted file mode 100644 index c6c6722..0000000 --- a/models/characters/db.go +++ /dev/null @@ -1,55 +0,0 @@ -package characters - -import ( - "log" - - "git.aiterp.net/rpdata/api/internal/store" - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo" -) - -var collection *mgo.Collection - -func find(query interface{}) (models.Character, error) { - character := models.Character{} - err := collection.Find(query).One(&character) - if err != nil { - return models.Character{}, err - } - - return character, nil -} - -func list(query interface{}) ([]models.Character, error) { - characters := make([]models.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) - } - }) -} diff --git a/models/characters/edit.go b/models/characters/edit.go deleted file mode 100644 index 7572f37..0000000 --- a/models/characters/edit.go +++ /dev/null @@ -1,36 +0,0 @@ -package characters - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// 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 Edit(character models.Character, name, shortName, description *string) (models.Character, error) { - changes := bson.M{} - - if name != nil && *name != character.Name { - character.Name = *name - changes["name"] = *name - } - if shortName != nil && *shortName != character.ShortName { - character.ShortName = *shortName - changes["shortName"] = *shortName - } - if description != nil && *description != character.Description { - character.Description = *description - changes["description"] = *description - } - - if len(changes) == 0 { - return character, nil - } - - err := collection.UpdateId(character.ID, bson.M{"$set": changes}) - if err != nil { - return models.Character{}, err - } - - return character, nil -} diff --git a/models/characters/find.go b/models/characters/find.go deleted file mode 100644 index 7e67214..0000000 --- a/models/characters/find.go +++ /dev/null @@ -1,16 +0,0 @@ -package characters - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// FindID finds a character by id. -func FindID(id string) (models.Character, error) { - return find(bson.M{"_id": id}) -} - -// FindNick finds a character by nick -func FindNick(nick string) (models.Character, error) { - return find(bson.M{"nicks": nick}) -} diff --git a/models/characters/list.go b/models/characters/list.go deleted file mode 100644 index 5820be2..0000000 --- a/models/characters/list.go +++ /dev/null @@ -1,56 +0,0 @@ -package characters - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// 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"` -} - -// List lists all characters -func List(filter *Filter) ([]models.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"] = filter.Nicks[0] - } else if filter.Nicks != nil { - query["nicks"] = bson.M{"$in": filter.Nicks} - } - - 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.Author != nil { - query["author"] = *filter.Author - } - - if filter.Search != nil { - query["$text"] = bson.M{"$search": *filter.Search} - } - } - - return list(query) -} diff --git a/models/characters/remove-nick.go b/models/characters/remove-nick.go deleted file mode 100644 index d4ca771..0000000 --- a/models/characters/remove-nick.go +++ /dev/null @@ -1,32 +0,0 @@ -package characters - -import ( - "errors" - "strings" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// RemoveNick removes a nick to a characters -func RemoveNick(character models.Character, nick string) (models.Character, error) { - index := -1 - for i := range character.Nicks { - if strings.EqualFold(character.Nicks[i], nick) { - index = i - break - } - } - if index == -1 { - return models.Character{}, errors.New("Nick does not exist") - } - - err := collection.UpdateId(character.ID, bson.M{"$pull": bson.M{"nicks": nick}}) - if err != nil { - return models.Character{}, err - } - - character.Nicks = append(character.Nicks[:index], character.Nicks[index+1:]...) - - return character, nil -} diff --git a/models/characters/remove.go b/models/characters/remove.go deleted file mode 100644 index 7af4bc4..0000000 --- a/models/characters/remove.go +++ /dev/null @@ -1,13 +0,0 @@ -package characters - -import "git.aiterp.net/rpdata/api/models" - -// Remove removes a character, returning it if it succeeds -func Remove(character models.Character) (models.Character, error) { - err := collection.RemoveId(character.ID) - if err != nil { - return models.Character{}, err - } - - return character, nil -} diff --git a/models/comments/add.go b/models/comments/add.go deleted file mode 100644 index 2958f24..0000000 --- a/models/comments/add.go +++ /dev/null @@ -1,35 +0,0 @@ -package comments - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" -) - -// Add adds a comment. -func Add(chapter models.Chapter, subject, author, source, characterName string, character *models.Character, createdDate time.Time, fictionalDate time.Time) (models.Comment, error) { - characterID := "" - if character != nil { - characterID = character.ID - } - - comment := models.Comment{ - ID: makeCommentID(), - ChapterID: chapter.ID, - Subject: subject, - Author: author, - CharacterName: characterName, - CharacterID: characterID, - FictionalDate: fictionalDate, - CreatedDate: createdDate, - EditedDate: createdDate, - Source: source, - } - - err := collection.Insert(comment) - if err != nil { - return models.Comment{}, err - } - - return comment, nil -} diff --git a/models/comments/db.go b/models/comments/db.go deleted file mode 100644 index 19ff182..0000000 --- a/models/comments/db.go +++ /dev/null @@ -1,66 +0,0 @@ -package comments - -import ( - "crypto/rand" - "encoding/binary" - "strconv" - - "git.aiterp.net/rpdata/api/internal/store" - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo" -) - -var collection *mgo.Collection - -func find(query interface{}) (models.Comment, error) { - comment := models.Comment{} - err := collection.Find(query).One(&comment) - - return comment, err -} - -func list(query interface{}, limit int) ([]models.Comment, error) { - allocSize := 32 - if limit >= 0 { - allocSize = limit - } else { - limit = 0 - } - - comments := make([]models.Comment, 0, allocSize) - err := collection.Find(query).Sort("createdDate").Limit(limit).All(&comments) - if err != nil { - return nil, err - } - - return comments, nil -} - -func makeCommentID() string { - result := "SCC" - offset := 0 - data := make([]byte, 48) - - rand.Read(data) - for len(result) < 32 { - result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36) - offset += 8 - - if offset >= 48 { - rand.Read(data) - offset = 0 - } - } - - return result[:32] -} - -func init() { - store.HandleInit(func(db *mgo.Database) { - collection = db.C("story.comments") - - collection.EnsureIndexKey("chapterId") - collection.EnsureIndexKey("author") - collection.EnsureIndexKey("createdDate") - }) -} diff --git a/models/comments/edit.go b/models/comments/edit.go deleted file mode 100644 index ee20338..0000000 --- a/models/comments/edit.go +++ /dev/null @@ -1,44 +0,0 @@ -package comments - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Edit edits a comment. -func Edit(comment models.Comment, source, characterName, characterID, subject *string, fictionalDate *time.Time) (models.Comment, error) { - changes := make(bson.M, 6) - - comment.EditedDate = time.Now() - changes["editedDate"] = comment.EditedDate - - if source != nil { - comment.Source = *source - changes["source"] = *source - } - if characterName != nil { - comment.CharacterName = *characterName - changes["characterName"] = *characterName - } - if characterID != nil { - comment.CharacterID = *characterID - changes["characterId"] = *characterID - } - if subject != nil { - comment.Subject = *subject - changes["subject"] = *subject - } - if fictionalDate != nil { - comment.FictionalDate = *fictionalDate - changes["fictionalDate"] = *fictionalDate - } - - err := collection.UpdateId(comment.ID, bson.M{"$set": changes}) - if err != nil { - return models.Comment{}, err - } - - return comment, nil -} diff --git a/models/comments/find.go b/models/comments/find.go deleted file mode 100644 index 4238fdc..0000000 --- a/models/comments/find.go +++ /dev/null @@ -1,11 +0,0 @@ -package comments - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Find finds a comment by ID. -func Find(id string) (models.Comment, error) { - return find(bson.M{"_id": id}) -} diff --git a/models/comments/list.go b/models/comments/list.go deleted file mode 100644 index 442effe..0000000 --- a/models/comments/list.go +++ /dev/null @@ -1,11 +0,0 @@ -package comments - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// ListChapterID lists all comments by chapter-ID -func ListChapterID(chapterID string, limit int) ([]models.Comment, error) { - return list(bson.M{"chapterId": chapterID}, limit) -} diff --git a/models/comments/remove.go b/models/comments/remove.go deleted file mode 100644 index db1fe79..0000000 --- a/models/comments/remove.go +++ /dev/null @@ -1,17 +0,0 @@ -package comments - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Remove removes one comment. -func Remove(comment models.Comment) error { - return collection.RemoveId(comment.ID) -} - -// RemoveChapter removes all comments for the given chapter. -func RemoveChapter(chapter models.Chapter) error { - _, err := collection.RemoveAll(bson.M{"chapterId": chapter.ID}) - return err -} diff --git a/models/posts/add-many.go b/models/posts/add-many.go deleted file mode 100644 index e642c9d..0000000 --- a/models/posts/add-many.go +++ /dev/null @@ -1,36 +0,0 @@ -package posts - -import ( - "git.aiterp.net/rpdata/api/internal/counter" - "git.aiterp.net/rpdata/api/models" -) - -// AddMany adds multiple posts in on query. Each post gets a new ID and is associated with the log. -func AddMany(log models.Log, posts []models.Post) ([]models.Post, error) { - docs := make([]interface{}, len(posts)) - copies := make([]models.Post, len(posts)) - - mutex.RLock() - defer mutex.RUnlock() - - startPosition, err := counter.NextMany("next_post_id", log.ShortID, len(posts)) - if err != nil { - return nil, err - } - - for i, post := range posts { - post.ID = generateID(post.Time) - post.LogID = log.ShortID - post.Position = startPosition + i - - docs[i] = post - copies[i] = post - } - - err = collection.Insert(docs...) - if err != nil { - return nil, err - } - - return copies, nil -} diff --git a/models/posts/add.go b/models/posts/add.go deleted file mode 100644 index f59a3db..0000000 --- a/models/posts/add.go +++ /dev/null @@ -1,51 +0,0 @@ -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) -} diff --git a/models/posts/db.go b/models/posts/db.go deleted file mode 100644 index b5153b6..0000000 --- a/models/posts/db.go +++ /dev/null @@ -1,56 +0,0 @@ -package posts - -import ( - "log" - "sync" - - "git.aiterp.net/rpdata/api/internal/store" - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo" -) - -var collection *mgo.Collection -var mutex sync.RWMutex - -func find(query interface{}) (models.Post, error) { - post := models.Post{} - err := collection.Find(query).One(&post) - if err != nil { - return models.Post{}, err - } - - return post, nil -} - -func list(query interface{}, limit int, sort ...string) ([]models.Post, error) { - size := 64 - if limit > 0 { - size = limit - } - posts := make([]models.Post, 0, size) - - err := collection.Find(query).Limit(limit).Sort(sort...).All(&posts) - if err != nil { - return nil, err - } - - return posts, nil -} - -func init() { - store.HandleInit(func(db *mgo.Database) { - collection = db.C("logbot3.posts") - - collection.EnsureIndexKey("logId") - collection.EnsureIndexKey("time") - collection.EnsureIndexKey("kind") - collection.EnsureIndexKey("position") - - err := collection.EnsureIndex(mgo.Index{ - Key: []string{"$text:text"}, - }) - if err != nil { - log.Fatalln("init logbot3.logs:", err) - } - }) -} diff --git a/models/posts/edit.go b/models/posts/edit.go deleted file mode 100644 index 64307c8..0000000 --- a/models/posts/edit.go +++ /dev/null @@ -1,44 +0,0 @@ -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 -} diff --git a/models/posts/find.go b/models/posts/find.go deleted file mode 100644 index c7c8ed4..0000000 --- a/models/posts/find.go +++ /dev/null @@ -1,14 +0,0 @@ -package posts - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// FindID finds a log post by ID. -func FindID(id string) (models.Post, error) { - mutex.RLock() - defer mutex.RUnlock() - - return find(bson.M{"_id": id}) -} diff --git a/models/posts/list.go b/models/posts/list.go deleted file mode 100644 index b69fb87..0000000 --- a/models/posts/list.go +++ /dev/null @@ -1,57 +0,0 @@ -package posts - -import ( - "strings" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Filter is used to generate a query to the database. -type Filter struct { - ID []string - Kind []string - LogID *string - Search *string - Limit int -} - -// List lists the posts according to the filter -func List(filter *Filter) ([]models.Post, error) { - mutex.RLock() - defer mutex.RUnlock() - - limit := 256 - query := bson.M{} - - if filter != nil { - if filter.LogID != nil { - query["logId"] = filter.LogID - } - - if len(filter.ID) > 1 { - query["_id"] = bson.M{"$in": filter.ID} - } else if len(filter.ID) == 1 { - query["_id"] = filter.ID[0] - } - - if len(filter.Kind) > 1 { - for i := range filter.Kind { - filter.Kind[i] = strings.ToLower(filter.Kind[i]) - } - - query["kind"] = bson.M{"$in": filter.Kind} - } else if len(filter.Kind) == 1 { - query["kind"] = strings.ToLower(filter.Kind[0]) - } - - limit = filter.Limit - } - - posts, err := list(query, limit, "position") - if err != nil { - return nil, err - } - - return posts, nil -} diff --git a/models/posts/move.go b/models/posts/move.go deleted file mode 100644 index 82a8f3f..0000000 --- a/models/posts/move.go +++ /dev/null @@ -1,69 +0,0 @@ -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 -} diff --git a/models/posts/remove.go b/models/posts/remove.go deleted file mode 100644 index 2bc9e18..0000000 --- a/models/posts/remove.go +++ /dev/null @@ -1,33 +0,0 @@ -package posts - -import ( - "git.aiterp.net/rpdata/api/internal/counter" - "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 - } - - counter.NextMany("next_post_id", post.LogID, -1) - - return post, nil -} - -// RemoveAllInLog removes all posts for the given log. -func RemoveAllInLog(log models.Log) error { - _, err := collection.RemoveAll(bson.M{"logId": log.ShortID}) - return err -} diff --git a/models/stories/add-tag.go b/models/stories/add-tag.go deleted file mode 100644 index 55a189e..0000000 --- a/models/stories/add-tag.go +++ /dev/null @@ -1,29 +0,0 @@ -package stories - -import ( - "errors" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// ErrTagAlreadyExists is an error returned by Story.AddTag -var ErrTagAlreadyExists = errors.New("Tag already exists on story") - -// AddTag adds a tag to the story. It returns ErrTagAlreadyExists if the tag is already there -func AddTag(story models.Story, tag models.Tag) (models.Story, error) { - for i := range story.Tags { - if story.Tags[i].Equal(tag) { - return models.Story{}, ErrTagAlreadyExists - } - } - - err := collection.UpdateId(story.ID, bson.M{"$push": bson.M{"tags": tag}}) - if err != nil { - return models.Story{}, err - } - - story.Tags = append(story.Tags, tag) - - return story, nil -} diff --git a/models/stories/add.go b/models/stories/add.go deleted file mode 100644 index 9d0b4bd..0000000 --- a/models/stories/add.go +++ /dev/null @@ -1,30 +0,0 @@ -package stories - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" -) - -// Add creates a new story. -func Add(name, author string, category models.StoryCategory, listed, open bool, tags []models.Tag, createdDate, fictionalDate time.Time) (models.Story, error) { - story := models.Story{ - ID: makeStoryID(), - Name: name, - Author: author, - Category: category, - Listed: listed, - Open: open, - Tags: tags, - CreatedDate: createdDate, - FictionalDate: fictionalDate, - UpdatedDate: createdDate, - } - - err := collection.Insert(story) - if err != nil { - return models.Story{}, err - } - - return story, nil -} diff --git a/models/stories/db.go b/models/stories/db.go deleted file mode 100644 index 1a48556..0000000 --- a/models/stories/db.go +++ /dev/null @@ -1,59 +0,0 @@ -package stories - -import ( - "crypto/rand" - "encoding/binary" - "strconv" - - "git.aiterp.net/rpdata/api/internal/store" - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo" -) - -var collection *mgo.Collection - -func find(query interface{}) (models.Story, error) { - story := models.Story{} - err := collection.Find(query).One(&story) - - return story, err -} - -func list(query interface{}, limit int) ([]models.Story, error) { - stories := make([]models.Story, 0, 64) - err := collection.Find(query).Limit(limit).Sort("-updatedDate").All(&stories) - - return stories, err -} - -// makeStoryID makes a random story ID that's 16 characters long -func makeStoryID() string { - result := "S" - offset := 0 - data := make([]byte, 32) - - rand.Read(data) - for len(result) < 16 { - result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36) - offset += 8 - - if offset >= 32 { - rand.Read(data) - offset = 0 - } - } - - return result[:16] -} - -func init() { - store.HandleInit(func(db *mgo.Database) { - collection = db.C("story.stories") - - collection.EnsureIndexKey("tags") - collection.EnsureIndexKey("author") - collection.EnsureIndexKey("updatedDate") - collection.EnsureIndexKey("fictionalDate") - collection.EnsureIndexKey("listed") - }) -} diff --git a/models/stories/edit.go b/models/stories/edit.go deleted file mode 100644 index fae96c8..0000000 --- a/models/stories/edit.go +++ /dev/null @@ -1,45 +0,0 @@ -package stories - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Edit edits the story and returns the edited story if it succeeds. -func Edit(story models.Story, name *string, category *models.StoryCategory, listed, open *bool, fictionalDate *time.Time) (models.Story, error) { - changes := bson.M{} - - if name != nil && *name != story.Name { - changes["name"] = *name - story.Name = *name - } - if category != nil && *category != story.Category { - changes["category"] = *category - story.Category = *category - } - if listed != nil && *listed != story.Listed { - changes["listed"] = *listed - story.Listed = *listed - } - if open != nil && *open != story.Open { - changes["open"] = *open - story.Open = *open - } - if fictionalDate != nil && !fictionalDate.Equal(story.FictionalDate) { - changes["fictionalDate"] = *fictionalDate - story.FictionalDate = *fictionalDate - } - - if len(changes) == 0 { - return story, nil - } - - err := collection.UpdateId(story.ID, bson.M{"$set": changes}) - if err != nil { - return models.Story{}, err - } - - return story, nil -} diff --git a/models/stories/find.go b/models/stories/find.go deleted file mode 100644 index a48e472..0000000 --- a/models/stories/find.go +++ /dev/null @@ -1,11 +0,0 @@ -package stories - -import ( - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// FindID finds a story by ID -func FindID(id string) (models.Story, error) { - return find(bson.M{"_id": id}) -} diff --git a/models/stories/list.go b/models/stories/list.go deleted file mode 100644 index bed4bad..0000000 --- a/models/stories/list.go +++ /dev/null @@ -1,67 +0,0 @@ -package stories - -import ( - "time" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// Filter for stories.List -type Filter struct { - Author *string - Tags []models.Tag - EarliestFictionalDate time.Time - LatestFictionalDate time.Time - Category *models.StoryCategory - Open *bool - Unlisted *bool - Limit int -} - -// List lists stories by any non-zero criteria passed with it. -func List(filter *Filter) ([]models.Story, error) { - query := bson.M{"listed": true} - limit := 0 - - if filter != nil { - if filter.Author != nil { - query["author"] = *filter.Author - } - - if len(filter.Tags) > 0 { - query["tags"] = bson.M{"$in": filter.Tags} - } - - if !filter.EarliestFictionalDate.IsZero() && !filter.LatestFictionalDate.IsZero() { - query["fictionalDate"] = bson.M{ - "$gte": filter.EarliestFictionalDate, - "$lt": filter.LatestFictionalDate, - } - } else if !filter.LatestFictionalDate.IsZero() { - query["fictionalDate"] = bson.M{ - "$lt": filter.LatestFictionalDate, - } - } else if !filter.EarliestFictionalDate.IsZero() { - query["fictionalDate"] = bson.M{ - "$gte": filter.EarliestFictionalDate, - } - } - - if filter.Category != nil { - query["category"] = *filter.Category - } - - if filter.Open != nil { - query["open"] = *filter.Open - } - - if filter.Unlisted != nil { - query["listed"] = !*filter.Unlisted - } - - limit = filter.Limit - } - - return list(query, limit) -} diff --git a/models/stories/remove-tag.go b/models/stories/remove-tag.go deleted file mode 100644 index ec7300b..0000000 --- a/models/stories/remove-tag.go +++ /dev/null @@ -1,34 +0,0 @@ -package stories - -import ( - "errors" - - "git.aiterp.net/rpdata/api/models" - "github.com/globalsign/mgo/bson" -) - -// ErrTagNotExists is an error returned by Story.RemoveTag -var ErrTagNotExists = errors.New("Tag does not exist on story") - -// RemoveTag removes a tag to the story. It returns ErrTagNotExists if the tag does not exist. -func RemoveTag(story models.Story, tag models.Tag) (models.Story, error) { - index := -1 - for i := range story.Tags { - if story.Tags[i].Equal(tag) { - index = i - break - } - } - if index == -1 { - return models.Story{}, ErrTagNotExists - } - - err := collection.UpdateId(story.ID, bson.M{"$pull": bson.M{"tags": tag}}) - if err != nil { - return models.Story{}, err - } - - story.Tags = append(story.Tags[:index], story.Tags[index+1:]...) - - return story, nil -} diff --git a/models/stories/remove.go b/models/stories/remove.go deleted file mode 100644 index 7f2a157..0000000 --- a/models/stories/remove.go +++ /dev/null @@ -1,10 +0,0 @@ -package stories - -import ( - "git.aiterp.net/rpdata/api/models" -) - -// Remove the story from the database -func Remove(story models.Story) (models.Story, error) { - return story, collection.RemoveId(story.ID) -}