blob: 0a6b0b296e5f8fa053674779c5336986776734c8 [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,
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050024 IConversationMember,
idillon07d31cc2022-12-06 22:40:14 -050025 IConversationSummary,
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050026 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
idillon07d31cc2022-12-06 22:40:14 -050036async function createConversationSummary(
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050037 accountId: string,
38 accountUri: string,
39 conversationId: string
idillon07d31cc2022-12-06 22:40:14 -050040): Promise<IConversationSummary | 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
idillon07d31cc2022-12-06 22:40:14 -050048 const membersNames = [];
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);
idillon07d31cc2022-12-06 22:40:14 -050057 membersNames.push(username ?? member.uri);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040058 }
59
idillon07d31cc2022-12-06 22:40:14 -050060 const lastMessage = (await jamid.getConversationMessages(accountId, conversationId, '', 1))[0];
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040061
62 return {
63 id: conversationId,
idillon07d31cc2022-12-06 22:40:14 -050064 avatar: infos.avatar,
65 title: infos.title,
66 membersNames,
67 lastMessage,
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040068 };
69}
70
71export const conversationRouter = Router();
72
73conversationRouter.use(authenticateToken);
74
75conversationRouter.get(
76 '/',
77 asyncHandler(async (_req, res) => {
78 const accountId = res.locals.accountId;
79
80 // Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
81 const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
82
83 const conversationIds = jamid.getConversationIds(accountId);
84
idillon07d31cc2022-12-06 22:40:14 -050085 const conversationsSummaries = [];
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040086 for (const conversationId of conversationIds) {
idillon07d31cc2022-12-06 22:40:14 -050087 const conversationSummary = await createConversationSummary(accountId, accountUri, conversationId);
88 conversationsSummaries.push(conversationSummary);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040089 }
90
idillon07d31cc2022-12-06 22:40:14 -050091 res.send(conversationsSummaries);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -040092 })
93);
94
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -050095conversationRouter.post(
96 '/',
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -050097 (req: Request<ParamsDictionary, ContactDetails | string, Partial<NewConversationRequestBody>>, res) => {
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -050098 const { members } = req.body;
99 if (members === undefined || members.length !== 1) {
100 res.status(HttpStatusCode.BadRequest).send('Missing members or more than one member in body');
101 return;
102 }
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400103
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500104 const accountId = res.locals.accountId;
105
106 const contactId = members[0];
107 jamid.addContact(accountId, contactId);
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -0500108 // We need to manually send a conversation request
109 jamid.sendTrustRequest(accountId, contactId);
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500110
111 const contactDetails = jamid.getContactDetails(accountId, contactId);
112 if (Object.keys(contactDetails).length === 0) {
113 res.status(HttpStatusCode.NotFound).send('No such member found');
114 return;
115 }
116
117 res.send(contactDetails);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400118 }
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500119);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400120
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400121conversationRouter.get(
122 '/:conversationId',
123 asyncHandler(async (req, res) => {
124 const accountId = res.locals.accountId;
125 const conversationId = req.params.conversationId;
126
127 // Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
128 const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
129
idillon07d31cc2022-12-06 22:40:14 -0500130 const conversationSummary = await createConversationSummary(accountId, accountUri, conversationId);
131 if (conversationSummary === undefined) {
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500132 res.status(HttpStatusCode.NotFound).send('No such conversation found');
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400133 return;
134 }
135
idillon07d31cc2022-12-06 22:40:14 -0500136 res.send(conversationSummary);
137 })
138);
139
140conversationRouter.get(
141 '/:conversationId/infos',
142 asyncHandler(async (req, res) => {
143 const accountId = res.locals.accountId;
144 const conversationId = req.params.conversationId;
145
146 const infos = jamid.getConversationInfos(accountId, conversationId);
147 if (Object.keys(infos).length === 0) {
148 res.status(HttpStatusCode.NotFound).send('No such conversation found');
149 }
150
151 res.send(infos);
152 })
153);
154
155conversationRouter.get(
156 '/:conversationId/members',
157 asyncHandler(async (req, res) => {
158 const accountId = res.locals.accountId;
159 const conversationId = req.params.conversationId;
160
161 // Retrieve the URI of the current account (Account.username actually stores the URI rather than the username)
162 const accountUri = jamid.getAccountDetails(accountId)['Account.username'];
163
164 const infos = jamid.getConversationInfos(accountId, conversationId);
165 if (Object.keys(infos).length === 0) {
166 res.status(HttpStatusCode.NotFound).send('No such conversation found');
167 return;
168 }
169
170 const members = jamid.getConversationMembers(accountId, conversationId);
171
172 const namedMembers: IConversationMember[] = [];
173 for (const member of members) {
174 // Exclude current user from returned conversation members
175 if (member.uri === accountUri) {
176 continue;
177 }
178
179 // Add usernames for conversation members
180 const { username } = await jamid.lookupAddress(member.uri, accountId);
181 namedMembers.push({
182 role: member.role,
183 contact: {
184 uri: member.uri,
185 registeredName: username,
186 },
187 });
188 }
189
190 res.send(namedMembers);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400191 })
192);
193
194conversationRouter.get(
195 '/:conversationId/messages',
196 asyncHandler(async (req, res) => {
197 const accountId = res.locals.accountId;
198 const conversationId = req.params.conversationId;
199
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500200 const infos = jamid.getConversationInfos(accountId, conversationId);
201 if (Object.keys(infos).length === 0) {
202 res.status(HttpStatusCode.NotFound).send('No such conversation found');
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400203 return;
204 }
205
206 const messages = await jamid.getConversationMessages(accountId, conversationId);
207 res.send(messages);
208 })
209);
210
211conversationRouter.post(
212 '/:conversationId/messages',
Misha Krieger-Raynauldcfa44302022-11-30 18:36:36 -0500213 (req: Request<ParamsDictionary, string, Partial<NewMessageRequestBody>>, res) => {
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400214 const { message } = req.body;
215 if (message === undefined) {
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500216 res.status(HttpStatusCode.BadRequest).send('Missing message in body');
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400217 return;
218 }
219
Misha Krieger-Raynauldcb11bba2022-11-11 18:08:33 -0500220 const accountId = res.locals.accountId;
221 const conversationId = req.params.conversationId;
222
223 const infos = jamid.getConversationInfos(accountId, conversationId);
224 if (Object.keys(infos).length === 0) {
225 res.status(HttpStatusCode.NotFound).send('No such conversation found');
226 return;
227 }
228
229 jamid.sendConversationMessage(accountId, conversationId, message);
230 res.sendStatus(HttpStatusCode.NoContent);
Misha Krieger-Raynauld8a381da2022-11-03 17:37:51 -0400231 }
232);
idillon07d31cc2022-12-06 22:40:14 -0500233
234conversationRouter.delete('/:conversationId', (req, res) => {
235 jamid.removeConversation(res.locals.accountId, req.params.conversationId);
236 res.sendStatus(HttpStatusCode.NoContent);
237});