GraphQL API and utilities for the rpdata project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
4.7 KiB

  1. package mongodb
  2. import (
  3. "context"
  4. "errors"
  5. "git.aiterp.net/rpdata/api/internal/generate"
  6. "git.aiterp.net/rpdata/api/models"
  7. "github.com/globalsign/mgo"
  8. "github.com/globalsign/mgo/bson"
  9. "strconv"
  10. "sync"
  11. )
  12. type logRepository struct {
  13. openMutex sync.Mutex
  14. logs *mgo.Collection
  15. posts *mgo.Collection
  16. shortIdCounter *counter
  17. }
  18. func newLogRepository(db *mgo.Database) (*logRepository, error) {
  19. logs := db.C("logbot3.logs")
  20. posts := db.C("logbot3.posts")
  21. err := logs.EnsureIndexKey("date")
  22. if err != nil {
  23. return nil, err
  24. }
  25. err = logs.EnsureIndexKey("channel")
  26. if err != nil {
  27. return nil, err
  28. }
  29. err = logs.EnsureIndexKey("characterIds")
  30. if err != nil {
  31. return nil, err
  32. }
  33. err = logs.EnsureIndexKey("event")
  34. if err != nil {
  35. return nil, err
  36. }
  37. err = logs.EnsureIndex(mgo.Index{Key: []string{"channel", "open"}})
  38. if err != nil {
  39. return nil, err
  40. }
  41. err = logs.EnsureIndex(mgo.Index{
  42. Key: []string{"shortId"},
  43. Unique: true,
  44. DropDups: true,
  45. })
  46. if err != nil {
  47. return nil, err
  48. }
  49. return &logRepository{
  50. logs: logs,
  51. posts: posts,
  52. shortIdCounter: newCounter(db, "auto_increment", "Log"),
  53. }, nil
  54. }
  55. func (r *logRepository) Find(ctx context.Context, id string) (*models.Log, error) {
  56. log := new(models.Log)
  57. err := r.logs.Find(bson.M{"$or": []bson.M{{"_id": id}, {"shortId": id}}}).One(log)
  58. if err != nil {
  59. return nil, err
  60. }
  61. return log, nil
  62. }
  63. func (r *logRepository) List(ctx context.Context, filter models.LogFilter) ([]*models.Log, error) {
  64. query := bson.M{}
  65. if filter.Search != nil {
  66. searchQuery := bson.M{
  67. "$text": bson.M{"$search": *filter.Search},
  68. "logId": bson.M{"$ne": nil},
  69. }
  70. logIds := make([]string, 0, 64)
  71. err := r.posts.Find(searchQuery).Distinct("logId", &logIds)
  72. if err != nil {
  73. return nil, err
  74. }
  75. query["shortId"] = bson.M{"$in": logIds}
  76. }
  77. if filter.Open != nil {
  78. r.openMutex.Lock()
  79. defer r.openMutex.Unlock()
  80. query["open"] = filter.Open
  81. }
  82. if len(filter.Characters) > 0 {
  83. query["characterIds"] = bson.M{"$all": filter.Characters}
  84. }
  85. if len(filter.Channels) > 0 {
  86. query["channel"] = bson.M{"$in": filter.Channels}
  87. }
  88. if len(filter.Events) > 0 {
  89. query["event"] = bson.M{"$in": filter.Events}
  90. }
  91. if filter.MinDate != nil && filter.MaxDate != nil {
  92. query["date"] = bson.M{"$gte": *filter.MinDate, "$lte": *filter.MaxDate}
  93. } else if filter.MinDate != nil {
  94. query["date"] = bson.M{"$gte": *filter.MinDate}
  95. } else if filter.MaxDate != nil {
  96. query["date"] = bson.M{"$lt": *filter.MaxDate}
  97. }
  98. logs := make([]*models.Log, 0, 32)
  99. err := r.logs.Find(query).Sort("-date").Limit(filter.Limit).All(&logs)
  100. if err != nil {
  101. if err == mgo.ErrNotFound {
  102. return logs, nil
  103. }
  104. return nil, err
  105. }
  106. return logs, nil
  107. }
  108. func (r *logRepository) Insert(ctx context.Context, log models.Log) (*models.Log, error) {
  109. nextShortId, err := r.shortIdCounter.Increment(1)
  110. if err != nil {
  111. return nil, err
  112. }
  113. log.ID = generate.LogID(log)
  114. log.ShortID = "L" + strconv.Itoa(nextShortId)
  115. if log.Open {
  116. // There can be only one open log in the same channel.
  117. r.openMutex.Lock()
  118. defer r.openMutex.Unlock()
  119. _, err = r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}})
  120. if err != nil {
  121. return nil, errors.New("Cannot close other logs: " + err.Error())
  122. }
  123. }
  124. err = r.logs.Insert(&log)
  125. if err != nil {
  126. return nil, err
  127. }
  128. return &log, nil
  129. }
  130. func (r *logRepository) Update(ctx context.Context, log models.Log, update models.LogUpdate) (*models.Log, error) {
  131. updateBson := bson.M{}
  132. if update.Open != nil {
  133. if *update.Open == true {
  134. // There can be only one open log in the same channel.
  135. r.openMutex.Lock()
  136. defer r.openMutex.Unlock()
  137. _, err := r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}})
  138. if err != nil {
  139. return nil, errors.New("Cannot close other logs: " + err.Error())
  140. }
  141. }
  142. updateBson["open"] = *update.Open
  143. log.Open = *update.Open
  144. }
  145. if update.Title != nil {
  146. updateBson["title"] = *update.Title
  147. log.Title = *update.Title
  148. }
  149. if update.Description != nil {
  150. updateBson["description"] = *update.Description
  151. log.Description = *update.Description
  152. }
  153. if update.EventName != nil {
  154. updateBson["event"] = *update.EventName
  155. log.EventName = *update.EventName
  156. }
  157. if update.CharacterIDs != nil {
  158. updateBson["characterIds"] = update.CharacterIDs
  159. log.CharacterIDs = update.CharacterIDs
  160. }
  161. err := r.logs.UpdateId(log.ID, bson.M{"$set": updateBson})
  162. if err != nil {
  163. return nil, err
  164. }
  165. return &log, nil
  166. }
  167. func (r *logRepository) Delete(ctx context.Context, log models.Log) error {
  168. err := r.logs.RemoveId(log.ID)
  169. if err != nil {
  170. return err
  171. }
  172. _, _ = r.posts.RemoveAll(bson.M{"logId": log.ShortID})
  173. return nil
  174. }