Reorganize server files and address TODO comments

Changes:
- Remove unneeded dependencies from package.json
- Remove unneeded async build() methods from services
    - Use constructor as often as possible
- Rename and move storage services for clarity
    - creds.ts -> accounts.ts, and creds.json -> accounts.json
    - admin-config.ts -> admin-account.ts
    - vault.ts -> signing-keys.ts
- Rename ws.ts to websocket-server.ts for clarity and consistency
- Make WebSocketServer initialize using constructor and bind server upgrade to WebSocketServer.upgrade
- Remove unused send-account-message endpoint from account-router.ts
- Set issuer and audience claims for JWT
- Create new utils/jwt.ts file to remove code duplication for JWT signing and verifying
- Delete utils.ts and merge it with jami-swig.ts
- Handle potentially undefined types in jami-swig.ts
- Replace hard to read one-liners with functions in jami-swig.ts
- Rename types in jami-swig.ts for consistency with daemon
- Remove handled/answered TODO comments
- Remove TODO comment about using .env for jamid.node as it does not work for require()

GitLab: #87
Change-Id: I1e5216ffa79ea34dd7e9b61540fb7e37d1f66c9f
diff --git a/server/src/jamid/jami-swig.ts b/server/src/jamid/jami-swig.ts
index 7182270..3b4ddd8 100644
--- a/server/src/jamid/jami-swig.ts
+++ b/server/src/jamid/jami-swig.ts
@@ -15,36 +15,66 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { Constructable } from '../interfaces.js';
-import { itMap, itRange, itToArr, itToRecord } from './utils.js';
+import { Constructable } from '../interfaces/constructable.js';
 
-interface SwigVec<T> {
+interface SwigVect<T> {
   size(): number;
-  get(i: number): T; // TODO: | undefined;
+  get(index: number): T | undefined;
 }
 
 interface SwigMap<T, U> {
-  keys(): SwigVec<T>;
-  get(k: T): U; // TODO: | undefined;
-  set(k: T, v: U): void;
+  keys(): SwigVect<T>;
+  get(key: T): U | undefined;
+  set(key: T, value: U): void;
 }
 
-// TODO: Review these conversion functions
-const swigVecToIt = <T>(v: SwigVec<T>) => itMap(itRange(0, v.size()), (i) => v.get(i));
-const swigMapToIt = <T, U>(m: SwigMap<T, U>) => itMap(swigVecToIt(m.keys()), (k): [T, U] => [k, m.get(k)]);
-
 // export type IntVect = SwigVec<number>;
 // export type UintVect = SwigVec<number>;
 // export type FloatVect = SwigVec<number>;
-export type StringVect = SwigVec<string>;
+export type StringVect = SwigVect<string>;
 // export type IntegerMap = SwigMap<string, number>;
 export type StringMap = SwigMap<string, string>;
-export type VectMap = SwigVec<StringMap>;
+export type VectMap = SwigVect<StringMap>;
 // export type Blob = SwigVec<number>;
 
-export const stringVectToArray = (sv: StringVect) => itToArr(swigVecToIt(sv));
-export const stringMapToRecord = (sm: StringMap) => itToRecord(swigMapToIt(sm));
-export const vectMapToRecordArray = (vm: VectMap) => itToArr(itMap(swigVecToIt(vm), stringMapToRecord));
+function* swigVectToIt<T>(swigVect: SwigVect<T>) {
+  const size = swigVect.size();
+  for (let i = 0; i < size; i++) {
+    yield swigVect.get(i)!;
+  }
+}
+
+function* swigMapToIt<T, U>(swigMap: SwigMap<T, U>) {
+  const keys = swigVectToIt(swigMap.keys());
+  for (const key of keys) {
+    const value = swigMap.get(key)!;
+    yield [key, value];
+  }
+}
+
+export function stringVectToArray(stringVect: StringVect): string[] {
+  const elements = swigVectToIt(stringVect);
+  return Array.from(elements);
+}
+
+export function stringMapToRecord(stringMap: StringMap): Record<string, string> {
+  const keyValuePairs = swigMapToIt(stringMap);
+  const record: Record<string, string> = {};
+  for (const [key, value] of keyValuePairs) {
+    record[key] = value;
+  }
+  return record;
+}
+
+export function vectMapToRecordArray(vectMap: VectMap): Record<string, string>[] {
+  const stringMaps = swigVectToIt(vectMap);
+  const records = [];
+  for (const stringMap of stringMaps) {
+    const record = stringMapToRecord(stringMap);
+    records.push(record);
+  }
+  return records;
+}
 
 /**
  * Non-exhaustive list of properties for JamiSwig.
diff --git a/server/src/jamid/jamid.ts b/server/src/jamid/jamid.ts
index 0c54adf..773b298 100644
--- a/server/src/jamid/jamid.ts
+++ b/server/src/jamid/jamid.ts
@@ -15,6 +15,8 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
+import { createRequire } from 'node:module';
+
 import {
   AccountDetails,
   AccountTextMessage,
@@ -28,7 +30,7 @@
 import { filter, firstValueFrom, map, Subject } from 'rxjs';
 import { Service } from 'typedi';
 
-import { Ws } from '../ws.js';
+import { WebSocketServer } from '../websocket/websocket-server.js';
 import { JamiSignal } from './jami-signal.js';
 import {
   AccountDetailsChanged,
@@ -47,22 +49,19 @@
   VolatileDetailsChanged,
 } from './jami-signal-interfaces.js';
 import { JamiSwig, StringMap, stringMapToRecord, stringVectToArray, vectMapToRecordArray } from './jami-swig.js';
-import { require } from './utils.js';
 
-// TODO: Mechanism to map account IDs to a list of WebSockets
+const require = createRequire(import.meta.url);
+
 // TODO: Convert Records to interfaces and replace them in common/ (e.g. Contact)
 
 @Service()
 export class Jamid {
-  private readonly jamiSwig: JamiSwig;
-  private readonly usernamesToAccountIds: Map<string, string>;
+  private jamiSwig: JamiSwig;
+  private usernamesToAccountIds = new Map<string, string>();
   private readonly events;
 
-  constructor(private ws: Ws) {
-    // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
-    this.jamiSwig = require('../../jamid.node') as JamiSwig; // TODO: we should put the path in the .env
-
-    this.usernamesToAccountIds = new Map<string, string>();
+  constructor(private webSocketServer: WebSocketServer) {
+    this.jamiSwig = require('../../jamid.node') as JamiSwig;
 
     // Setup signal handlers
     const handlers: Record<string, unknown> = {};
@@ -380,12 +379,17 @@
       const message: WebSocketMessage<any> = JSON.parse(signal.payload['application/json']);
 
       if (message === undefined) {
-        log.debug('Undefined account message');
+        log.warn('Undefined account message');
+        return;
+      }
+
+      if (message.type === undefined || message.data === undefined) {
+        log.warn('Account message is not a valid WebSocketMessage (missing type or data fields)');
         return;
       }
 
       if (!Object.values(WebSocketMessageType).includes(message.type)) {
-        log.warn(`Unhandled account message type: ${message.type}`);
+        log.warn(`Invalid WebSocket message type: ${message.type}`);
         return;
       }
 
@@ -394,10 +398,8 @@
         to: signal.accountId,
         message: message.data.message,
       };
-      message.data = data;
 
-      log.info(`Sending ${JSON.stringify(message)} to ${signal.accountId}`);
-      this.ws.send(signal.accountId, message.type, data);
+      this.webSocketServer.send(signal.accountId, message.type, data);
     });
 
     this.events.onAccountMessageStatusChanged.subscribe((signal) => {
@@ -433,7 +435,7 @@
         conversationId: signal.conversationId,
         message: signal.message,
       };
-      this.ws.send(signal.accountId, WebSocketMessageType.ConversationMessage, data);
+      this.webSocketServer.send(signal.accountId, WebSocketMessageType.ConversationMessage, data);
     });
   }
 }
diff --git a/server/src/jamid/utils.ts b/server/src/jamid/utils.ts
deleted file mode 100644
index f692578..0000000
--- a/server/src/jamid/utils.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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 { createRequire } from 'node:module';
-
-// TODO: Move these functions to jami-swig.ts
-
-export function* itRange(lo: number, hi: number) {
-  for (let i = lo; i < hi; ++i) {
-    yield i;
-  }
-}
-
-export function* itMap<T, U>(it: Iterable<T>, cb: (value: T, index: number) => U) {
-  let i = 0;
-  for (const item of it) {
-    yield cb(item, i++);
-  }
-}
-
-export function* itFilter<T>(it: Iterable<T>, cb: (value: T, index: number) => boolean) {
-  let i = 0;
-  for (const item of it) {
-    if (cb(item, i++)) {
-      yield item;
-    }
-  }
-}
-
-export const itToArr = <T>(it: Iterable<T>) => Array.from(it);
-
-export const itToRecord = <T>(it: Iterable<[string, T]>) => {
-  const r: Record<string, T> = {};
-  for (const [k, v] of it) {
-    r[k] = v;
-  }
-  return r;
-};
-
-export const require = createRequire(import.meta.url);