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.

197 lines
4.4 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. logs := make([]*models.Log, 0, 32)
  92. err := r.logs.Find(query).Sort("-date").Limit(filter.Limit).All(&logs)
  93. if err != nil {
  94. if err == mgo.ErrNotFound {
  95. return logs, nil
  96. }
  97. return nil, err
  98. }
  99. return logs, nil
  100. }
  101. func (r *logRepository) Insert(ctx context.Context, log models.Log) (*models.Log, error) {
  102. nextShortId, err := r.shortIdCounter.Increment(1)
  103. if err != nil {
  104. return nil, err
  105. }
  106. log.ID = generate.LogID(log)
  107. log.ShortID = "L" + strconv.Itoa(nextShortId)
  108. if log.Open {
  109. // There can be only one open log in the same channel.
  110. r.openMutex.Lock()
  111. defer r.openMutex.Unlock()
  112. _, err = r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}})
  113. if err != nil {
  114. return nil, errors.New("Cannot close other logs: " + err.Error())
  115. }
  116. }
  117. err = r.logs.Insert(&log)
  118. if err != nil {
  119. return nil, err
  120. }
  121. return &log, nil
  122. }
  123. func (r *logRepository) Update(ctx context.Context, log models.Log, update models.LogUpdate) (*models.Log, error) {
  124. updateBson := bson.M{}
  125. if update.Open != nil {
  126. if *update.Open == true {
  127. // There can be only one open log in the same channel.
  128. r.openMutex.Lock()
  129. defer r.openMutex.Unlock()
  130. _, err := r.logs.UpdateAll(bson.M{"channel": log.ChannelName, "open": true}, bson.M{"$set": bson.M{"open": false}})
  131. if err != nil {
  132. return nil, errors.New("Cannot close other logs: " + err.Error())
  133. }
  134. }
  135. updateBson["open"] = *update.Open
  136. log.Open = *update.Open
  137. }
  138. if update.Title != nil {
  139. updateBson["title"] = *update.Title
  140. log.Title = *update.Title
  141. }
  142. if update.Description != nil {
  143. updateBson["description"] = *update.Description
  144. log.Description = *update.Description
  145. }
  146. if update.EventName != nil {
  147. updateBson["event"] = *update.EventName
  148. log.EventName = *update.EventName
  149. }
  150. if update.CharacterIDs != nil {
  151. updateBson["characterIds"] = update.CharacterIDs
  152. log.CharacterIDs = update.CharacterIDs
  153. }
  154. err := r.logs.UpdateId(log.ID, bson.M{"$set": updateBson})
  155. if err != nil {
  156. return nil, err
  157. }
  158. return &log, nil
  159. }
  160. func (r *logRepository) Delete(ctx context.Context, log models.Log) error {
  161. err := r.logs.RemoveId(log.ID)
  162. if err != nil {
  163. return err
  164. }
  165. _, _ = r.posts.RemoveAll(bson.M{"logId": log.ShortID})
  166. return nil
  167. }