thread.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. let urlSlug = require('url-slug')
  2. module.exports = (sequelize, DataTypes) => {
  3. let Thread = sequelize.define('Thread', {
  4. name: {
  5. type: DataTypes.TEXT,
  6. set (val) {
  7. this.setDataValue('name', val)
  8. if(val) {
  9. this.setDataValue(
  10. 'slug',
  11. //if you don't covert to lowercase it doesn't
  12. //correctly slugify diacritics, e.g. thrËad
  13. //becomes 'thr-ead' not 'thread'
  14. urlSlug(val.toString().toLowerCase() || '') || '_'
  15. )
  16. }
  17. },
  18. allowNull: false,
  19. validate: {
  20. notEmpty: {
  21. msg: 'The title cannot be empty'
  22. },
  23. len: {
  24. args: [0, 256],
  25. msg: 'The title can only be up to 256 characters'
  26. },
  27. isString (val) {
  28. if(typeof val !== 'string') {
  29. throw new sequelize.ValidationError('The title must be a string')
  30. }
  31. }
  32. }
  33. },
  34. slug: DataTypes.TEXT,
  35. postsCount: {
  36. type: DataTypes.INTEGER,
  37. defaultValue: 0
  38. },
  39. locked: {
  40. type: DataTypes.BOOLEAN,
  41. defaultValue: false
  42. }
  43. }, {
  44. instanceMethods: {
  45. getMeta (limit) {
  46. let meta = {}
  47. let posts = this.Posts
  48. let firstPost = posts[0]
  49. let lastPost = posts.slice(-1)[0]
  50. //next url
  51. if(!lastPost || lastPost.postNumber+1 === this.postsCount) {
  52. meta.nextURL = null
  53. } else {
  54. meta.nextURL =
  55. `/api/v1/thread/${this.id}?limit=${limit}&from=${lastPost.postNumber + 1}`
  56. }
  57. //previous url
  58. if(!firstPost || firstPost.postNumber === 0) {
  59. meta.previousURL = null
  60. } else if(firstPost.postNumber - limit < 0) {
  61. meta.previousURL =
  62. `/api/v1/thread/${this.id}?limit=${firstPost.postNumber}&from=0`
  63. } else {
  64. meta.previousURL =
  65. `/api/v1/thread/${this.id}?limit=${limit}&from=${firstPost.postNumber - limit}`
  66. }
  67. //remaining posts
  68. if(lastPost === undefined) {
  69. meta.nextPostsCount = 0
  70. meta.previousPostsCount = 0
  71. meta.postsRemaining = 0
  72. } else {
  73. let postsRemaining =
  74. this.postsCount - lastPost.postNumber - 1
  75. meta.postsRemaining = postsRemaining
  76. if(postsRemaining < limit) {
  77. meta.nextPostsCount = postsRemaining
  78. } else {
  79. meta.nextPostsCount = limit
  80. }
  81. if(firstPost.postNumber === 0) {
  82. meta.previousPostsCount = 0
  83. } else if(firstPost.postNumber - limit < 0) {
  84. meta.previousPostsCount = firstPost.postNumber
  85. } else {
  86. meta.previousPostsCount = limit
  87. }
  88. }
  89. return meta
  90. }
  91. },
  92. classMethods: {
  93. associate (models) {
  94. Thread.belongsTo(models.User)
  95. Thread.belongsTo(models.Category)
  96. Thread.belongsTo(models.PollQuestion)
  97. Thread.hasMany(models.Post, { foreignKeyConstraint: true, onDelete: 'CASCADE' })
  98. },
  99. includeOptions (from, limit) {
  100. let models = sequelize.models
  101. return [
  102. { model: models.User, attributes: ['username', 'createdAt', 'color', 'picture', 'updatedAt', 'id'] },
  103. models.Category,
  104. {
  105. model: models.Post,
  106. where: { postNumber: { $gte: from } },
  107. order: [['id', 'ASC']],
  108. limit,
  109. include: [
  110. { model: models.Thread, attributes: ['slug'] },
  111. { model: models.User, as: 'Likes', attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
  112. { model: models.User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'admin'] },
  113. {
  114. model: models.Post, as: 'Replies', include:
  115. [{ model: models.User, attributes: ['username', 'id', 'color', 'picture'] }]
  116. }
  117. ]
  118. }
  119. ]
  120. }
  121. }
  122. })
  123. return Thread
  124. }