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.

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