post.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. let marked = require('marked')
  2. let cheerio = require('cheerio')
  3. const Errors = require('../lib/errors')
  4. marked.setOptions({
  5. highlight: function (code) {
  6. return require('highlight.js').highlightAuto(code).value;
  7. },
  8. sanitize: true
  9. });
  10. const renderer = new marked.Renderer();
  11. renderer.link = function (href, title, text) {
  12. if(!href.match(/[a-z]+:\/\/.+/i)) {
  13. href = 'http://' + href;
  14. }
  15. return `
  16. <a href='${href}' ${title ? "title='" + title + "'" : "" } target='_blank' rel='noopener'>
  17. ${text}
  18. </a>
  19. `;
  20. };
  21. module.exports = (sequelize, DataTypes) => {
  22. let Post = sequelize.define('Post', {
  23. content: {
  24. type: DataTypes.TEXT,
  25. set (val) {
  26. if(!val) throw Errors.sequelizeValidation(sequelize, {
  27. error: 'content must be a string',
  28. path: 'content'
  29. })
  30. let md = marked(val, { renderer });
  31. this.setDataValue('content', md)
  32. this.setDataValue('plainText', cheerio(md).text())
  33. },
  34. allowNull: false
  35. },
  36. plainText: DataTypes.TEXT,
  37. postNumber: DataTypes.INTEGER,
  38. replyingToUsername: DataTypes.STRING,
  39. removed: {
  40. type: DataTypes.BOOLEAN,
  41. defaultValue: false
  42. }
  43. }, {
  44. instanceMethods: {
  45. getReplyingTo () {
  46. return Post.findByPrimary(this.replyId)
  47. },
  48. setReplyingTo (post) {
  49. return post.getUser().then(user => {
  50. return this.update({ replyingToUsername: user.username, replyId: post.id })
  51. })
  52. }
  53. },
  54. classMethods: {
  55. associate (models) {
  56. Post.belongsTo(models.User)
  57. Post.belongsTo(models.Thread)
  58. Post.hasMany(models.Post, { as: 'Replies', foreignKey: 'replyId' })
  59. Post.belongsToMany(models.User, { as: 'Likes', through: 'user_post' })
  60. Post.hasMany(models.Report, { foreignKeyConstraint: true, onDelete: 'CASCADE', hooks: true })
  61. },
  62. includeOptions () {
  63. let models = sequelize.models
  64. return [
  65. { model: models.User, attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
  66. { model: models.User, as: 'Likes', attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
  67. { model: models.Thread, include: [models.Category]} ,
  68. {
  69. model: models.Post, as: 'Replies', include:
  70. [{ model: models.User, attributes: ['username', 'id', 'color', 'picture'] }]
  71. }
  72. ]
  73. },
  74. async getReplyingToPost (id, thread) {
  75. let { Thread, User } = sequelize.models
  76. let replyingToPost = await Post.findById(
  77. id,
  78. { include: [Thread, { model: User, attributes: ['username'] }] }
  79. )
  80. if(!replyingToPost) {
  81. throw Errors.invalidParameter('replyingToId', 'post does not exist')
  82. } else if(replyingToPost.Thread.id !== thread.id) {
  83. throw Errors.invalidParameter('replyingToId', 'replies must be in same thread')
  84. } else if (replyingToPost.removed) {
  85. throw Errors.postRemoved
  86. } else {
  87. return replyingToPost
  88. }
  89. }
  90. }
  91. })
  92. return Post
  93. }