package mongodb import ( "context" "errors" "git.aiterp.net/rpdata/api/internal/generate" "git.aiterp.net/rpdata/api/models" "github.com/globalsign/mgo" "github.com/globalsign/mgo/bson" "strconv" "sync" ) type logRepository struct { openMutex sync.Mutex restoreIds bool logs *mgo.Collection posts *mgo.Collection shortIdCounter *counter } func newLogRepository(db *mgo.Database, restoreIds bool) (*logRepository, error) { logs := db.C("logbot3.logs") posts := db.C("logbot3.posts") err := logs.EnsureIndexKey("date") if err != nil { return nil, err } err = logs.EnsureIndexKey("channel") if err != nil { return nil, err } err = logs.EnsureIndexKey("characterIds") if err != nil { return nil, err } err = logs.EnsureIndexKey("event") if err != nil { return nil, err } err = logs.EnsureIndex(mgo.Index{Key: []string{"channel", "open"}}) if err != nil { return nil, err } err = logs.EnsureIndex(mgo.Index{ Key: []string{"shortId"}, Unique: true, DropDups: true, }) if err != nil { return nil, err } return &logRepository{ restoreIds: restoreIds, logs: logs, posts: posts, shortIdCounter: newCounter(db, "auto_increment", "Log"), }, nil } func (r *logRepository) Find(ctx context.Context, id string) (*models.Log, error) { log := new(models.Log) err := r.logs.Find(bson.M{"$or": []bson.M{{"_id": id}, {"shortId": id}}}).One(log) if err != nil { return nil, err } return log, nil } func (r *logRepository) List(ctx context.Context, filter models.LogFilter) ([]*models.Log, error) { query := bson.M{} if filter.Search != nil { searchQuery := bson.M{ "$text": bson.M{"$search": *filter.Search}, "logId": bson.M{"$ne": nil}, } logIds := make([]string, 0, 64) err := r.posts.Find(searchQuery).Distinct("logId", &logIds) if err != nil { return nil, err } query["shortId"] = bson.M{"$in": logIds} } if filter.Open != nil { r.openMutex.Lock() defer r.openMutex.Unlock() query["open"] = filter.Open } if len(filter.Characters) > 0 { query["characterIds"] = bson.M{"$all": filter.Characters} } if len(filter.Channels) > 0 { query["channel"] = bson.M{"$in": filter.Channels} } if len(filter.Events) > 0 { query["event"] = bson.M{"$in": filter.Events} } if filter.MinDate != nil && filter.MaxDate != nil { query["date"] = bson.M{"$gte": *filter.MinDate, "$lte": *filter.MaxDate} } else if filter.MinDate != nil { query["date"] = bson.M{"$gte": *filter.MinDate} } else if filter.MaxDate != nil { query["date"] = bson.M{"$lt": *filter.MaxDate} } logs := make([]*models.Log, 0, 32) err := r.logs.Find(query).Sort("-date").Limit(filter.Limit).All(&logs) if err != nil { if err == mgo.ErrNotFound { return logs, nil } return nil, err } return logs, nil } func (r *logRepository) Insert(ctx context.Context, log models.Log) (*models.Log, error) { if !r.restoreIds || log.ID == "" || log.ShortID == "" { nextShortId, err := r.shortIdCounter.Increment(1) if err != nil { return nil, err } log.ID = generate.LogID(log) log.ShortID = "L" + strconv.Itoa(nextShortId) } else { n, err := strconv.Atoi(log.ShortID[1:]) if err != nil { return nil, err } _ = r.shortIdCounter.Bump(n) } if log.Open { // There can be only one open log in the same channel. r.openMutex.Lock() defer r.openMutex.Unlock() _, err := r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}}) if err != nil { return nil, errors.New("Cannot close other logs: " + err.Error()) } } err := r.logs.Insert(&log) if err != nil { return nil, err } return &log, nil } func (r *logRepository) Update(ctx context.Context, log models.Log, update models.LogUpdate) (*models.Log, error) { updateBson := bson.M{} if update.Open != nil { if *update.Open == true { // There can be only one open log in the same channel. r.openMutex.Lock() defer r.openMutex.Unlock() _, err := r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}}) if err != nil { return nil, errors.New("Cannot close other logs: " + err.Error()) } } updateBson["open"] = *update.Open log.Open = *update.Open } if update.Title != nil { updateBson["title"] = *update.Title log.Title = *update.Title } if update.Description != nil { updateBson["description"] = *update.Description log.Description = *update.Description } if update.EventName != nil { updateBson["event"] = *update.EventName log.EventName = *update.EventName } if update.CharacterIDs != nil { updateBson["characterIds"] = update.CharacterIDs log.CharacterIDs = update.CharacterIDs } err := r.logs.UpdateId(log.ID, bson.M{"$set": updateBson}) if err != nil { return nil, err } return &log, nil } func (r *logRepository) Delete(ctx context.Context, log models.Log) error { err := r.logs.RemoveId(log.ID) if err != nil { return err } _, _ = r.posts.RemoveAll(bson.M{"logId": log.ShortID}) return nil }