Create default moderators API routes

GitLab: #93
Change-Id: Idc107915f4248842949efa844e4e17601650d375
diff --git a/server/src/app.ts b/server/src/app.ts
index 32f930e..f7c860a 100644
--- a/server/src/app.ts
+++ b/server/src/app.ts
@@ -27,6 +27,7 @@
 import { callRouter } from './routers/call-router.js';
 import { contactsRouter } from './routers/contacts-router.js';
 import { conversationRouter } from './routers/conversation-router.js';
+import { defaultModeratorsRouter } from './routers/default-moderators-router.js';
 import { nameserverRouter } from './routers/nameserver-router.js';
 
 @Service()
@@ -43,7 +44,7 @@
     app.use('/auth', authRouter);
     app.use('/account', accountRouter);
     app.use('/contacts', contactsRouter);
-    // TODO: Moderator routes: https://git.jami.net/savoirfairelinux/jami-web/-/issues/93
+    app.use('/default-moderators', defaultModeratorsRouter);
     app.use('/conversations', conversationRouter);
     app.use('/calls', callRouter);
     app.use('/ns', nameserverRouter);
diff --git a/server/src/jamid/jami-swig.ts b/server/src/jamid/jami-swig.ts
index 484aad1..9d2959f 100644
--- a/server/src/jamid/jami-swig.ts
+++ b/server/src/jamid/jami-swig.ts
@@ -63,7 +63,6 @@
   getAccountDetails(accountId: string): StringMap;
   getVolatileAccountDetails(accountId: string): StringMap;
   setAccountDetails(accountId: string, details: StringMap): void;
-  setAccountActive(accountId: string, active: Bool): void;
 
   addAccount(details: StringMap): string;
   removeAccount(accountId: string): void;
@@ -84,7 +83,7 @@
   getContactDetails(accountId: string, contactId: string): StringMap;
 
   getDefaultModerators(accountId: string): StringVect;
-  setDefaultModerators(accountId: string, uri: string, state: boolean): void;
+  setDefaultModerator(accountId: string, uri: string, state: boolean): void;
 
   getConversations(accountId: string): StringVect;
   conversationInfos(accountId: string, conversationId: string): StringMap;
diff --git a/server/src/jamid/jamid.ts b/server/src/jamid/jamid.ts
index 957f370..78cd71a 100644
--- a/server/src/jamid/jamid.ts
+++ b/server/src/jamid/jamid.ts
@@ -260,10 +260,18 @@
     return stringMapToRecord(this.jamiSwig.getContactDetails(accountId, contactId));
   }
 
-  getDefaultModerators(accountId: string): string[] {
+  getDefaultModeratorUris(accountId: string): string[] {
     return stringVectToArray(this.jamiSwig.getDefaultModerators(accountId));
   }
 
+  addDefaultModerator(accountId: string, contactId: string): void {
+    this.jamiSwig.setDefaultModerator(accountId, contactId, true);
+  }
+
+  removeDefaultModerator(accountId: string, contactId: string): void {
+    this.jamiSwig.setDefaultModerator(accountId, contactId, false);
+  }
+
   getConversationIds(accountId: string): string[] {
     return stringVectToArray(this.jamiSwig.getConversations(accountId));
   }
diff --git a/server/src/routers/account-router.ts b/server/src/routers/account-router.ts
index 59bad05..c870f83 100644
--- a/server/src/routers/account-router.ts
+++ b/server/src/routers/account-router.ts
@@ -16,6 +16,7 @@
  * <https://www.gnu.org/licenses/>.
  */
 import { Request, Router } from 'express';
+import asyncHandler from 'express-async-handler';
 import { ParamsDictionary } from 'express-serve-static-core';
 import { AccountDetails, HttpStatusCode } from 'jami-web-common';
 import { Container } from 'typedi';
@@ -39,17 +40,33 @@
 // TODO: If tokens can be generated on one daemon and used on another (transferrable between daemons),
 // then add middleware to check that the currently logged-in accountId is stored in this daemon instance
 
-accountRouter.get('/', (_req, res) => {
-  const accountId = res.locals.accountId;
+// TODO: Do we really need this route to return the default moderators?
+// It would be cleaner just to GET /default-moderators for this
+accountRouter.get(
+  '/',
+  asyncHandler(async (_req, res) => {
+    const accountId = res.locals.accountId;
 
-  res.json({
-    id: accountId,
-    details: jamid.getAccountDetails(accountId),
-    volatileDetails: jamid.getVolatileAccountDetails(accountId),
-    defaultModerators: jamid.getDefaultModerators(accountId),
-    devices: jamid.getDevices(accountId),
-  });
-});
+    // Add usernames for default moderators
+    const defaultModeratorUris = jamid.getDefaultModeratorUris(accountId);
+    const namedDefaultModerators = [];
+    for (const defaultModeratorUri of defaultModeratorUris) {
+      const { username } = await jamid.lookupAddress(defaultModeratorUri, accountId);
+      namedDefaultModerators.push({
+        uri: defaultModeratorUri,
+        registeredName: username,
+      });
+    }
+
+    res.json({
+      id: accountId,
+      details: jamid.getAccountDetails(accountId),
+      volatileDetails: jamid.getVolatileAccountDetails(accountId),
+      defaultModerators: namedDefaultModerators,
+      devices: jamid.getDevices(accountId),
+    });
+  })
+);
 
 accountRouter.patch('/', (req, res) => {
   const accountId = res.locals.accountId;
diff --git a/server/src/routers/default-moderators-router.ts b/server/src/routers/default-moderators-router.ts
new file mode 100644
index 0000000..797aac3
--- /dev/null
+++ b/server/src/routers/default-moderators-router.ts
@@ -0,0 +1,60 @@
+/*
+ * 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 { Router } from 'express';
+import asyncHandler from 'express-async-handler';
+import { HttpStatusCode } 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);
+
+export const defaultModeratorsRouter = Router();
+
+defaultModeratorsRouter.use(authenticateToken);
+
+defaultModeratorsRouter.get(
+  '/',
+  asyncHandler(async (_req, res) => {
+    const accountId = res.locals.accountId;
+
+    // Add usernames for default moderators
+    const defaultModeratorUris = jamid.getDefaultModeratorUris(accountId);
+    const namedDefaultModerators = [];
+    for (const defaultModeratorUri of defaultModeratorUris) {
+      const { username } = await jamid.lookupAddress(defaultModeratorUri, accountId);
+      namedDefaultModerators.push({
+        uri: defaultModeratorUri,
+        registeredName: username,
+      });
+    }
+
+    res.send(namedDefaultModerators);
+  })
+);
+
+defaultModeratorsRouter.put('/:contactId', (req, res) => {
+  jamid.addDefaultModerator(res.locals.accountId, req.params.contactId);
+  res.end();
+});
+
+defaultModeratorsRouter.delete('/:contactId', (req, res) => {
+  jamid.removeDefaultModerator(res.locals.accountId, req.params.contactId);
+  res.sendStatus(HttpStatusCode.NoContent);
+});