const jwtSecret = '4atikJgznM' const jwt = require('jsonwebtoken') const express = require('express'); const express_graphql = require('express-graphql'); const { buildSchema, printSchema, GraphQLString } = require('graphql'); const expand = require('mm-graphql/expand') const fs = require('fs') const uploadPath = `${__dirname}/public/images/`; const upload = require('multer')({ dest: uploadPath }) ;(async () => { const {Savable, slice, getModels} = await require('./models.js')() const { jwtGQLAnon, jwtCheck } = require('mm-graphql/jwt') let schema = buildSchema(` type User { _id: ID createdAt: String login: String nick : String acl: [String] avatar: Media chats: [Chat] } input UserInput { _id: ID login: String nick : String password: String acl: [String] avatar: MediaInput chats: [ChatInput] } type Message { _id: ID createdAt: String owner: User chat: Chat text: String media: [Media] replies: [Message] replyTo: Message forwarded: Message forwardWith: [Message] } input MessageInput { _id: ID text: String chat: ChatInput media: [MediaInput] replies: [MessageInput] replyTo: MessageInput forwarded: MessageInput forwardWith: [MessageInput] } type Chat { _id: ID createdAt: String lastModified: String lastMessage: Message owner: User title: String members: [User] messages: [Message] avatar: Media } input ChatInput { _id: ID title: String members: [UserInput] messages: [MessageInput] } type Media { _id: ID, createdAt: String owner: User text: String, url: String, originalFileName: String, type: String userAvatar: User chatAvatars: [Chat] messages: [Message] } input MediaInput { _id: ID, text: String, userAvatar: UserInput, chatAvatars: [ChatInput] messages: [MessageInput] } `); schema = expand(schema, { login:{ type: GraphQLString, args: {login: {type: GraphQLString}, password: {type: GraphQLString}, }, async resolve(root, {login, password}, context, info){ const Savable = context.models.Savable if (!login || !password) return null; const user = await Savable.m.User.findOne({login, password}) console.log(user, {login, password}) if (!user) return null; const token = jwt.sign({ sub: {id: user._id, login, acl: user.acl}}, jwtSecret); //подписывам токен нашим ключем return token } } }) console.log(printSchema(schema)) const app = express(); const http = require('http').Server(app); const io = require('socket.io')(http); app.use(require('cors')()) app.use(express.static('public')); app.use('/graphql', express_graphql(jwtGQLAnon({schema, createContext: decoded => getModels(decoded, messageWatcher, chatWatcher), graphiql: true, secret: jwtSecret}))) app.post('/upload', upload.single('media'), async (req, res, next) => { let decoded; if (decoded = jwtCheck(req, jwtSecret)){ console.log('SOME UPLOAD', decoded, req.file) let {models: {Media }} = await getModels(decoded.sub) let media = await Media.fromFileData(req.file) res.end(JSON.stringify({_id: media._id, url: media.url})) } else { res.status(503).send('permission denied') } }) app.use(express.static('public')); const sockets = {} async function messageWatcher(msg){ console.log('NEW MSG', msg.text, msg.chat.title, msg.owner.login) for (let [id,{user, socket}] of Object.entries(sockets)){ if (user.chats.some(chat => chat._id.toString() === msg.chat._id.toString())){ let {_id, createdAt, text, chat, media, replyTo, forwarded, owner} = msg await chat; await Promise.all(media || []); await replyTo; await forwarded; owner = await owner; await owner.avatar console.log('MEDIA', media) socket.emit('msg', {_id, createdAt: createdAt.getTime(), text, chat: chat && {_id: chat._id, title: chat.title}, media: media && media.map(media => ({_id: media._id, url: media.url, type: media.type})), replyTo: replyTo && {_id: replyTo._id, text: replyTo.text}, forwarded: forwarded && {_id: forwarded._id, text: forwarded.text}, owner: {_id: owner._id, login: owner.login, nick: owner.nick, avatar: owner.avatar && {url: owner.avatar.url}}}) } } } async function chatWatcher({_loadRelations, members, _id, createdAt, title, messages, avatar, lastModified}){ console.log('CHAT SAVE', _id, title) for (let [id,socketData] of Object.entries(sockets)){ const {user, socket} = socketData const isMember = members.some(member => member._id.toString() === user._id.toString()) const wasMember =(_loadRelations.members && _loadRelations.members.some(member => member._id.toString() === user._id.toString())) if (isMember || wasMember){ //await chat; //await media; //await replyTo; //await forwarded; await avatar; await Promise.all(members) //owner = await owner; console.log('EMIT', _id, createdAt.getTime(), title) socket.emit(isMember ? 'chat' : 'chat_left', {_id, createdAt: createdAt.getTime(), title, avatar: avatar && {_id: avatar._id, url: avatar.url}, members: members.map(({_id, login, nick}) => ({_id, login, nick})), lastModified}) socketData.user = (await getModels(socketData.decoded.sub)).thisUser //re-read chat permissions } } } io.on('connection', socket => { console.log('connect', socket.id) socket.on('disconnect', () => { console.log('disconnect', socket.id) delete sockets[socket.id] }) socket.on('jwt', async token =>{ let decoded; try { decoded = jwt.verify(token, jwtSecret) } catch (e){ socket.emit('jwt_fail', e) return } socket.emit('jwt_ok', decoded) //sockets[socket.id] = decoded sockets[socket.id] = {decoded, user: (await getModels(decoded.sub)).thisUser, socket} }) }) let socketPath = 5001 http.listen(socketPath, () => { console.log(`Express GraphQL Server Now Running On ${socketPath}/graphql`); }); })()