|
|
package log
import ( "crypto/rand" "encoding/binary" "errors" "log" "strconv" "time"
"git.aiterp.net/rpdata/api/internal/store" "github.com/globalsign/mgo" "github.com/globalsign/mgo/bson" )
var postCollection *mgo.Collection
// A Post is a part of a log file.
type Post struct { ID string `bson:"_id"` LogID string `bson:"logId"` Time time.Time `bson:"time"` Kind string `bson:"kind"` Nick string `bson:"nick"` Text string `bson:"text"` Index int `bson:"index"` }
// Edit the post
func (post *Post) Edit(time *time.Time, kind *string, nick *string, text *string) error { changes := bson.M{} changed := false postCopy := *post
if time != nil && !time.IsZero() && !time.Equal(post.Time) { changes["time"] = *time changed = true postCopy.Time = *time } if kind != nil && *kind != "" && *kind != post.Kind { changes["kind"] = *kind changed = true postCopy.Kind = *kind } if nick != nil && *nick != "" && *nick != post.Nick { changes["nick"] = *nick changed = true postCopy.Nick = *nick } if text != nil && *text != "" && *text != post.Text { changes["text"] = *text changed = true postCopy.Text = *text }
if !changed { return nil }
err := postCollection.UpdateId(post.ID, bson.M{"$set": changes}) if err != nil { return err }
*post = postCopy return nil }
// Move the post
func (post *Post) Move(targetIndex int) error { if targetIndex < 1 { return errors.New("Invalid index") }
postMutex.Lock() defer postMutex.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 index.
if targetIndex > 1 { existingPost := Post{} err := postCollection.Find(bson.M{"logId": post.LogID, "index": targetIndex}).One(&existingPost)
if err != nil || existingPost.Index != targetIndex { return errors.New("No post found at the index") } }
query := bson.M{"logId": post.LogID} operation := bson.M{"$inc": bson.M{"index": 1}}
if targetIndex < post.Index { query["$and"] = []bson.M{ bson.M{"index": bson.M{"$gte": targetIndex}}, bson.M{"index": bson.M{"$lt": post.Index}}, } } else { query["$and"] = []bson.M{ bson.M{"index": bson.M{"$gt": post.Index}}, bson.M{"index": bson.M{"$lte": targetIndex}}, }
operation["$inc"] = bson.M{"index": -1} }
_, err := postCollection.UpdateAll(query, operation) if err != nil { return errors.New("moving others: " + err.Error()) }
err = postCollection.UpdateId(post.ID, bson.M{"$set": bson.M{"index": targetIndex}}) if err != nil { return errors.New("moving: " + err.Error()) }
post.Index = targetIndex
return nil }
// FindPostID finds a log post by ID.
func FindPostID(id string) (Post, error) { return findPost(bson.M{"_id": id}) }
// ListPostIDs lists log posts by ID
func ListPostIDs(ids ...string) ([]Post, error) { return listPosts(bson.M{"_id": bson.M{"$in": ids}}) }
// RemovePost removes a post, moving all subsequent post up one index
func RemovePost(id string) (Post, error) { postMutex.Lock() defer postMutex.Unlock()
post, err := findPost(bson.M{"_id": id}) if err != nil { return Post{}, err }
err = postCollection.RemoveId(id) if err != nil { return Post{}, err }
_, err = postCollection.UpdateAll(bson.M{"logId": post.LogID, "index": bson.M{"$gt": post.Index}}, bson.M{"$inc": bson.M{"index": -1}}) if err != nil { return Post{}, err }
return post, nil }
func findPost(query interface{}) (Post, error) { post := Post{} err := postCollection.Find(query).One(&post) if err != nil { return Post{}, err }
return post, nil }
func listPosts(query interface{}) ([]Post, error) { posts := make([]Post, 0, 64) err := postCollection.Find(query).All(&posts) if err != nil { return nil, err }
return posts, nil }
// MakePostID makes a random post ID
func MakePostID(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) }
func init() { store.HandleInit(func(db *mgo.Database) { postCollection = db.C("logbot3.posts")
postCollection.EnsureIndexKey("logId") postCollection.EnsureIndexKey("time") postCollection.EnsureIndexKey("kind") postCollection.EnsureIndexKey("index")
err := postCollection.EnsureIndex(mgo.Index{ Key: []string{"$text:text"}, }) if err != nil { log.Fatalln("init logbot3.logs:", err) } }) }
|