Refactor project for typescript support

Update and move tsconfig.json from client/ to the root folder.
Update docker-compose.yml and .dockerignore.
Update client/webpack.config.js for better typescript support.

Change-Id: I3b0cc88f7c828dc5bfa5ac129352bf3d40189af3
diff --git a/.dockerignore b/.dockerignore
index 2b722b5..66dafd5 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -5,6 +5,10 @@
 
 node_modules/
 dist/
+daemon/
+.env
+dring.node
+jamid.node
 
 client/dist/
 client/node_modules/
diff --git a/JamiDaemon.js b/JamiDaemon.js
index b0e10bd..1a32f4b 100755
--- a/JamiDaemon.js
+++ b/JamiDaemon.js
@@ -21,7 +21,9 @@
 
 import Account from './model/Account.js'
 import Conversation from './model/Conversation.js'
-import { createRequire } from 'module';
+import {createRequire} from 'module';
+import path from "path";
+
 const require = createRequire(import.meta.url);
 
 class JamiDaemon {
@@ -29,7 +31,7 @@
         this.accounts = []
         this.lookups = []
         this.tempAccounts = []
-        this.dring = require("./jamid.node")
+        this.dring = require(path.join(process.cwd(), "jamid.node"))
         this.dring.init({
             AccountsChanged: () => {
                 console.log("AccountsChanged")
diff --git a/README.md b/README.md
index 1cf3b63..6eab9d8 100644
--- a/README.md
+++ b/README.md
@@ -60,9 +60,9 @@
 
 - uncomment the line `// import config from "./sentry-server.config.json" assert { type: "json" };` in `./sentry.js`
 
-- uncomment the line `// import config from "../sentry-client.config.json"` and the init config`Sentry.init(...` in `./client/index.js`  
+- uncomment the line `// import config from "../sentry-client.config.json"` and the init config`Sentry.init(...` in `./client/index.js`
 
-- uncomment the lines `// import { sentrySetUp } from './sentry.js'` and `sentrySetUp(app)` in `./app.js`  
+- uncomment the lines `// import { sentrySetUp } from './sentry.js'` and `sentrySetUp(app)` in `./app.ts`  
 
 - add `sentry-client.config.json` file in `client` and `sentry-server.config.json` (ask them to an admin) in your project root
 
diff --git a/app.js b/app.ts
similarity index 88%
rename from app.js
rename to app.ts
index 9e2c313..eea1786 100644
--- a/app.js
+++ b/app.ts
@@ -14,9 +14,6 @@
 import { Strategy as LocalStrategy } from 'passport-local'
 //import { createRequire } from 'module';
 //const require = createRequire(import.meta.url);
-import { fileURLToPath } from 'url';
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
 
 //const redis = require('redis-url').connect()
 //const RedisStore = require('connect-redis')(session)
@@ -38,7 +35,7 @@
 const loadConfig = async (filePath) => {
     const config = {users: {}, authMethods: []}
     try {
-        return Object.assign(config, JSON.parse(await fs.readFile(filePath)))
+        return Object.assign(config, JSON.parse((await fs.readFile(filePath)).toString()))
     } catch(e) {
         console.log(e)
         return config
@@ -74,25 +71,24 @@
 const connectedUsers = {}
 
 const createServer = async (appConfig) => {
+    const node_env = process.env.NODE_ENV || 'development'
     const app = express()
-    console.log(`Loading server for ${app.get('env')} with config:`)
+    console.log(`Loading server for ${node_env} with config:`)
     console.log(appConfig)
-    const development = app.get('env') === 'development'
 
     var corsOptions = {
         origin: 'http://127.0.0.1:3000'
     }
 
-    if (development) {
-        const [ webpack, webpackDev, webpackHot, webpackConfig ] = await Promise.all([
-            import('webpack'),
-            import('webpack-dev-middleware'),
-            import('webpack-hot-middleware'),
-            import('./client/webpack.config.js')
-        ])
-        const compiler = webpack.default(webpackConfig.default)
+    if (node_env === 'development') {
+        const webpack = await import('webpack')
+        const webpackDev = await import('webpack-dev-middleware')
+        const webpackHot = await import ('webpack-hot-middleware')
+        const {default: webpackConfig} = await import ('jami-web-client/webpack.config.js') as any
+
+        const compiler = webpack.default(webpackConfig)
         app.use(webpackDev.default(compiler, {
-            publicPath: webpackConfig.default.output.publicPath
+            publicPath: webpackConfig.output.publicPath
         }))
         app.use(webpackHot.default(compiler))
     }
@@ -123,7 +119,7 @@
         console.log('JamiDaemon onMessage')
 
         if (conversation.listeners) {
-            Object.values(conversation.listeners).forEach(listener => {
+            Object.values(conversation.listeners).forEach((listener: any) => {
                 listener.socket.emit('newMessage', message)
             })
         }
@@ -187,8 +183,8 @@
     passport.deserializeUser(deserializeUser)
 
     const jamsStrategy = new LocalStrategy(
-        (username, password, done) => {
-            const accountId = jami.addAccount({
+        async (username, password, done) => {
+            const accountId = await jami.addAccount({
                 'managerUri': 'https://jams.savoirfairelinux.com',
                 'managerUsername': username,
                 'archivePassword': password
@@ -288,15 +284,17 @@
 
     /* GET React App */
 
-    app.use(express.static(path.join(__dirname, 'client', 'dist')))
+    const cwd = process.cwd()
+    app.use(express.static(path.join(cwd,  'client/dist')));
 
-    app.use((req, res, next) => {
-        res.render(path.join(__dirname, 'client', 'dist', 'index.ejs'), {
+    app.use((req, res) => {
+        res.render(path.join(cwd, 'client/dist/index.ejs'), {
             initdata: JSON.stringify(getState(req))
         })
     })
 
 
+    // @ts-ignore TODO: Fix the typescript error
     const server = http.Server(app)
 
     const io = new Server(server, { cors: corsOptions })
@@ -305,7 +303,7 @@
     io.use(wrap(passport.initialize()))
     io.use(wrap(passport.session()))
     io.use((socket, next) => {
-        if (socket.request.user) {
+        if ((socket.request as any).user) {
             next()
         } else {
             next(new Error('unauthorized'))
@@ -313,7 +311,7 @@
     })
     io.on('connect', (socket) => {
         console.log(`new connection ${socket.id}`)
-        const session = socket.request.session
+        const session = (socket.request as any).session
         console.log(`saving sid ${socket.id} in session ${session.id}`)
         session.socketId = socket.id
         session.save()
diff --git a/client/package.json b/client/package.json
index 1a484c7..0cc3eda 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,8 +1,26 @@
 {
   "name": "jami-web-client",
-  "type": "module",
   "version": "0.1.0",
   "private": true,
+  "type": "module",
+  "scripts": {
+    "build": "webpack",
+    "extract-translations": "i18next"
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  },
+  "eslintConfig": {
+    "extends": "../.eslintrc.json"
+  },
   "dependencies": {
     "@babel/runtime": "7.18.9",
     "@emotion/react": "^11.10.0",
@@ -56,23 +74,5 @@
     "typescript": "^4.7.4",
     "webpack": "^5.74.0",
     "webpack-cli": "^4.10.0"
-  },
-  "scripts": {
-    "build": "npx webpack",
-    "extract-translations": "i18next"
-  },
-  "eslintConfig": {
-    "extends": "react-app"
-  },
-  "browserslist": {
-    "production": [
-      ">0.2%",
-      "not dead"
-    ],
-    "development": [
-      "last 1 chrome version",
-      "last 1 firefox version",
-      "last 1 safari version"
-    ]
   }
 }
diff --git a/client/redux/appSlice.ts b/client/redux/appSlice.ts
index 20c2a06..60b0521 100644
--- a/client/redux/appSlice.ts
+++ b/client/redux/appSlice.ts
@@ -5,7 +5,7 @@
 // Define a type for the slice state
 interface appState {
   accountId: string;
-  accountObject: Account;
+  accountObject: Account | null;
   refresh: boolean;
 }
 
diff --git a/client/tsconfig.json b/client/tsconfig.json
index c2fd010..51406b2 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -1,103 +1,9 @@
 {
+  "extends": "../tsconfig",
   "compilerOptions": {
-    /* Visit https://aka.ms/tsconfig to read more about this file */
-
-    /* Projects */
-    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
-    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
-    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
-    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
-    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
-    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */
-
-    /* Language and Environment */
-    "target": "ES6",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
-    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
-    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
-    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
-    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
-    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
-    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
-    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
-    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
-    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
-    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
-    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */
-
-    /* Modules */
-    // "module": "commonjs",                                /* Specify what module code is generated. */
-    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
-    "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
-    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
-    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
-    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
-    // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
-    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
-    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
-    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
-    // "resolveJsonModule": true,                        /* Enable importing .json files. */
-    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
-
-    /* JavaScript Support */
-    "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
-    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
-    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
-
-    /* Emit */
-    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
-    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
-    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
-    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
-    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
-    // "outDir": "./",                                   /* Specify an output folder for all emitted files. */
-    // "removeComments": true,                           /* Disable emitting comments. */
-    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
-    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
-    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
-    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
-    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
-    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
-    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
-    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
-    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
-    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
-    // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
-    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
-    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
-    // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
-    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
-    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
-
-    /* Interop Constraints */
-    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
-    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
-    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
-    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
-    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
-
-    /* Type Checking */
-    // "strict": true,                                      /* Enable all strict type-checking options. */
-    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
-    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
-    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
-    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
-    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
-    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
-    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
-    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
-    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
-    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
-    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
-    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
-    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
-    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
-    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
-    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
-    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
-    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */
-
-    /* Completeness */
-    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
-    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
-  }
+    /* Specify what JSX code is generated. */
+    "jsx": "react-jsx",
+    /* Specify an output folder for all emitted files. */
+    "outDir": "./dist/"
+  },
 }
diff --git a/client/webpack.config.js b/client/webpack.config.js
index 7da8fb5..f5aeab5 100644
--- a/client/webpack.config.js
+++ b/client/webpack.config.js
@@ -1,25 +1,33 @@
 'use strict'
 
-import { fileURLToPath } from 'url';
-import { dirname } from 'path';
+import {dirname, resolve} from 'path'
+import dotenv from 'dotenv'
+import HtmlWebpackPlugin from 'html-webpack-plugin'
+import CopyWebpackPlugin from 'copy-webpack-plugin'
+import webpack from 'webpack'
+import {fileURLToPath} from 'url';
+
 const __filename = fileURLToPath(import.meta.url);
 const __dirname = dirname(__filename);
 
-import dotenv from 'dotenv'
 dotenv.config({ path: resolve(__dirname, '..', '.env') })
 
-import { resolve } from 'path'
-import HtmlWebpackPlugin from 'html-webpack-plugin'
-import CopyWebpackPlugin from 'copy-webpack-plugin'
 const mode = process.env.NODE_ENV || 'development'
 
 let entry = [resolve(__dirname, 'src', 'index.js')]
 let plugins = [
-  new HtmlWebpackPlugin({ template: '!!raw-loader!' + resolve(__dirname, 'src', 'index.ejs'), filename: 'index.ejs' }),
+  new HtmlWebpackPlugin({
+    template: '!!raw-loader!' + resolve(__dirname, 'src', 'index.ejs'),
+    filename: 'index.ejs'
+  }),
   new CopyWebpackPlugin({
-    patterns: [{ from: resolve(__dirname, 'public'), to: resolve(__dirname, 'dist') }]
+    patterns: [{
+      from: resolve(__dirname, 'public'),
+      to: resolve(__dirname, 'dist')
+    }]
   })
 ]
+
 let devtool = undefined
 let babelLoaderPlugins = ["@babel/plugin-transform-runtime"]
 
@@ -45,10 +53,10 @@
   module: {
     rules: [
       {
-        // test: /\.jsx?/,
-        test: /\.(js|jsx|ts|tsx)?$/,
+        test: /\.[tj]sx?$/,
         resolve: {
           fullySpecified: false,
+          extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
         },
         exclude: /node_modules/,
         use: {
@@ -69,6 +77,9 @@
                   runtime: "automatic",
                 },
               ],
+              [
+                "@babel/preset-typescript"
+              ],
             ],
           },
         },
@@ -94,4 +105,4 @@
     extensions: [".ts", ".tsx", ".js", ".jsx"],
   },
   plugins,
-};
\ No newline at end of file
+};
diff --git a/docker-compose.yml b/docker-compose.yml
index 3a896aa..965f2a9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,8 +5,15 @@
     image: jami-web
     build: .
     volumes:
-      - ./client:/web-client/client # Add bind mount to hot-reload client
-      - /web-client/client/node_modules/ # Ignore node_modules from bind mount
+      # Add bind mounts for hot-reload
+      - ./client:/web-client/client
+      - ./routes:/web-client/routes
+      - ./model:/web-client/model
+      # Ignore folders from the bind mounts
+      - /web-client/client/node_modules/
+      - /web-client/node_modules/
+      - /web-client/dist/
+      - /web-client/client/dist
     ports:
       - "3000:3000"
     stdin_open: true
diff --git a/package-lock.json b/package-lock.json
index 0bdf92f..a16b77b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -39,6 +39,8 @@
         "llnode": "^4.0.0",
         "nodemon": "^2.0.20",
         "raw-loader": "^4.0.2",
+        "ts-node": "^10.9.1",
+        "typescript": "^4.8.3",
         "webpack-dev-middleware": "^5.3.3",
         "webpack-hot-middleware": "^2.25.2"
       }
@@ -1907,6 +1909,28 @@
         "node": ">=0.1.90"
       }
     },
+    "node_modules/@cspotcode/source-map-support": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "0.3.9"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+      "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
     "node_modules/@cypress/request": {
       "version": "2.88.10",
       "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz",
@@ -3627,6 +3651,30 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/@tsconfig/node10": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+      "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node12": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node14": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node16": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+      "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+      "dev": true
+    },
     "node_modules/@types/aria-query": {
       "version": "4.2.2",
       "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
@@ -3858,6 +3906,19 @@
       "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
       "dev": true
     },
+    "node_modules/@types/webpack": {
+      "version": "5.28.0",
+      "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz",
+      "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==",
+      "dev": true,
+      "optional": true,
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*",
+        "tapable": "^2.2.0",
+        "webpack": "^5"
+      }
+    },
     "node_modules/@types/yargs": {
       "version": "17.0.12",
       "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz",
@@ -4369,6 +4430,15 @@
         "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
       }
     },
+    "node_modules/acorn-walk": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+      "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/agent-base": {
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -4559,6 +4629,12 @@
         }
       ]
     },
+    "node_modules/arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
     "node_modules/argparse": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -5807,6 +5883,12 @@
         "node": ">=10"
       }
     },
+    "node_modules/create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true
+    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -6202,6 +6284,15 @@
         "npm": "1.2.8000 || >= 1.4.16"
       }
     },
+    "node_modules/diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
     "node_modules/diff-sequences": {
       "version": "28.1.1",
       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz",
@@ -10274,6 +10365,12 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
     "node_modules/matcher-collection": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz",
@@ -13519,6 +13616,49 @@
         "node": ">=8"
       }
     },
+    "node_modules/ts-node": {
+      "version": "10.9.1",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+      "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+      "dev": true,
+      "dependencies": {
+        "@cspotcode/source-map-support": "^0.8.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.1",
+        "yn": "3.1.1"
+      },
+      "bin": {
+        "ts-node": "dist/bin.js",
+        "ts-node-cwd": "dist/bin-cwd.js",
+        "ts-node-esm": "dist/bin-esm.js",
+        "ts-node-script": "dist/bin-script.js",
+        "ts-node-transpile-only": "dist/bin-transpile.js",
+        "ts-script": "dist/bin-script-deprecated.js"
+      },
+      "peerDependencies": {
+        "@swc/core": ">=1.2.50",
+        "@swc/wasm": ">=1.2.50",
+        "@types/node": "*",
+        "typescript": ">=2.7"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/wasm": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/tslib": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@@ -13828,6 +13968,12 @@
         "uuid": "dist/bin/uuid"
       }
     },
+    "node_modules/v8-compile-cache-lib": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+      "dev": true
+    },
     "node_modules/value-or-function": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
@@ -14400,6 +14546,15 @@
         "fd-slicer": "~1.1.0"
       }
     },
+    "node_modules/yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -15656,6 +15811,27 @@
       "dev": true,
       "optional": true
     },
+    "@cspotcode/source-map-support": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/trace-mapping": "0.3.9"
+      },
+      "dependencies": {
+        "@jridgewell/trace-mapping": {
+          "version": "0.3.9",
+          "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+          "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+          "dev": true,
+          "requires": {
+            "@jridgewell/resolve-uri": "^3.0.3",
+            "@jridgewell/sourcemap-codec": "^1.4.10"
+          }
+        }
+      }
+    },
     "@cypress/request": {
       "version": "2.88.10",
       "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz",
@@ -16808,6 +16984,30 @@
       "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
       "dev": true
     },
+    "@tsconfig/node10": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+      "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+      "dev": true
+    },
+    "@tsconfig/node12": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+      "dev": true
+    },
+    "@tsconfig/node14": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+      "dev": true
+    },
+    "@tsconfig/node16": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+      "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+      "dev": true
+    },
     "@types/aria-query": {
       "version": "4.2.2",
       "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
@@ -17032,6 +17232,19 @@
       "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
       "dev": true
     },
+    "@types/webpack": {
+      "version": "5.28.0",
+      "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz",
+      "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==",
+      "dev": true,
+      "optional": true,
+      "peer": true,
+      "requires": {
+        "@types/node": "*",
+        "tapable": "^2.2.0",
+        "webpack": "^5"
+      }
+    },
     "@types/yargs": {
       "version": "17.0.12",
       "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz",
@@ -17411,6 +17624,12 @@
       "dev": true,
       "requires": {}
     },
+    "acorn-walk": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+      "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+      "dev": true
+    },
     "agent-base": {
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -17538,6 +17757,12 @@
       "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
       "dev": true
     },
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
     "argparse": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -18478,6 +18703,12 @@
         "yaml": "^1.10.0"
       }
     },
+    "create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true
+    },
     "cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -18768,6 +18999,12 @@
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
       "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
     },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
     "diff-sequences": {
       "version": "28.1.1",
       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz",
@@ -21955,6 +22192,12 @@
         "semver": "^6.0.0"
       }
     },
+    "make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
     "matcher-collection": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz",
@@ -24419,6 +24662,27 @@
         }
       }
     },
+    "ts-node": {
+      "version": "10.9.1",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+      "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+      "dev": true,
+      "requires": {
+        "@cspotcode/source-map-support": "^0.8.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.1",
+        "yn": "3.1.1"
+      }
+    },
     "tslib": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@@ -24645,6 +24909,12 @@
       "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
       "dev": true
     },
+    "v8-compile-cache-lib": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+      "dev": true
+    },
     "value-or-function": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
@@ -25072,6 +25342,12 @@
         "fd-slicer": "~1.1.0"
       }
     },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true
+    },
     "yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 96ed873..f8e046a 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,20 @@
 {
   "name": "jami-web-server",
-  "type": "module",
   "version": "1.0.0",
   "description": "Jaas Web API that handles client requests to Jami daemon",
-  "main": "index.js",
+  "license": "ISC",
+  "author": "Larbi Gharib",
+  "type": "module",
+  "main": "app.ts",
+  "scripts": {
+    "build": "npm run build-client",
+    "build:prod": "tsc && NODE_ENV=production npm run build-client",
+    "build-client": "cd client && webpack",
+    "clean": "rm -rf dist client/dist",
+    "clean-all": "npm run clean && rm -rf node_modules client/node_modules",
+    "start": "nodemon",
+    "start:prod": "NODE_ENV=production node dist/app.js"
+  },
   "dependencies": {
     "@sentry/node": "^7.13.0",
     "@sentry/tracing": "^7.13.0",
@@ -35,15 +46,9 @@
     "llnode": "^4.0.0",
     "nodemon": "^2.0.20",
     "raw-loader": "^4.0.2",
+    "ts-node": "^10.9.1",
+    "typescript": "^4.8.3",
     "webpack-dev-middleware": "^5.3.3",
     "webpack-hot-middleware": "^2.25.2"
-  },
-  "scripts": {
-    "build": "cd client && npx webpack",
-    "build-dev": "cd client && npx webpack --mode development",
-    "start": "node app.js",
-    "start-dev": "nodemon app.js"
-  },
-  "author": "Larbi Gharib",
-  "license": "ISC"
+  }
 }
diff --git a/routes/index.js b/routes/index.js
index 409eed0..e4cf1e8 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -1,10 +1,6 @@
 import { Router } from 'express'
 const router = Router()
 import { join } from 'path'
-import { fileURLToPath } from 'url'
-import { dirname } from 'path'
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = dirname(__filename)
 
 /* GET React App */
 router.get(['/app', '/app/*'], (req, res, next) => {
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..34d02fe
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,32 @@
+{
+  "compilerOptions": {
+    /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+    "allowJs": true,
+    /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+    "esModuleInterop": true,
+    /* Ensure that casing is correct in imports. */
+    "forceConsistentCasingInFileNames": true,
+    /* Specify what module code is generated. */
+    "module": "ES2022",
+    /* Specify how TypeScript looks up a file from a given module specifier. */
+    "moduleResolution": "Node",
+    /* Specify an output folder for all emitted files. */
+    "outDir": "./dist/",
+    /* Skip type checking all .d.ts files. */
+    "skipLibCheck": true,
+    /* Create source map files for emitted JavaScript files. */
+    "sourceMap": true,
+    /* When type checking, take into account 'null' and 'undefined'. */
+    "strictNullChecks": true,
+    /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+    "target": "ES2017",
+  },
+  "include": [
+    "model",
+    "routes",
+    "app.ts",
+  ],
+  "ts-node": {
+    "esm": "true"
+  }
+}