blob: 0a6b0b296e5f8fa053674779c5336986776734c8 [file] [log] [blame]
/*
* Copyright (C) 2022 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
import { Request, Router } from 'express';
import asyncHandler from 'express-async-handler';
import { ParamsDictionary } from 'express-serve-static-core';
import {
ContactDetails,
HttpStatusCode,
IConversationMember,
IConversationSummary,
NewConversationRequestBody,
NewMessageRequestBody,
} from 'jami-web-common';
import { Container } from 'typedi';
import { Jamid } from '../jamid/jamid.js';
import { authenticateToken } from '../middleware/auth.js';
const jamid = Container.get(Jamid);
async function createConversationSummary(
accountId: string,
accountUri: string,
conversationId: string
): Promise<IConversationSummary | undefined> {
const infos = jamid.getConversationInfos(accountId, conversationId);
if (Object.keys(infos).length === 0) {
return undefined;
}
const members = jamid.getConversationMembers(accountId, conversationId);
const membersNames = [];
for (const member of members) {
// Exclude current user from returned conversation members
if (member.uri === accountUri) {
continue;
}
// Add usernames for conversation members
const { username } = await jamid.lookupAddress(member.uri, accountId);
membersNames.push(username ?? member.uri);
}
const lastMessage = (await jamid.getConversationMessages(accountId, conversationId, '', 1))[0];
return {
id: conversationId,
avatar: infos.avatar,
title: infos.title,
membersNames,
lastMessage,
};
}
export const conversationRouter = Router();
conversationRouter.use(authenticateToken);
conversationRouter.get(
'/',
asyncHandler(async (_req, res) => {
const accountId = res.locals.accountId;
// Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
const conversationIds = jamid.getConversationIds(accountId);
const conversationsSummaries = [];
for (const conversationId of conversationIds) {
const conversationSummary = await createConversationSummary(accountId, accountUri, conversationId);
conversationsSummaries.push(conversationSummary);
}
res.send(conversationsSummaries);
})
);
conversationRouter.post(
'/',
(req: Request<ParamsDictionary, ContactDetails | string, Partial<NewConversationRequestBody>>, res) => {
const { members } = req.body;
if (members === undefined || members.length !== 1) {
res.status(HttpStatusCode.BadRequest).send('Missing members or more than one member in body');
return;
}
const accountId = res.locals.accountId;
const contactId = members[0];
jamid.addContact(accountId, contactId);
// We need to manually send a conversation request
jamid.sendTrustRequest(accountId, contactId);
const contactDetails = jamid.getContactDetails(accountId, contactId);
if (Object.keys(contactDetails).length === 0) {
res.status(HttpStatusCode.NotFound).send('No such member found');
return;
}
res.send(contactDetails);
}
);
conversationRouter.get(
'/:conversationId',
asyncHandler(async (req, res) => {
const accountId = res.locals.accountId;
const conversationId = req.params.conversationId;
// Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
const conversationSummary = await createConversationSummary(accountId, accountUri, conversationId);
if (conversationSummary === undefined) {
res.status(HttpStatusCode.NotFound).send('No such conversation found');
return;
}
res.send(conversationSummary);
})
);
conversationRouter.get(
'/:conversationId/infos',
asyncHandler(async (req, res) => {
const accountId = res.locals.accountId;
const conversationId = req.params.conversationId;
const infos = jamid.getConversationInfos(accountId, conversationId);
if (Object.keys(infos).length === 0) {
res.status(HttpStatusCode.NotFound).send('No such conversation found');
}
res.send(infos);
})
);
conversationRouter.get(
'/:conversationId/members',
asyncHandler(async (req, res) => {
const accountId = res.locals.accountId;
const conversationId = req.params.conversationId;
// Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
const infos = jamid.getConversationInfos(accountId, conversationId);
if (Object.keys(infos).length === 0) {
res.status(HttpStatusCode.NotFound).send('No such conversation found');
return;
}
const members = jamid.getConversationMembers(accountId, conversationId);
const namedMembers: IConversationMember[] = [];
for (const member of members) {
// Exclude current user from returned conversation members
if (member.uri === accountUri) {
continue;
}
// Add usernames for conversation members
const { username } = await jamid.lookupAddress(member.uri, accountId);
namedMembers.push({
role: member.role,
contact: {
uri: member.uri,
registeredName: username,
},
});
}
res.send(namedMembers);
})
);
conversationRouter.get(
'/:conversationId/messages',
asyncHandler(async (req, res) => {
const accountId = res.locals.accountId;
const conversationId = req.params.conversationId;
const infos = jamid.getConversationInfos(accountId, conversationId);
if (Object.keys(infos).length === 0) {
res.status(HttpStatusCode.NotFound).send('No such conversation found');
return;
}
const messages = await jamid.getConversationMessages(accountId, conversationId);
res.send(messages);
})
);
conversationRouter.post(
'/:conversationId/messages',
(req: Request<ParamsDictionary, string, Partial<NewMessageRequestBody>>, res) => {
const { message } = req.body;
if (message === undefined) {
res.status(HttpStatusCode.BadRequest).send('Missing message in body');
return;
}
const accountId = res.locals.accountId;
const conversationId = req.params.conversationId;
const infos = jamid.getConversationInfos(accountId, conversationId);
if (Object.keys(infos).length === 0) {
res.status(HttpStatusCode.NotFound).send('No such conversation found');
return;
}
jamid.sendConversationMessage(accountId, conversationId, message);
res.sendStatus(HttpStatusCode.NoContent);
}
);
conversationRouter.delete('/:conversationId', (req, res) => {
jamid.removeConversation(res.locals.accountId, req.params.conversationId);
res.sendStatus(HttpStatusCode.NoContent);
});