You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
408 lines
12 KiB
408 lines
12 KiB
// email.js
|
|
// Copyright (C) 2021 Digital Telepresence, LLC
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
const express = require('express');
|
|
|
|
const { SiteController,/*, SiteError*/
|
|
SiteError} = require('../../lib/site-lib');
|
|
|
|
class ChatController extends SiteController {
|
|
|
|
constructor (dtp) {
|
|
super(dtp, module.exports);
|
|
}
|
|
|
|
async start ( ) {
|
|
const {
|
|
chat: chatService,
|
|
limiter: limiterService,
|
|
session: sessionService,
|
|
} = this.dtp.services;
|
|
|
|
const upload = this.createMulter();
|
|
|
|
const router = express.Router();
|
|
this.dtp.app.use('/chat', router);
|
|
|
|
router.use(
|
|
sessionService.authCheckMiddleware({ requireLogin: true }),
|
|
chatService.middleware({ maxOwnedRooms: 25, maxJoinedRooms: 50 }),
|
|
async (req, res, next) => {
|
|
res.locals.currentView = 'chat';
|
|
return next();
|
|
},
|
|
);
|
|
|
|
router.param('roomId', this.populateRoomId.bind(this));
|
|
router.param('inviteId', this.populateInviteId.bind(this));
|
|
|
|
router.post(
|
|
'/room/:roomId/invite/:inviteId/action',
|
|
limiterService.createMiddleware(limiterService.config.chat.postRoomInviteAction),
|
|
upload.none(),
|
|
this.postRoomInviteAction.bind(this),
|
|
);
|
|
|
|
router.post(
|
|
'/room/:roomId/invite',
|
|
limiterService.createMiddleware(limiterService.config.chat.postRoomInvite),
|
|
upload.none(),
|
|
this.postRoomInvite.bind(this),
|
|
);
|
|
|
|
router.post(
|
|
'/room/:roomId',
|
|
limiterService.createMiddleware(limiterService.config.chat.postRoomUpdate),
|
|
this.postRoomUpdate.bind(this),
|
|
);
|
|
|
|
router.post(
|
|
'/room',
|
|
limiterService.createMiddleware(limiterService.config.chat.postRoomCreate),
|
|
this.postRoomCreate.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/room/create',
|
|
this.getRoomEditor.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/room/:roomId/form/:formName',
|
|
limiterService.createMiddleware(limiterService.config.chat.getRoomForm),
|
|
this.getRoomForm.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/room/:roomId/invite/:inviteId',
|
|
limiterService.createMiddleware(limiterService.config.chat.getRoomInviteView),
|
|
this.getRoomInviteView.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/room/:roomId/invite',
|
|
limiterService.createMiddleware(limiterService.config.chat.getRoomInviteView),
|
|
this.getRoomInviteHome.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/room/:roomId/settings',
|
|
limiterService.createMiddleware(limiterService.config.chat.getRoomSettings),
|
|
this.getRoomSettings.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/room/:roomId',
|
|
limiterService.createMiddleware(limiterService.config.chat.getRoomView),
|
|
this.getRoomView.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/room',
|
|
limiterService.createMiddleware(limiterService.config.chat.getRoomHome),
|
|
this.getRoomHome.bind(this),
|
|
);
|
|
|
|
router.get(
|
|
'/',
|
|
limiterService.createMiddleware(limiterService.config.chat.getHome),
|
|
this.getHome.bind(this),
|
|
);
|
|
|
|
/*
|
|
* DELETE operations
|
|
*/
|
|
|
|
router.delete(
|
|
'/room/:roomId/invite/:inviteId',
|
|
limiterService.createMiddleware(limiterService.config.chat.deleteInvite),
|
|
this.deleteInvite.bind(this),
|
|
);
|
|
|
|
router.delete(
|
|
'/room/:roomId',
|
|
limiterService.createMiddleware(limiterService.config.chat.deleteRoom),
|
|
this.deleteInvite.bind(this),
|
|
);
|
|
|
|
return router;
|
|
}
|
|
|
|
async populateRoomId (req, res, next, roomId) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.room = await chatService.getRoomById(roomId);
|
|
if (!res.locals.room) {
|
|
throw new SiteError(404, 'Room not found');
|
|
}
|
|
return next();
|
|
} catch (error) {
|
|
this.log.error('failed to populate roomId', { roomId, error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async populateInviteId (req, res, next, inviteId) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.invite = await chatService.getRoomInviteById(inviteId);
|
|
if (!res.locals.invite) {
|
|
throw new SiteError(404, 'Invite not found');
|
|
}
|
|
return next();
|
|
} catch (error) {
|
|
this.log.error('failed to populate inviteId', { inviteId, error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async postRoomInviteAction (req, res) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
const { response } = req.body;
|
|
const displayList = this.createDisplayList('room-invite-action');
|
|
this.log.debug('room invite action', { message: req.body });
|
|
switch (response) {
|
|
case 'accept':
|
|
await chatService.acceptRoomInvite(res.locals.invite);
|
|
displayList.navigateTo(`/chat/room/${res.locals.invite.room._id}`);
|
|
break;
|
|
|
|
case 'reject':
|
|
await chatService.rejectRoomInvite(res.locals.invite);
|
|
displayList.showNotification(
|
|
`Chat room invite rejected`,
|
|
'success',
|
|
'top-center',
|
|
5000,
|
|
);
|
|
break;
|
|
|
|
default:
|
|
throw new SiteError(400, 'Must specify invite action');
|
|
}
|
|
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to execute room invite action', {
|
|
inviteId: res.locals.invite._id,
|
|
response: req.body.response,
|
|
error,
|
|
});
|
|
return res.status(error.statusCode || 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
async postRoomInvite (req, res) {
|
|
const { chat: chatService, user: userService } = this.dtp.services;
|
|
this.log.debug('room invite received', { invite: req.body });
|
|
if (!req.body.username || !req.body.username.length) {
|
|
return res.status(400).json({ success: false, message: 'Please provide a username' });
|
|
}
|
|
try {
|
|
req.body.username = req.body.username.trim().toLowerCase();
|
|
while (req.body.username[0] === '@') {
|
|
req.body.username = req.body.username.slice(1);
|
|
}
|
|
|
|
if (!req.body.username || !req.body.username.length) {
|
|
throw new SiteError(400, 'Please provide a username');
|
|
}
|
|
|
|
const member = await userService.getPublicProfile(req.body.username);
|
|
if (!member) {
|
|
throw new SiteError(404, `There is no account with username @${req.body.username}`);
|
|
}
|
|
if (member._id.equals(res.locals.room.owner._id)) {
|
|
throw new SiteError(400, "You can't invite yourself.");
|
|
}
|
|
|
|
await chatService.sendRoomInvite(res.locals.room, member, req.body);
|
|
|
|
const displayList = this.createDisplayList('invite create');
|
|
displayList.showNotification(
|
|
`Chat room invite sent to ${member.displayName || member.username}!`,
|
|
'success',
|
|
'top-left',
|
|
5000,
|
|
);
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to create room invitation', { error });
|
|
return res.status(error.statusCode || 500).json({
|
|
success: false,
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
async postRoomUpdate (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.room = await chatService.updateRoom(res.locals.room, req.body);
|
|
res.redirect(`/chat/room/${res.locals.room._id}`);
|
|
} catch (error) {
|
|
this.log.error('failed to update chat room', {
|
|
// roomId: res.locals.room._id,
|
|
error,
|
|
});
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async postRoomCreate (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.room = await chatService.createRoom(req.user, req.body);
|
|
res.redirect(`/chat/room/${res.locals.room._id}`);
|
|
} catch (error) {
|
|
this.log.error('failed to create chat room', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async getRoomEditor (req, res) {
|
|
res.render('chat/room/editor');
|
|
}
|
|
|
|
async getRoomForm (req, res, next) {
|
|
const validFormNames = [
|
|
'invite-member',
|
|
];
|
|
const formName = req.params.formName;
|
|
if (validFormNames.indexOf(formName) === -1) {
|
|
return next(new SiteError(404, 'Form not found'));
|
|
}
|
|
try {
|
|
res.render(`chat/room/form/${formName}`);
|
|
} catch (error) {
|
|
this.log.error('failed to render form', { formName, error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async getRoomInviteView (req, res) {
|
|
res.render('chat/room/invite/view');
|
|
}
|
|
|
|
async getRoomInviteHome (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.invites = {
|
|
new: await chatService.getRoomInvites(res.locals.room, 'new'),
|
|
accepted: await chatService.getRoomInvites(res.locals.room, 'accepted'),
|
|
rejected: await chatService.getRoomInvites(res.locals.room, 'rejected'),
|
|
};
|
|
res.render('chat/room/invite');
|
|
} catch (error) {
|
|
this.log.error('failed to render the room invites view', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async getRoomSettings (req, res) {
|
|
res.render('chat/room/editor');
|
|
}
|
|
|
|
async getRoomView (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.pageTitle = res.locals.room.name;
|
|
|
|
const pagination = { skip: 0, cpp: 20 };
|
|
res.locals.chatMessages = await chatService.getChannelHistory(res.locals.room, pagination);
|
|
|
|
res.render('chat/room/view');
|
|
} catch (error) {
|
|
this.log.error('failed to render chat room view', { roomId: req.params.roomId, error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async getRoomHome (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.pagination = this.getPaginationParameters(req, 20);
|
|
res.locals.publicRooms = await chatService.getPublicRooms(req.user, res.locals.pagination);
|
|
res.render('chat/room/index');
|
|
} catch (error) {
|
|
this.log.error('failed to render room home', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async getHome (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
res.locals.pageTitle = 'Chat Home';
|
|
|
|
res.locals.pagination = this.getPaginationParameters(req, 20);
|
|
|
|
const roomIds = [ ];
|
|
res.locals.ownedChatRooms.forEach((room) => roomIds.push(room._id));
|
|
res.locals.joinedChatRooms.forEach((room) => roomIds.push(room._id));
|
|
res.locals.timeline = await chatService.getMultiRoomTimeline(roomIds, res.locals.pagination);
|
|
|
|
res.render('chat/index');
|
|
} catch (error) {
|
|
this.log.error('failed to render chat home', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async deleteInvite (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
if (res.locals.room.owner._id.equals(req.user._id)) {
|
|
throw new SiteError(403, 'This is not your invitiation');
|
|
}
|
|
|
|
await chatService.deleteRoomInvite(res.locals.invite);
|
|
|
|
const displayList = this.createDisplayList('delete chat invite');
|
|
displayList.removeElement(`li[data-invite-id="${res.locals.invite._id}"]`);
|
|
displayList.showNotification(
|
|
`Invitation to ${res.locals.invite.member.displayName || res.locals.invite.member.username} deleted successfully`,
|
|
'success',
|
|
'top-left',
|
|
5000,
|
|
);
|
|
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to delete chat room invite', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
|
|
async deleteRoom (req, res, next) {
|
|
const { chat: chatService } = this.dtp.services;
|
|
try {
|
|
if (res.locals.room.owner._id.equals(req.user._id)) {
|
|
throw new SiteError(403, 'This is not your chat room');
|
|
}
|
|
|
|
await chatService.deleteRoom(res.locals.room);
|
|
|
|
const displayList = this.createDisplayList('delete chat invite');
|
|
displayList.navigateTo('/chat');
|
|
|
|
res.status(200).json({ success: true, displayList });
|
|
} catch (error) {
|
|
this.log.error('failed to delete chat room invite', { error });
|
|
return next(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
slug: 'chat',
|
|
name: 'chat',
|
|
create: async (dtp) => { return new ChatController(dtp); },
|
|
};
|