UNPKG

ep_profile_modal

Version:
487 lines (439 loc) 18.6 kB
const db = require('ep_etherpad-lite/node/db/DB'); const defaultImg = '/static/plugins/ep_profile_modal/static/dist/img/user-blue.png'; const defaultImgUserOff = '/static/plugins/ep_profile_modal/static/dist/img/user.png'; const gravatar = require('gravatar'); const fetch = require('node-fetch'); const Busboy = require('busboy'); const uuid = require('uuid'); const path = require('path'); const settings = require('ep_etherpad-lite/node/utils/Settings'); const AWS = require('aws-sdk'); const resizeImg = require('resize-img'); const sizeOf = require('buffer-image-size'); const padMessageHandler = require('ep_etherpad-lite/node/handler/PadMessageHandler'); const emailService = require('../services/email'); exports.expressConfigure = (hookName, context) => { context.app.get('/static/:padId/pluginfw/ep_profile_modal/getUserInfo/:userId', async (req, res, next) => { const padId = req.params.padId; const userId = req.params.userId; console.log(`ep_profile_modal:${userId}_${padId}`); const user = await db.get(`ep_profile_modal:${userId}_${padId}`) || {}; user.homepage = getValidUrl(user.homepage) || ''; return res.status(201).json({user}); }); // comes from users email when they already recieved an email for this context.app.get('/static/emailConfirmation/:userId/:padId/:confirmCode', async (req, res, next) => { const settings = await db.get('ep_profile_modal_settings') || {}; const userId = Buffer.from(req.params.userId, 'base64').toString('ascii'); const padId = Buffer.from(req.params.padId, 'base64').toString('ascii'); const confirmCode = Buffer.from(req.params.confirmCode, 'base64').toString('ascii'); const user = await db.get(`ep_profile_modal:${userId}_${padId}`) || {}; if (user.confirmationCode === confirmCode) { if (user.image !== '' && user.image !== 'reset' && user.image) { user.image = await moveImageToAccount(userId, padId, user.email, user.image) || user.image; } user.confirmationCode = 0; user.verified = true; user.updateDate = new Date(); user.verifiedDate = new Date(); db.set(`ep_profile_modal:${userId}_${padId}`, user); // for global session db.set(`ep_profile_modal:${userId}`, user); // for global session // for global session // gathering verified user id of pads let verified_users = await db.get(`ep_profile_modal_verified_${padId}`); if (verified_users) { if (verified_users.indexOf(userId) == -1) { verified_users.push(userId); } } else { verified_users = [userId]; } db.set(`ep_profile_modal_verified_${padId}`, verified_users); // gathering verified user id of pads // gathering verified email of pads var verified_emails = await db.get(`ep_profile_modal_verified_email_${padId}`); if (verified_emails) { if (verified_emails.indexOf(user.email) == -1) { verified_emails.push(user.email); } } else { verified_emails = [user.email]; } db.set(`ep_profile_modal_verified_email_${padId}`, verified_emails); // gathering verified email of pads // gathering verified emails var verified_emails = await db.get('ep_profile_modal_verified_emails'); if (verified_emails) { if (verified_emails.indexOf(user.email) == -1) { verified_emails.push(user.email); } } else { verified_emails = [user.email]; } db.set('ep_profile_modal_verified_emails', verified_emails); // gathering verified emails // upsert general data on each validation. const emailUser = await db.get(`ep_profile_modal:${user.email}`); if (emailUser) { user.pads = emailUser.pads || []; } // store pads of verified users if (user.pads) { if (user.pads.indexOf(padId) == -1) { user.pads.push(padId); } } else { user.pads = [padId]; } db.set(`ep_profile_modal:${user.email}`, user); // upsert general data on each validation. // /// store users in email way // email collecting users const datetime = new Date(); const _timestamp = datetime.getTime(); const _date = datetime.toISOString().slice(0, 10); const email_contributed_users = await db.get(`ep_profile_modal_email_contributed_${padId}`) || []; const lastUserIndex = email_contributed_users.findIndex((i) => i.email === user.email); if (lastUserIndex !== -1) { email_contributed_users[lastUserIndex].data.last_seen_timestamp = _timestamp; email_contributed_users[lastUserIndex].data.last_seen_date = _date; } else { email_contributed_users.push({ email: user.email, data: { last_seen_timestamp: _timestamp, last_seen_date: _date, created_at_timestamp: _timestamp, created_at_date: _date, }, }); } db.set(`ep_profile_modal_email_contributed_${padId}`, email_contributed_users); // remove user id from contributed users because we have email now const pad_users = await db.get(`ep_profile_modal_contributed_${padId}`); const indexOfUserId = pad_users.indexOf(userId); if (indexOfUserId != -1) { pad_users.splice(indexOfUserId, 1); db.set(`ep_profile_modal_contributed_${padId}`, pad_users); } // remove user id from contributed users because we have email now // // store users in email way } if (settings.redirectToPad) return res.redirect(`/${padId}`); else return res.redirect(`/`); }); context.app.get('/static/getUserProfileImage/:userId/:padId', async (req, res, next) => { let profile_json = null; let httpsUrl = null; const user = await db.get(`ep_profile_modal:${req.params.userId}_${req.params.padId}`) || {}; console.log(user); if (user.status == '2') { // logged in if (user.image && user.image != 'reset') { const s3 = new AWS.S3({ accessKeyId: settings.ep_profile_modal.storage.accessKeyId, secretAccessKey: settings.ep_profile_modal.storage.secretAccessKey, endpoint: settings.ep_profile_modal.storage.endPoint, s3ForcePathStyle: true, // needed with minio? signatureVersion: 'v4', }); try { const params = {Bucket: settings.ep_profile_modal.storage.bucket, Key: `${user.image}`}; s3.getObject(params, (err, data) => { console.log('data going to be ', params, data, err); if (data) { res.writeHead(200, {'Content-Type': 'image/jpeg'}); res.write(data.Body, 'binary'); res.end(null, 'binary'); } else { res.end(null, 'binary'); } }); } catch (error) { console.log('error', error); } } else { const profile_url = gravatar.profile_url(user.email, {protocol: 'https'}); profile_json = await fetch(profile_url); profile_json = await profile_json.json(); if (profile_json != 'User not found') { httpsUrl = gravatar.url(user.email, {protocol: 'https', s: '200'}); } else { profile_json = null; } return res.redirect((profile_json != null) ? httpsUrl : defaultImg); } } else { return res.redirect(defaultImgUserOff); } }); // for sending email validation context.app.get('/static/:padId/pluginfw/ep_profile_modal/sendVerificationEmail/:userId/:userName/:email', async (req, res, next) => { const settings = await db.get('ep_profile_modal_settings') || {}; if (settings.settingsDomain && settings.settingsEmailSmtp && settings.settingsEmailPort && settings.settingsEmailUser && settings.settingsEmailPassword) { const padId = req.params.padId; const userId = req.params.userId; const user = await db.get(`ep_profile_modal:${userId}_${padId}`) || {}; let userEmail = ''; if (validation(req.params.email)) { if (validation(user.email)) { userEmail = user.email; } else { userEmail = req.params.email; } } else { userEmail = user.email; } var userName = ''; if(validation(req.params.userName)){ if(validation(user.username)){ userName = user.username ; }else{ userName = req.params.userName ; } }else{ userName = user.username ; } console.log(userEmail,userName) if (userEmail){ if (!user.verified){ const confirmCode = new Date().getTime().toString() user.confirmationCode = confirmCode user.email =userEmail user.username =userName var link = `https://${settings.settingsDomain}/static/emailConfirmation/${Buffer.from(userId).toString('base64')}/${Buffer.from(padId).toString('base64')}/${Buffer.from(confirmCode).toString('base64')}` var html = (settings.settingsHtmlBodyTemplate) ? eval(settings.settingsHtmlBodyTemplate) : `<p><b>Hello ${userName}! </b></p> <p> Please <a href='${link}'>click here</a> to verify your email address for ${settings.settingsDomain}/${padId} .</p> <p>If this wasn’t you, ignore this message.</p>` console.log(html) emailService.sendMail(settings,{ to : userEmail , subject : (settings.settingsHtmlSubjectTemplate) ? settings.settingsHtmlSubjectTemplate: `confirm email for ${settings.settingsDomain}/${padId}`, html: html }) .then((data)=>{ console.log(data,"from email",data.messageId) }) .catch((err)=>{ console.log(err.message,"error from email") }) db.set("ep_profile_modal:"+userId+"_"+padId , user) }else{ console.log('email already verified'); } }else{ console.log('there is not valid email', email); } }else { console.log('setting not set'); } return res.status(201).json({status: 'ok'}); }); // for reset profile image context.app.get('/static/:padId/pluginfw/ep_profile_modal/resetProfileImage/:userId', async (req, res, next) => { const padId = req.params.padId; const userId = req.params.userId; db.set(`ep_profile_modal_image:${userId}`, 'reset'); const user = await db.get(`ep_profile_modal:${userId}_${padId}`) || {}; user.image = 'reset'; await db.set(`ep_profile_modal:${userId}_${padId}`, user); return res.status(201).json({status: 'ok'}); }); // for upload user image context.app.post('/static/:padId/pluginfw/ep_profile_modal/upload/:userId', async (req, res, next) => { const padId = req.params.padId; const userId = req.params.userId; const s3 = new AWS.S3({ accessKeyId: settings.ep_profile_modal.storage.accessKeyId, secretAccessKey: settings.ep_profile_modal.storage.secretAccessKey, endpoint: settings.ep_profile_modal.storage.endPoint, s3ForcePathStyle: true, // needed with minio? signatureVersion: 'v4', }); console.log({ accessKeyId: settings.ep_profile_modal.storage.accessKeyId, secretAccessKey: settings.ep_profile_modal.storage.secretAccessKey, endpoint: settings.ep_profile_modal.storage.endPoint, s3ForcePathStyle: true, // needed with minio? signatureVersion: 'v4', }); try { var busboy = new Busboy({ headers: req.headers, limits: { fileSize: settings.ep_profile_modal.maxFileSize, }, }); } catch (error) { console.log('ep_profile_modal ERROR', error); return next(error); } const done = function (error) { if (error) { console.log('ep_profile_modal UPLOAD ERROR', error); return; } if (isDone) return; isDone = true; req.unpipe(busboy); drainStream(req); busboy.removeAllListeners(); const msg = error.stack.substring(0, error.stack.indexOf('\n')); return res.status(201).json({error: msg}); }; const newFileName = uuid.v4(); busboy.on('file', async (fieldname, file, filename, encoding, mimetype) => { const fileType = path.extname(filename); const fileTypeWithoutDot = fileType.substr(1); const fileRead = []; const user = await db.get(`ep_profile_modal:${userId}_${padId}`) || {}; if (user.verified) { var savedFilename = path.join(user.email, padId, newFileName + fileType); } else { var savedFilename = path.join(userId, padId, newFileName + fileType); } file.on('limit', () => res.status(201).json({error: 'File is too large'})); file.on('error', (error) => { busboy.emit('error', error); }); file.on('data', async (chunk) => { fileRead.push(chunk); }); file.on('end', async () => { const finalBuffer = Buffer.concat(fileRead); const sizeImage = sizeOf(finalBuffer); let selctedWidth = 128; let selectedHeight = 128; if (sizeImage) { const result_resize = getBestImageReszie(sizeImage.width, sizeImage.height, 128); console.log(sizeImage, result_resize); selctedWidth = result_resize.width; selectedHeight = result_resize.height; } try { var newFile = await resizeImg(finalBuffer, { width: selctedWidth, height: selectedHeight, }); } catch (error) { var msg = error.message.substring(0, error.message.indexOf('\n')); return res.status(201).json({error: msg}); } if (settings.ep_profile_modal.storage.type == 's3') { const params_upload = { bucket: settings.ep_profile_modal.storage.bucket, Bucket: settings.ep_profile_modal.storage.bucket, Key: savedFilename, // File name you want to save as in S3 Body: newFile, }; try { console.log(params_upload); s3.upload(params_upload, async (err, data) => { if (err) { console.log(err, err.stack, 'error'); } else { console.log(data); } if (data) { db.set(`ep_profile_modal_image:${userId}`, savedFilename); const user = await db.get(`ep_profile_modal:${userId}_${padId}`) || {}; user.image = savedFilename; user.updateDate = new Date(); await db.set(`ep_profile_modal:${userId}_${padId}`, user); var msg = { type: 'COLLABROOM', data: { type: 'CUSTOM', payload: { padId, action: 'EP_PROFILE_USER_IMAGE_CHANGE', userId, }, }, }; sendToRoom(msg); return res.status(201).json({type: settings.ep_profile_modal.storage.type, error: false, fileName: savedFilename, fileType, data}); } else { var msg = err.stack.substring(0, err.stack.indexOf('\n')); return res.status(201).json({error: msg}); } }); } catch (error) { var msg = error.message.substring(0, error.message.indexOf('\n')); return res.status(201).json({error: msg}); } } }); }); req.pipe(busboy); }); return context; }; const moveImageToAccount = async (userId, padId, email, userImage) => { console.log(userId, padId, email, userImage); if (userImage.indexOf(email) == -1) { console.log(settings.ep_profile_modal, settings.ep_profile_modal.storage.bucket, 'settings.ep_profile_modal.storage.bucket'); const s3 = new AWS.S3({ accessKeyId: settings.ep_profile_modal.storage.accessKeyId, secretAccessKey: settings.ep_profile_modal.storage.secretAccessKey, endpoint: settings.ep_profile_modal.storage.endPoint, s3ForcePathStyle: true, // needed with minio? signatureVersion: 'v4', }); const Key = userImage; const newKey = path.join(Key.replace(userId, email)); // not worked // var resultCopy = await s3.copyObject({ // Bucket: settings.ep_profile_modal.storage.bucket, // CopySource: `${settings.ep_profile_modal.storage.bucket}/${Key}`, // old file Key // Key: newKey , // new file Key // }).promise(); // console.log(resultCopy) // download image const getObjectResult = await s3.getObject({ Bucket: settings.ep_profile_modal.storage.bucket, Key, }).promise(); // upload image await s3.upload({ Bucket: settings.ep_profile_modal.storage.bucket, Key: newKey, // File name you want to save as in S3 Body: getObjectResult.Body, }).promise(); // delete await s3.deleteObject({ Bucket: settings.ep_profile_modal.storage.bucket, Key, }).promise(); return newKey; } return userImage; }; const getBestImageReszie = (originalWidth, originalHeight, size) => { const ratio = originalWidth / originalHeight; let targetWidth = targetHeight = Math.min(size, Math.max(originalWidth, originalHeight)); if (ratio < 1) { targetWidth = targetHeight * ratio; } else { targetHeight = targetWidth / ratio; } srcWidth = originalWidth; srcHeight = originalHeight; srcX = srcY = 0; return {width: targetWidth, height: targetHeight}; }; function getValidUrl(url = '') { if (url == '') return ''; let newUrl = decodeURIComponent(url); newUrl = newUrl.trim().replace(/\s/g, ''); if (/^(:\/\/)/.test(newUrl)) { return `http${newUrl}`; } if (!/^(f|ht)tps?:\/\//i.test(newUrl)) { return `http://${newUrl}`; } return newUrl; } const sendToRoom = (msg) => { const bufferAllows = true; // Todo write some buffer handling for protection and to stop DDoS -- myAuthorId exists in message. if (bufferAllows) { setTimeout(() => { // This is bad.. We have to do it because ACE hasn't redrawn by the time the chat has arrived try { padMessageHandler.handleCustomObjectMessage(msg, false, (error) => { // console.log(error) // TODO: Error handling. }); } catch (error) { console.log(error); } } , 100); } }; const validation = (variable) => { if (variable == 'null' || !variable || variable == undefined || variable == '' || variable == null) { return false; } else { return true; } };