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.

341 lines
8.1 KiB

  1. package services
  2. import (
  3. "context"
  4. "errors"
  5. "git.aiterp.net/rpdata/api/internal/auth"
  6. "git.aiterp.net/rpdata/api/models"
  7. "git.aiterp.net/rpdata/api/models/changekeys"
  8. "git.aiterp.net/rpdata/api/models/channels"
  9. "git.aiterp.net/rpdata/api/repositories"
  10. "git.aiterp.net/rpdata/api/services/parsers"
  11. "time"
  12. )
  13. type LogService struct {
  14. logs repositories.LogRepository
  15. posts repositories.PostRepository
  16. changeService *ChangeService
  17. channelService *ChannelService
  18. }
  19. func (s *LogService) Find(ctx context.Context, id string) (*models.Log, error) {
  20. return s.logs.Find(ctx, id)
  21. }
  22. func (s *LogService) FindPosts(ctx context.Context, id string) (*models.Post, error) {
  23. return s.posts.Find(ctx, id)
  24. }
  25. func (s *LogService) List(ctx context.Context, filter *models.LogFilter) ([]*models.Log, error) {
  26. if filter == nil {
  27. filter = &models.LogFilter{}
  28. }
  29. return s.logs.List(ctx, *filter)
  30. }
  31. func (s *LogService) ListPosts(ctx context.Context, filter *models.PostFilter) ([]*models.Post, error) {
  32. // Some sanity checks to avoid querying an insame amount of posts.
  33. if filter == nil {
  34. filter = &models.PostFilter{Limit: 100}
  35. } else {
  36. if (filter.Limit <= 0 || filter.Limit > 512) && (filter.LogID == nil && len(filter.IDs) == 0) {
  37. return nil, errors.New("a limit of 0 (no limit) or >512 without a logId or a set of IDs is not allowed")
  38. }
  39. if len(filter.IDs) > 100 {
  40. return nil, errors.New("you may not query for more than 100 ids, split your query")
  41. }
  42. }
  43. return s.posts.List(ctx, *filter)
  44. }
  45. func (s *LogService) Create(ctx context.Context, title, description, channelName, eventName string, open bool) (*models.Log, error) {
  46. if channelName == "" {
  47. return nil, errors.New("channel name cannot be empty")
  48. }
  49. log := &models.Log{
  50. Title: title,
  51. Description: description,
  52. ChannelName: channelName,
  53. EventName: eventName,
  54. Date: time.Now(),
  55. Open: open,
  56. }
  57. if err := auth.CheckPermission(ctx, "add", log); err != nil {
  58. return nil, err
  59. }
  60. _, err := s.channelService.Ensure(ctx, channelName)
  61. if err != nil {
  62. return nil, err
  63. }
  64. log, err = s.logs.Insert(ctx, *log)
  65. if err != nil {
  66. return nil, err
  67. }
  68. s.changeService.Submit(ctx, models.ChangeModelLog, "add", true, changekeys.Listed(log), log)
  69. return log, nil
  70. }
  71. // Import creates new logs from common formats.
  72. func (s *LogService) Import(ctx context.Context, importer models.LogImporter, date time.Time, tz *time.Location, channelName string, data string) ([]*models.Log, error) {
  73. if err := auth.CheckPermission(ctx, "add", &models.Log{}); err != nil {
  74. return nil, err
  75. }
  76. results := make([]*models.Log, 0, 8)
  77. eventName := ""
  78. if channel, err := channels.FindName(channelName); err != nil {
  79. eventName = channel.EventName
  80. }
  81. _, err := s.channelService.Ensure(ctx, channelName)
  82. if err != nil {
  83. return nil, err
  84. }
  85. date = date.In(tz)
  86. switch importer {
  87. case models.LogImporterMircLike:
  88. {
  89. if date.IsZero() {
  90. return nil, errors.New("date is not optional for mirc-like logs")
  91. }
  92. parsed, err := parsers.MircLog(data, date, true)
  93. if err != nil {
  94. return nil, err
  95. }
  96. parsed.Log.EventName = eventName
  97. parsed.Log.ChannelName = channelName
  98. log, err := s.logs.Insert(ctx, parsed.Log)
  99. if err != nil {
  100. return nil, err
  101. }
  102. for _, post := range parsed.Posts {
  103. post.LogID = log.ShortID
  104. }
  105. posts, err := s.posts.InsertMany(ctx, parsed.Posts...)
  106. if err != nil {
  107. return nil, err
  108. }
  109. s.changeService.Submit(ctx, models.ChangeModelLog, "add", true, changekeys.Listed(log), log)
  110. s.changeService.Submit(ctx, models.ChangeModelPost, "add", true, changekeys.Listed(log, posts), log, posts)
  111. results = append(results, log)
  112. }
  113. case models.LogImporterForumLog:
  114. {
  115. parseResults, err := parsers.ForumLog(data, tz)
  116. if err != nil {
  117. return nil, err
  118. }
  119. for _, parsed := range parseResults {
  120. log, err := s.logs.Insert(ctx, parsed.Log)
  121. if err != nil {
  122. return nil, err
  123. }
  124. parsed.Log.EventName = eventName
  125. parsed.Log.ChannelName = channelName
  126. for _, post := range parsed.Posts {
  127. post.LogID = log.ShortID
  128. }
  129. posts, err := s.posts.InsertMany(ctx, parsed.Posts...)
  130. if err != nil {
  131. return nil, err
  132. }
  133. s.changeService.Submit(ctx, models.ChangeModelLog, "add", true, changekeys.Listed(log), log)
  134. s.changeService.Submit(ctx, models.ChangeModelPost, "add", true, changekeys.Listed(log, posts), log, posts)
  135. }
  136. }
  137. default:
  138. {
  139. return nil, errors.New("Invalid importer: " + importer.String())
  140. }
  141. }
  142. return results, nil
  143. }
  144. func (s *LogService) Update(ctx context.Context, id string, update models.LogUpdate) (*models.Log, error) {
  145. log, err := s.logs.Find(ctx, id)
  146. if err != nil {
  147. return nil, err
  148. }
  149. if err := auth.CheckPermission(ctx, "edit", log); err != nil {
  150. return nil, err
  151. }
  152. log, err = s.logs.Update(ctx, *log, update)
  153. if err != nil {
  154. return nil, err
  155. }
  156. s.changeService.Submit(ctx, models.ChangeModelLog, "edit", true, changekeys.Listed(log), log)
  157. return log, nil
  158. }
  159. func (s *LogService) AddPost(ctx context.Context, logId string, time time.Time, kind, nick, text string) (*models.Post, error) {
  160. if kind == "" || nick == "" || time.IsZero() {
  161. return nil, errors.New("kind, nick and time must be non-empty")
  162. }
  163. log, err := s.logs.Find(ctx, logId)
  164. if err != nil {
  165. return nil, err
  166. }
  167. post := &models.Post{
  168. LogID: log.ShortID,
  169. Kind: kind,
  170. Nick: nick,
  171. Text: text,
  172. Time: time,
  173. }
  174. if err := auth.CheckPermission(ctx, "add", post); err != nil {
  175. return nil, err
  176. }
  177. post, err = s.posts.Insert(ctx, *post)
  178. if err != nil {
  179. return nil, err
  180. }
  181. s.changeService.Submit(ctx, models.ChangeModelPost, "add", true, changekeys.Many(log, post), log, post)
  182. return post, nil
  183. }
  184. func (s *LogService) EditPost(ctx context.Context, id string, update models.PostUpdate) (*models.Post, error) {
  185. if (update.Kind != nil && *update.Kind == "") || (update.Nick != nil && *update.Nick == "") || (update.Text != nil && *update.Text == "") {
  186. return nil, errors.New("kind, nick and time must be non-empty")
  187. }
  188. post, err := s.posts.Find(ctx, id)
  189. if err != nil {
  190. return nil, err
  191. }
  192. if err := auth.CheckPermission(ctx, "edit", post); err != nil {
  193. return nil, err
  194. }
  195. post, err = s.posts.Update(ctx, *post, update)
  196. if err != nil {
  197. return nil, err
  198. }
  199. go func() {
  200. log, err := s.logs.Find(context.Background(), post.LogID)
  201. if err != nil {
  202. return
  203. }
  204. s.changeService.Submit(ctx, models.ChangeModelPost, "edit", true, changekeys.Many(log, post), post)
  205. }()
  206. return post, nil
  207. }
  208. func (s *LogService) MovePost(ctx context.Context, id string, position int) ([]*models.Post, error) {
  209. if position < 1 {
  210. return nil, repositories.ErrInvalidPosition
  211. }
  212. post, err := s.posts.Find(ctx, id)
  213. if err != nil {
  214. return nil, err
  215. }
  216. if err := auth.CheckPermission(ctx, "move", post); err != nil {
  217. return nil, err
  218. }
  219. posts, err := s.posts.Move(ctx, *post, position)
  220. if err != nil {
  221. return nil, err
  222. }
  223. go func() {
  224. if len(posts) == 0 {
  225. return
  226. }
  227. log, err := s.logs.Find(context.Background(), posts[0].LogID)
  228. if err != nil {
  229. return
  230. }
  231. s.changeService.Submit(ctx, models.ChangeModelPost, "move", true, changekeys.Many(log, posts), posts)
  232. }()
  233. return posts, nil
  234. }
  235. func (s *LogService) DeletePost(ctx context.Context, id string) (*models.Post, error) {
  236. post, err := s.posts.Find(ctx, id)
  237. if err != nil {
  238. return nil, err
  239. }
  240. if err := auth.CheckPermission(ctx, "remove", post); err != nil {
  241. return nil, err
  242. }
  243. err = s.posts.Delete(ctx, *post)
  244. if err != nil {
  245. return nil, err
  246. }
  247. go func() {
  248. log, err := s.logs.Find(context.Background(), post.LogID)
  249. if err != nil {
  250. return
  251. }
  252. s.changeService.Submit(ctx, models.ChangeModelPost, "remove", true, changekeys.Many(log, post), post)
  253. }()
  254. return post, nil
  255. }
  256. func (s *LogService) Delete(ctx context.Context, id string) (*models.Log, error) {
  257. log, err := s.logs.Find(ctx, id)
  258. if err != nil {
  259. return nil, err
  260. }
  261. if err := auth.CheckPermission(ctx, "remove", log); err != nil {
  262. return nil, err
  263. }
  264. err = s.logs.Delete(ctx, *log)
  265. if err != nil {
  266. return nil, err
  267. }
  268. s.changeService.Submit(ctx, models.ChangeModelLog, "remove", true, changekeys.Listed(log), log)
  269. return log, nil
  270. }