const ObjectID = require("mongodb").ObjectID; const {connect} = require('mm') module.exports = async (dbName='graphql-chat') => { const {Savable, slice} = await connect(dbName) async function getModels({id, acl}, onMsgSave, onChatSave){ const thisUser = id !== 'anon' && await Savable.m.User.findOne({_id: ObjectID(id)}) const chatACL = thisUser && thisUser.chats && thisUser.chats.map && thisUser.chats.map(c => c._id.toString()) const SlicedSavable = slice(id === 'anon' ? [id] : [...acl, ...chatACL]) class User extends SlicedSavable { constructor(...params){ super(...params) this.originalACL = this.acl && [...this.acl] this.chats = Savable.arrize(this.chats) } async save(...params){ console.log(this) let otherUserWithThisLogin = this.login && await Savable.m.User.findOne({login: this.login}) if (this._id){ if (otherUserWithThisLogin && otherUserWithThisLogin._id.toString() !== this._id.toString()){ throw new ReferenceError(`User ${this.login} already exists`) } if (!acl.includes('admin') && this.originalACL.toString() !== this.acl.toString()) throw new ReferenceError(`Not enough permissions for changing acl on ${this.login}`) else return await super.save(...params) } else { if (otherUserWithThisLogin){ throw new ReferenceError(`User ${this.login} already exists`) } await super.save(...params) this.___owner = this._id.toString() this.acl = [this.___owner, "user"] return await Savable.prototype.save.call(this, ...params) } } static get relations(){ //don't needed due to ___owner in most cases return { avatar : "userAvatar", chats: ["members"] } } static get defaultPermissions(){ return { create: ['anon'], read: ['owner', 'user', 'admin'], write: ['owner', 'admin'], delete: [] } } static get guestRelations(){ return ["chats"]; } } SlicedSavable.addClass(User) class OwnerSlicedSavable extends SlicedSavable { get owner(){ if (!this.___owner) return this.___owner return SlicedSavable.m.User.findOne({_id: ObjectID(this.___owner)}) } } class Media extends OwnerSlicedSavable { constructor(...params){ super(...params) } static async fromFileData(fileData){ let media = new Media({}) media.fileData = fileData media.url = `images/${fileData.filename}` media.originalFileName = fileData.originalname media.type = fileData.mimetype await media.save() return media; } //async save(...params){ //if (this.userAvatar){ //if (this.userAvatar._id.toString() !== id){ //throw new ReferenceError(`You can't set ava for other user`) //} //} //return await super.save(...params) //} static get relations(){ return { userAvatar: "avatar", //if it is ava chatAvatars: "avatar", messages: ["media"] } } static get defaultPermissions(){ return { create: ['user', 'admin'], read: ['anon', 'user', 'admin'], write: ["owner", 'admin'], delete: ['admin'] } } } SlicedSavable.addClass(Media) class Message extends OwnerSlicedSavable { async save(...params){ if (!this.chat){ throw new ReferenceError("You should set chat") } const chatId = this.chat._id.toString() if (!chatACL.includes(chatId)) { throw new ReferenceError("You cannot post to chat without membership.") } this.___permissions.read.push(chatId) this.___permissions.read = [...new Set(this.___permissions.read)] let newMsg = !this._id let result = await super.save(...params) await this.chat if (newMsg){ this.chat.lastModified = this.createdAt.getTime() } await Savable.prototype.save.call(this.chat) onMsgSave(this) return result } static get relations(){ return { media: ['messages'], replyTo: ['replies'], replies: 'replyTo', forwardWith: 'forwarded', forwarded: ['forwardWith'], chat: ['messages'] } } static get defaultPermissions(){ return { create: ['user', 'admin'], read: ['owner', 'admin'], write: ['owner','admin'], delete: ['admin'] } } static get guestRelations(){ return ['replies', 'forwardWith'] } } SlicedSavable.addClass(Message) class Chat extends OwnerSlicedSavable { constructor(...params){ super(...params) this.members = Savable.arrize(this.members) this.messages = Savable.arrize(this.messages) } get lastMessage(){ return this.messages[this.messages.length -1] } async save(...params){ if (!this._id){ this.members.push(thisUser) await super.save(...params) this.lastModified = this.createdAt.getTime() this.___permissions.read.push(this._id.toString()) } onChatSave(this) return await super.save(...params) } static get relations(){ return { members: ['chats'], messages: 'chat', avatar: ["chatAvatars"] } } static get defaultPermissions(){ return { create: ['user'], read: ['owner', 'admin', 'user'], write: ['owner'], delete: [] } } static get guestRelations(){ return ['messages', 'members'] } } SlicedSavable.addClass(Chat) return {models: { SlicedSavable, ...SlicedSavable.classes }, thisUser} } return { Savable, slice, getModels } }