blob: bd3a9002a6f1fdec3a7f9a9a71a8b7c4e6c471c7 [file] [log] [blame]
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -04001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18import { Request, Router } from 'express';
19import asyncHandler from 'express-async-handler';
20import { ParamsDictionary } from 'express-serve-static-core';
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050021import {
22 ContactDetails,
23 HttpStatusCode,
24 IConversation,
25 IConversationMember,
26 NewConversationRequestBody,
27 NewMessageRequestBody,
28} from 'jami-web-common';
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040029import { Container } from 'typedi';
30
31import { Jamid } from '../jamid/jamid.js';
32import { authenticateToken } from '../middleware/auth.js';
33
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040034const jamid = Container.get(Jamid);
35
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050036async function createConversationResponseObject(
37 accountId: string,
38 accountUri: string,
39 conversationId: string
40): Promise<IConversation | undefined> {
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040041 const infos = jamid.getConversationInfos(accountId, conversationId);
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -050042 if (Object.keys(infos).length === 0) {
43 return undefined;
44 }
45
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040046 const members = jamid.getConversationMembers(accountId, conversationId);
47
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050048 const namedMembers: IConversationMember[] = [];
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040049 for (const member of members) {
50 // Exclude current user from returned conversation members
51 if (member.uri === accountUri) {
52 continue;
53 }
54
55 // Add usernames for conversation members
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040056 const { username } = await jamid.lookupAddress(member.uri, accountId);
57 namedMembers.push({
58 role: member.role,
59 contact: {
60 uri: member.uri,
61 registeredName: username,
62 },
63 });
64 }
65
66 // TODO: Check if messages actually need to be added to response
67 // (does the client really need it for all endpoints, or just the /conversations/conversationId/messages endpoint?)
68 const messages = await jamid.getConversationMessages(accountId, conversationId);
69
70 return {
71 id: conversationId,
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040072 members: namedMembers,
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050073 messages: messages,
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040074 infos: infos,
75 };
76}
77
78export const conversationRouter = Router();
79
80conversationRouter.use(authenticateToken);
81
82conversationRouter.get(
83 '/',
84 asyncHandler(async (_req, res) => {
85 const accountId = res.locals.accountId;
86
87 // Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
88 const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
89
90 const conversationIds = jamid.getConversationIds(accountId);
91
92 const conversations = [];
93 for (const conversationId of conversationIds) {
94 const conversation = await createConversationResponseObject(accountId, accountUri, conversationId);
95 conversations.push(conversation);
96 }
97
98 res.send(conversations);
99 })
100);
101
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500102conversationRouter.post(
103 '/',
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -0500104 (req: Request<ParamsDictionary, ContactDetails | string, Partial<NewConversationRequestBody>>, res) => {
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500105 const { members } = req.body;
106 if (members === undefined || members.length !== 1) {
107 res.status(HttpStatusCode.BadRequest).send('Missing members or more than one member in body');
108 return;
109 }
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400110
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500111 const accountId = res.locals.accountId;
112
113 const contactId = members[0];
114 jamid.addContact(accountId, contactId);
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -0500115 // We need to manually send a conversation request
116 jamid.sendTrustRequest(accountId, contactId);
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500117
118 const contactDetails = jamid.getContactDetails(accountId, contactId);
119 if (Object.keys(contactDetails).length === 0) {
120 res.status(HttpStatusCode.NotFound).send('No such member found');
121 return;
122 }
123
124 res.send(contactDetails);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400125 }
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500126);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400127
128// TODO: Check if we actually need this endpoint to return messages.
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400129// At the moment, /conversations does a lot of work returning all the conversations with the same
130// level of detail as this, and /conversations/messages returns just the messages. Check whether or not
131// this is what we want, and if so, if we can be more economical with client requests.
132conversationRouter.get(
133 '/:conversationId',
134 asyncHandler(async (req, res) => {
135 const accountId = res.locals.accountId;
136 const conversationId = req.params.conversationId;
137
138 // Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
139 const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
140
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500141 const conversation = await createConversationResponseObject(accountId, accountUri, conversationId);
142 if (conversation === undefined) {
143 res.status(HttpStatusCode.NotFound).send('No such conversation found');
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400144 return;
145 }
146
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400147 res.send(conversation);
148 })
149);
150
151conversationRouter.get(
152 '/:conversationId/messages',
153 asyncHandler(async (req, res) => {
154 const accountId = res.locals.accountId;
155 const conversationId = req.params.conversationId;
156
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500157 const infos = jamid.getConversationInfos(accountId, conversationId);
158 if (Object.keys(infos).length === 0) {
159 res.status(HttpStatusCode.NotFound).send('No such conversation found');
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400160 return;
161 }
162
163 const messages = await jamid.getConversationMessages(accountId, conversationId);
164 res.send(messages);
165 })
166);
167
168conversationRouter.post(
169 '/:conversationId/messages',
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -0500170 (req: Request<ParamsDictionary, string, Partial<NewMessageRequestBody>>, res) => {
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400171 const { message } = req.body;
172 if (message === undefined) {
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500173 res.status(HttpStatusCode.BadRequest).send('Missing message in body');
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400174 return;
175 }
176
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500177 const accountId = res.locals.accountId;
178 const conversationId = req.params.conversationId;
179
180 const infos = jamid.getConversationInfos(accountId, conversationId);
181 if (Object.keys(infos).length === 0) {
182 res.status(HttpStatusCode.NotFound).send('No such conversation found');
183 return;
184 }
185
186 jamid.sendConversationMessage(accountId, conversationId, message);
187 res.sendStatus(HttpStatusCode.NoContent);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400188 }
189);