models.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. const ObjectID = require("mongodb").ObjectID;
  2. const {connect} = require('mm')
  3. module.exports = async (dbName='graphql-chat') => {
  4. const {Savable, slice} = await connect(dbName)
  5. async function getModels({id, acl}, onMsgSave, onChatSave){
  6. const thisUser = id !== 'anon' && await Savable.m.User.findOne({_id: ObjectID(id)})
  7. const chatACL = thisUser && thisUser.chats && thisUser.chats.map && thisUser.chats.map(c => c._id.toString())
  8. const SlicedSavable = slice(id === 'anon' ? [id] : [...acl, ...chatACL])
  9. class User extends SlicedSavable {
  10. constructor(...params){
  11. super(...params)
  12. this.originalACL = this.acl && [...this.acl]
  13. this.chats = Savable.arrize(this.chats)
  14. }
  15. async save(...params){
  16. console.log(this)
  17. let otherUserWithThisLogin = this.login && await Savable.m.User.findOne({login: this.login})
  18. if (this._id){
  19. if (otherUserWithThisLogin && otherUserWithThisLogin._id.toString() !== this._id.toString()){
  20. throw new ReferenceError(`User ${this.login} already exists`)
  21. }
  22. if (!acl.includes('admin') && this.originalACL.toString() !== this.acl.toString())
  23. throw new ReferenceError(`Not enough permissions for changing acl on ${this.login}`)
  24. else return await super.save(...params)
  25. }
  26. else {
  27. if (otherUserWithThisLogin){
  28. throw new ReferenceError(`User ${this.login} already exists`)
  29. }
  30. await super.save(...params)
  31. this.___owner = this._id.toString()
  32. this.acl = [this.___owner, "user"]
  33. return await Savable.prototype.save.call(this, ...params)
  34. }
  35. }
  36. static get relations(){ //don't needed due to ___owner in most cases
  37. return {
  38. avatar : "userAvatar",
  39. chats: ["members"]
  40. }
  41. }
  42. static get defaultPermissions(){
  43. return {
  44. create: ['anon'],
  45. read: ['owner', 'user', 'admin'],
  46. write: ['owner', 'admin'],
  47. delete: []
  48. }
  49. }
  50. static get guestRelations(){
  51. return ["chats"];
  52. }
  53. }
  54. SlicedSavable.addClass(User)
  55. class OwnerSlicedSavable extends SlicedSavable {
  56. get owner(){
  57. if (!this.___owner) return this.___owner
  58. return SlicedSavable.m.User.findOne({_id: ObjectID(this.___owner)})
  59. }
  60. }
  61. class Media extends OwnerSlicedSavable {
  62. constructor(...params){
  63. super(...params)
  64. }
  65. static async fromFileData(fileData){
  66. let media = new Media({})
  67. media.fileData = fileData
  68. media.url = `images/${fileData.filename}`
  69. media.originalFileName = fileData.originalname
  70. media.type = fileData.mimetype
  71. await media.save()
  72. return media;
  73. }
  74. //async save(...params){
  75. //if (this.userAvatar){
  76. //if (this.userAvatar._id.toString() !== id){
  77. //throw new ReferenceError(`You can't set ava for other user`)
  78. //}
  79. //}
  80. //return await super.save(...params)
  81. //}
  82. static get relations(){
  83. return {
  84. userAvatar: "avatar", //if it is ava
  85. chatAvatars: "avatar",
  86. messages: ["media"]
  87. }
  88. }
  89. static get defaultPermissions(){
  90. return {
  91. create: ['user', 'admin'],
  92. read: ['anon', 'user', 'admin'],
  93. write: ["owner", 'admin'],
  94. delete: ['admin']
  95. }
  96. }
  97. }
  98. SlicedSavable.addClass(Media)
  99. class Message extends OwnerSlicedSavable {
  100. async save(...params){
  101. if (!this.chat){
  102. throw new ReferenceError("You should set chat")
  103. }
  104. const chatId = this.chat._id.toString()
  105. if (!chatACL.includes(chatId)) {
  106. throw new ReferenceError("You cannot post to chat without membership.")
  107. }
  108. this.___permissions.read.push(chatId)
  109. this.___permissions.read = [...new Set(this.___permissions.read)]
  110. let result = await super.save(...params)
  111. await this.chat
  112. this.chat.lastModified = this.createdAt.getTime()
  113. await Savable.prototype.save.call(this.chat)
  114. onMsgSave(this)
  115. return result
  116. }
  117. static get relations(){
  118. return {
  119. media: ['messages'],
  120. replyTo: ['replies'],
  121. replies: 'replyTo',
  122. forwardWith: 'forwarded',
  123. forwarded: ['forwardWith'],
  124. chat: ['messages']
  125. }
  126. }
  127. static get defaultPermissions(){
  128. return {
  129. create: ['user', 'admin'],
  130. read: ['owner', 'admin'],
  131. write: ['owner','admin'],
  132. delete: ['admin']
  133. }
  134. }
  135. static get guestRelations(){
  136. return ['replies', 'forwardWith']
  137. }
  138. }
  139. SlicedSavable.addClass(Message)
  140. class Chat extends OwnerSlicedSavable {
  141. constructor(...params){
  142. super(...params)
  143. this.members = Savable.arrize(this.members)
  144. }
  145. async save(...params){
  146. if (!this._id){
  147. this.members.push(thisUser)
  148. await super.save(...params)
  149. this.lastModified = this.createdAt.getTime()
  150. this.___permissions.read.push(this._id.toString())
  151. }
  152. onChatSave(this)
  153. return await super.save(...params)
  154. }
  155. static get relations(){
  156. return {
  157. members: ['chats'],
  158. messages: 'chat',
  159. avatar: ["chatAvatars"]
  160. }
  161. }
  162. static get defaultPermissions(){
  163. return {
  164. create: ['user'],
  165. read: ['owner', 'admin', 'user'],
  166. write: ['owner'],
  167. delete: []
  168. }
  169. }
  170. static get guestRelations(){
  171. return ['messages']
  172. }
  173. }
  174. SlicedSavable.addClass(Chat)
  175. return {models: { SlicedSavable, ...SlicedSavable.classes },
  176. thisUser}
  177. }
  178. return {
  179. Savable,
  180. slice,
  181. getModels
  182. }
  183. }