Fix jamid.node path in server

Add npm script in server to generate keys.
Improve dockerfile to generate server keys.
Add dotenv and dotenv-cli dependencies to server

Change-Id: Ibcb21578b4cb59d3bc9a99fa0e832d3b30cfb01a
diff --git a/Dockerfile b/Dockerfile
index 466a46d..a1fde74 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -13,16 +13,13 @@
 RUN ln -s /daemon/bin/nodejs/build/Release/jamid.node jamid.node
 
 COPY package*.json ./
-COPY common/package*.json common/
 COPY client/package*.json client/
 COPY server/package*.json server/
-
-RUN npm ci --ignore-scripts
-
-# Build common library
 COPY common common
+COPY server/misc server/misc
 COPY tsconfig.json ./
-RUN npm run build --workspace common
+
+RUN npm ci
 
 COPY . .
 
diff --git a/README.md b/README.md
index f5c6a65..dd3cec9 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,12 @@
 
 To build the dring.node Javascript interface to talk to the daemon api go to the daemon repo and use ./configure --with-nodejs then execute make -j4 to build the daemon
 
+Create a symbolic link to `jamid.node` at the root of jami-web:
+
+```bash
+ln -s daemon/bin/nodejs/build/Release/jamid.node jamid.node
+```
+
 Then, start the servers:
 
 ```bash
diff --git a/package-lock.json b/package-lock.json
index 73599b7..91233da 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7304,6 +7304,30 @@
         "node": ">=12"
       }
     },
+    "node_modules/dotenv-cli": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-6.0.0.tgz",
+      "integrity": "sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "dotenv": "^16.0.0",
+        "dotenv-expand": "^8.0.1",
+        "minimist": "^1.2.5"
+      },
+      "bin": {
+        "dotenv": "cli.js"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.3.tgz",
+      "integrity": "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/duplexify": {
       "version": "3.7.1",
       "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@@ -17659,6 +17683,7 @@
       "name": "jami-web-server",
       "dependencies": {
         "argon2": "^0.29.1",
+        "dotenv": "^16.0.3",
         "express": "^4.18.2",
         "express-async-handler": "^1.2.0",
         "helmet": "^6.0.0",
@@ -17675,6 +17700,7 @@
         "@types/node": "^18.8.3",
         "@types/whatwg-url": "^11.0.0",
         "@types/ws": "^8.5.3",
+        "dotenv-cli": "^6.0.0",
         "nodemon": "^2.0.20",
         "npm-check-updates": "^16.3.3",
         "ts-node": "^10.9.1",
@@ -22871,6 +22897,24 @@
       "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
       "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
     },
+    "dotenv-cli": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-6.0.0.tgz",
+      "integrity": "sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^7.0.3",
+        "dotenv": "^16.0.0",
+        "dotenv-expand": "^8.0.1",
+        "minimist": "^1.2.5"
+      }
+    },
+    "dotenv-expand": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.3.tgz",
+      "integrity": "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==",
+      "dev": true
+    },
     "duplexify": {
       "version": "3.7.1",
       "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@@ -25507,6 +25551,8 @@
         "@types/whatwg-url": "^11.0.0",
         "@types/ws": "^8.5.3",
         "argon2": "^0.29.1",
+        "dotenv": "^16.0.3",
+        "dotenv-cli": "^6.0.0",
         "express": "^4.18.2",
         "express-async-handler": "^1.2.0",
         "helmet": "^6.0.0",
diff --git a/server/.gitignore b/server/.gitignore
index 54fcf25..efc99e0 100644
--- a/server/.gitignore
+++ b/server/.gitignore
@@ -1,2 +1 @@
 creds.json
-*.pem
diff --git a/server/misc/gen_key_pair.sh b/server/misc/gen_key_pair.sh
deleted file mode 100755
index ea60e05..0000000
--- a/server/misc/gen_key_pair.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-set -ex
-
-# Generate PEM-encoded PKCS#8 private key and PEM-encoded SPKI public key
-
-if command -v openssl; then
-  # -algorithm RSA -pkeyopt rsa_keygen_bits:2048
-  # ES256: -algorithm EC -pkeyopt ec_paramgen_curve:P-256
-  gen_pkcs8() { openssl genpkey -algorithm ed25519; }
-  pkcs8_to_spki() { openssl pkey -pubout; }
-else
-  printf 'No tools known\n' >&2 && exit 1
-fi
-
-gen_pkcs8 | tee ./privkey.pem | pkcs8_to_spki >./pubkey.pem
diff --git a/server/package.json b/server/package.json
index 6d17ce8..b11c92d 100644
--- a/server/package.json
+++ b/server/package.json
@@ -9,13 +9,16 @@
     "lint": "eslint src",
     "lint:fix": "eslint --fix src",
     "format": "prettier --write src",
-    "format:check": "prettier --check src"
+    "format:check": "prettier --check src",
+    "prepare": "npm run genkeys",
+    "genkeys": "sh scripts/genkeys.sh"
   },
   "devDependencies": {
     "@types/express": "^4.17.14",
     "@types/node": "^18.8.3",
     "@types/whatwg-url": "^11.0.0",
     "@types/ws": "^8.5.3",
+    "dotenv-cli": "^6.0.0",
     "nodemon": "^2.0.20",
     "npm-check-updates": "^16.3.3",
     "ts-node": "^10.9.1",
@@ -24,6 +27,7 @@
   },
   "dependencies": {
     "argon2": "^0.29.1",
+    "dotenv": "^16.0.3",
     "express": "^4.18.2",
     "express-async-handler": "^1.2.0",
     "helmet": "^6.0.0",
diff --git a/server/scripts/genkeys.sh b/server/scripts/genkeys.sh
new file mode 100755
index 0000000..18f1bdf
--- /dev/null
+++ b/server/scripts/genkeys.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+set -e
+
+if ! command -v dotenv; then
+  printf 'Missing "dotenv". Please run "npm install"\n' >&2 && exit 1
+fi
+
+if [ "$(dotenv -p PRIVATE_KEY)" ] && [ "$(dotenv -p PUBLIC_KEY)" ]; then
+  printf 'Public and private keys are already defined. Exiting...\n' >&2 && exit 0
+fi
+
+# Generate PEM-encoded PKCS#8 private key and PEM-encoded SPKI public key
+
+if command -v openssl; then
+  # -algorithm RSA -pkeyopt rsa_keygen_bits:2048
+  # ES256: -algorithm EC -pkeyopt ec_paramgen_curve:P-256
+  gen_pkcs8() { openssl genpkey -algorithm ed25519; }
+  pkcs8_to_spki() { openssl pkey -pubout; }
+else
+  printf 'No tools known\n' >&2 && exit 1
+fi
+
+private_key=$(gen_pkcs8)
+public_key=$(echo "${private_key}" | pkcs8_to_spki)
+
+echo "PRIVATE_KEY=\"${private_key}\"" >> .env
+echo "PUBLIC_KEY=\"${public_key}\"" >> .env
diff --git a/server/src/index.ts b/server/src/index.ts
index ec7067e..d00f78b 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -17,6 +17,9 @@
  */
 import 'reflect-metadata';
 
+import * as dotenv from 'dotenv';
+dotenv.config();
+
 import { createServer } from 'node:http';
 
 import log from 'loglevel';
diff --git a/server/src/jamid/jamid.ts b/server/src/jamid/jamid.ts
index 51394eb..48c20f2 100644
--- a/server/src/jamid/jamid.ts
+++ b/server/src/jamid/jamid.ts
@@ -38,7 +38,7 @@
 
   constructor() {
     // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
-    this.jamiSwig = require('../../jamid.node') as JamiSwig;
+    this.jamiSwig = require('../../jamid.node') as JamiSwig; // TODO: we should put the path in the .env
 
     const handlers: Record<string, unknown> = {};
     const handler = (sig: string) => {
diff --git a/server/src/vault.ts b/server/src/vault.ts
index 45548db..07e2914 100644
--- a/server/src/vault.ts
+++ b/server/src/vault.ts
@@ -15,8 +15,6 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { readFile } from 'node:fs/promises';
-
 import { importPKCS8, importSPKI, KeyLike } from 'jose';
 import { Service } from 'typedi';
 
@@ -25,13 +23,16 @@
   privateKey!: KeyLike;
   publicKey!: KeyLike;
 
-  // TODO: Convert to environment variables and check if defined
   async build() {
-    const privateKeyBuffer = await readFile('privkey.pem');
-    this.privateKey = await importPKCS8(privateKeyBuffer.toString(), 'EdDSA');
+    const privatekey = process.env.PRIVATE_KEY;
+    const publicKey = process.env.PUBLIC_KEY;
 
-    const publicKeyBuffer = await readFile('pubkey.pem');
-    this.publicKey = await importSPKI(publicKeyBuffer.toString(), 'EdDSA');
+    if (!privatekey || !publicKey) {
+      throw new Error('Missing private or public key environment variables. Try running "npm run genkeys"');
+    }
+
+    this.privateKey = await importPKCS8(privatekey, 'EdDSA');
+    this.publicKey = await importSPKI(publicKey, 'EdDSA');
 
     return this;
   }