Create new interfaces for objects transmitted using the REST API

Changes:
- Create new IContact, IAccount, and IConversation interfaces in common/
    - These interfaces represent the serialized versions of the models which are transferred
    - The client models are classes which implement these interfaces
- Create new LookupResult interface for nameserver lookup results
- Create new IConversationMember interface for conversation members
    - The client interface ConversationMember extends this interface to have a Contact field rather than IContact
- Create new ConversationInfos interface for conversation infos
- Create new ContactDetails interface for contact details (used by contacts routes)
- Move request and response body interfaces into common/
- Merge AccountConfig into AccountDetails interface
- Create interfaces for server-only objects:
    - ConversationMemberInfos
    - ConversationRequestMetadata
- Ensure interfaces in jami-signal-interfaces.ts do not contain fields with JamiSwig types
- Rename models/ filenames to camelCase as they are not components
- Rewrite client models to have proper TypeScript accessors and remove unused getters
- Rewrite how client models are initialized from the serialized interface using .fromInterface static methods
- Make client models implement the interfaces in common/ for consistency
- Remove unneeded _next parameter for Express.js route handlers
- Use Partial<T> for all Express.js request body types on server
- Type all Axios response body types with interfaces

GitLab: #92
Change-Id: I4b2c75ac632ec5d9bf12a874a5ba04467c76fa6d
diff --git a/client/src/contexts/AuthProvider.tsx b/client/src/contexts/AuthProvider.tsx
index d0d88cc..94f3dcf 100644
--- a/client/src/contexts/AuthProvider.tsx
+++ b/client/src/contexts/AuthProvider.tsx
@@ -16,13 +16,13 @@
  * <https://www.gnu.org/licenses/>.
  */
 import axios, { AxiosInstance } from 'axios';
-import { HttpStatusCode } from 'jami-web-common';
+import { HttpStatusCode, IAccount } from 'jami-web-common';
 import { useCallback, useEffect, useMemo, useState } from 'react';
 import { useNavigate } from 'react-router-dom';
 
 import ProcessingRequest from '../components/ProcessingRequest';
 import { createOptionalContext } from '../hooks/createOptionalContext';
-import { Account } from '../models/Account';
+import { Account } from '../models/account';
 import { apiUrl } from '../utils/constants';
 import { WithChildren } from '../utils/utils';
 
@@ -91,7 +91,7 @@
       return;
     }
 
-    axiosInstance.get('/account').then(({ data }) => setAccount(Account.from(data)));
+    axiosInstance.get<IAccount>('/account').then(({ data }) => setAccount(Account.fromInterface(data)));
   }, [axiosInstance, logout]);
 
   if (!token || !account || !axiosInstance) {
@@ -104,7 +104,7 @@
         token,
         logout,
         account,
-        accountId: account.getId(),
+        accountId: account.id,
         axiosInstance,
       }}
     >
diff --git a/client/src/contexts/CallProvider.tsx b/client/src/contexts/CallProvider.tsx
index 5464228..88951a9 100644
--- a/client/src/contexts/CallProvider.tsx
+++ b/client/src/contexts/CallProvider.tsx
@@ -156,7 +156,7 @@
   // TODO: This logic will have to change to support multiple people in a call. Could we move this logic to the server?
   //       The client could make a single request with the conversationId, and the server would be tasked with sending
   //       all the individual requests to the members of the conversation.
-  const contactUri = useMemo(() => conversation.getFirstMember().contact.getUri(), [conversation]);
+  const contactUri = useMemo(() => conversation.getFirstMember().contact.uri, [conversation]);
 
   useEffect(() => {
     if (callStatus !== CallStatus.InCall) {
diff --git a/client/src/contexts/ConversationProvider.tsx b/client/src/contexts/ConversationProvider.tsx
index b927278..8d5f7b5 100644
--- a/client/src/contexts/ConversationProvider.tsx
+++ b/client/src/contexts/ConversationProvider.tsx
@@ -21,7 +21,7 @@
 import LoadingPage from '../components/Loading';
 import { createOptionalContext } from '../hooks/createOptionalContext';
 import { useUrlParams } from '../hooks/useUrlParams';
-import { Conversation } from '../models/Conversation';
+import { Conversation } from '../models/conversation';
 import { ConversationRouteParams } from '../router';
 import { useConversationQuery } from '../services/conversationQueries';
 import { WithChildren } from '../utils/utils';
@@ -51,7 +51,7 @@
 
   useEffect(() => {
     if (conversationQuery.isSuccess) {
-      const conversation = Conversation.from(accountId, conversationQuery.data);
+      const conversation = Conversation.fromInterface(conversationQuery.data);
       setConversation(conversation);
     }
   }, [accountId, conversationQuery.isSuccess, conversationQuery.data]);
diff --git a/client/src/contexts/MessengerProvider.tsx b/client/src/contexts/MessengerProvider.tsx
index 3ec6164..51a19fb 100644
--- a/client/src/contexts/MessengerProvider.tsx
+++ b/client/src/contexts/MessengerProvider.tsx
@@ -15,11 +15,11 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { ConversationMessage, WebSocketMessageType } from 'jami-web-common';
+import { ConversationMessage, IConversation, LookupResult, WebSocketMessageType } from 'jami-web-common';
 import { createContext, ReactNode, useContext, useEffect, useState } from 'react';
 
-import { Contact } from '../models/Contact';
-import { Conversation } from '../models/Conversation';
+import { Contact } from '../models/contact';
+import { Conversation } from '../models/conversation';
 import { setRefreshFromSlice } from '../redux/appSlice';
 import { useAppDispatch, useAppSelector } from '../redux/hooks';
 import { SetState } from '../utils/utils';
@@ -61,11 +61,13 @@
   useEffect(() => {
     const controller = new AbortController();
     axiosInstance
-      .get<Conversation[]>('/conversations', {
+      .get<IConversation[]>('/conversations', {
         signal: controller.signal,
       })
       .then(({ data }) => {
-        setConversations(Object.values(data).map((c) => Conversation.from(accountId, c)));
+        setConversations(
+          Object.values(data).map((conversationInterface) => Conversation.fromInterface(conversationInterface))
+        );
       });
     // return () => controller.abort()
   }, [axiosInstance, accountId, refresh]);
@@ -89,15 +91,13 @@
   useEffect(() => {
     if (!searchQuery) return;
     const controller = new AbortController();
-    // TODO: Type properly https://git.jami.net/savoirfairelinux/jami-web/-/issues/92
     axiosInstance
-      .get<{ state: number; address: string; username: string }>(`/ns/username/${searchQuery}`, {
+      .get<LookupResult>(`/ns/username/${searchQuery}`, {
         signal: controller.signal,
       })
       .then(({ data }) => {
-        const contact = new Contact(data.address);
-        contact.setRegisteredName(data.username);
-        setSearchResults(contact ? Conversation.fromSingleContact(accountId, contact) : undefined);
+        const contact = new Contact(data.address, data.username);
+        setSearchResults(contact ? Conversation.fromSingleContact(contact) : undefined);
       })
       .catch(() => {
         setSearchResults(undefined);
diff --git a/client/src/contexts/WebRtcProvider.tsx b/client/src/contexts/WebRtcProvider.tsx
index 1ce3ff6..b7733bc 100644
--- a/client/src/contexts/WebRtcProvider.tsx
+++ b/client/src/contexts/WebRtcProvider.tsx
@@ -66,17 +66,17 @@
     if (!webRtcConnection && account) {
       const iceServers: RTCIceServer[] = [];
 
-      if (account.getDetails()['TURN.enable'] === 'true') {
+      if (account.details['TURN.enable'] === 'true') {
         iceServers.push({
-          urls: 'turn:' + account.getDetails()['TURN.server'],
-          username: account.getDetails()['TURN.username'],
-          credential: account.getDetails()['TURN.password'],
+          urls: 'turn:' + account.details['TURN.server'],
+          username: account.details['TURN.username'],
+          credential: account.details['TURN.password'],
         });
       }
 
-      if (account.getDetails()['STUN.enable'] === 'true') {
+      if (account.details['STUN.enable'] === 'true') {
         iceServers.push({
-          urls: 'stun:' + account.getDetails()['STUN.server'],
+          urls: 'stun:' + account.details['STUN.server'],
         });
       }
 
@@ -123,7 +123,7 @@
   const [iceCandidateQueue, setIceCandidateQueue] = useState<RTCIceCandidate[]>([]);
 
   // TODO: This logic will have to change to support multiple people in a call
-  const contactUri = useMemo(() => conversation.getFirstMember().contact.getUri(), [conversation]);
+  const contactUri = useMemo(() => conversation.getFirstMember().contact.uri, [conversation]);
 
   const getMediaDevices = useCallback(async (): Promise<MediaDevicesInfo> => {
     try {