diff --git a/Dockerfile b/Dockerfile
index 9562cdf5dad7fe1406a65d9dc73f1dc81378e6bf..8805e9642b765f9f45cf65ef59ed735739f1d2a9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -31,12 +31,11 @@ LABEL name="absd" \
 
 # Copy only the relevant files
 COPY --from=builder /usr/build/src/server ./src/server
+COPY --from=builder /usr/build/src/scripts ./src/scripts
 COPY --from=builder /usr/build/dist/ ./dist/
 COPY --from=builder /usr/build/package.json ./package.json
 COPY --from=builder /usr/build/package-lock.json ./package-lock.json
 COPY ./data/ ./data/
-# Scripts are needed to build the database if there is none
-COPY ./scripts/ ./scripts/
 
 # Install production dependencies and remove the cache for extra space saving
 RUN npm ci && npm cache clean --force
diff --git a/biome.json b/biome.json
index 18bef1a36cfcc583c7d450793864abd1d69658bd..b724216311f220e92778bda923ac76e7ed274393 100644
--- a/biome.json
+++ b/biome.json
@@ -21,7 +21,8 @@
 		"rules": {
 			"recommended": true,
       "complexity": {
-        "noForEach": "off"
+        "noForEach": "off",
+        "useArrowFunction": "off"
       }
 		}
 	}
diff --git a/data/2024-09-21-stats.json b/data/2024-09-21-stats.json
index 3a4842ccc1323111f5e151b1afc55e570d97e2fc..4eee69e6bf05cd23c61f8ad279b55acfcd4ad53e 100644
--- a/data/2024-09-21-stats.json
+++ b/data/2024-09-21-stats.json
@@ -40,7 +40,7 @@
     "CoV-AbDab-PDB": 331,
     "EBOLA": 294
   },
-  "antibodiesPerHeavySegments": {
+  "antibodiesPerHeavySegment": {
     "IGHV1": 114950,
     "IGHV3": 405509,
     "IGHV4": 173337,
@@ -57,7 +57,7 @@
     "IGHV11": 10,
     "IGHV15": 4
   },
-  "antibodiesPerLightSegments": {
+  "antibodiesPerLightSegment": {
     "IGLV8": 6932,
     "IGLV6": 7105,
     "IGKV3": 165916,
diff --git a/data/2024-12-03-stats.json b/data/2024-12-03-stats.json
index 626fc71b43c0ab189a7e4f989afbade01d1daffa..5180df8b03a124c8ee87293e3eba7c8d2a531dcd 100644
--- a/data/2024-12-03-stats.json
+++ b/data/2024-12-03-stats.json
@@ -50,7 +50,7 @@
     "OAS": 708308,
     "UNIPROT": 1822
   },
-  "antibodiesPerHeavySegments": {
+  "antibodiesPerHeavySegment": {
     "IGHV1": 231581,
     "IGHV2": 50537,
     "IGHV3": 679756,
@@ -67,7 +67,7 @@
     "IGHV12": 32,
     "IGHV15": 14
   },
-  "antibodiesPerLightSegments": {
+  "antibodiesPerLightSegment": {
     "IGKV4": 96494,
     "IGLV11": 31,
     "IGLV8": 10559,
diff --git a/data/2024-12-23-stats.json b/data/2024-12-23-stats.json
index 3d978b72c49948bc9257bd9dfa3bccd3c8fb2d17..ec864cf830db68b068c7e0b799c65642326aef6a 100644
--- a/data/2024-12-23-stats.json
+++ b/data/2024-12-23-stats.json
@@ -50,7 +50,7 @@
     "UNIPROT": 1822,
     "EBOLA": 282
   },
-  "antibodiesPerHeavySegments": {
+  "antibodiesPerHeavySegment": {
     "IGHV7": 10884,
     "IGHV4": 296316,
     "IGHV1": 231576,
@@ -67,7 +67,7 @@
     "IGHV15": 14,
     "IGHV12": 32
   },
-  "antibodiesPerLightSegments": {
+  "antibodiesPerLightSegment": {
     "IGLV7": 18156,
     "IGLV1": 152339,
     "IGKV2": 93389,
diff --git a/k8s/absd.yaml b/k8s/absd.yaml
index ab68a7ef6d2283895a8947ca5c9101b796792c02..0886fd57178dc80e9627331a4b59d54c1d2ed795 100644
--- a/k8s/absd.yaml
+++ b/k8s/absd.yaml
@@ -9,7 +9,7 @@ spec:
     - ReadWriteMany
   resources:
     requests:
-      storage: 10Gi
+      storage: 20Gi
   storageClassName: ceph-fs
 ---
 apiVersion: apps/v1
diff --git a/package-lock.json b/package-lock.json
index 44a3bf77b430055e8e614a3d1223c28ffa2ffe57..16d08d9f8734d7c892174fd60609bea89aa34372 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,35 +9,35 @@
       "version": "1.0.0",
       "license": "GPL-3.0",
       "dependencies": {
-        "@fastify/mongodb": "^9.0.1",
-        "@fastify/static": "^8.0.3",
+        "@fastify/mongodb": "^9.0.2",
+        "@fastify/static": "^8.0.4",
         "axios": "^1.7.9",
-        "env-schema": "^6.0.0",
-        "fastify": "^5.1.0",
+        "env-schema": "^6.0.1",
+        "fastify": "^5.2.1",
         "modern-normalize": "^3.0.1",
-        "mongodb": "^6.11.0",
-        "pinia": "^2.3.0",
-        "plotly.js-dist-min": "^2.35.2",
-        "qs": "^6.13.1",
+        "mongodb": "^6.12.0",
+        "pinia": "^2.3.1",
+        "plotly.js-dist-min": "^2.35.3",
+        "qs": "^6.14.0",
         "vue": "^3.5.13",
         "vue-router": "^4.5.0"
       },
       "devDependencies": {
         "@biomejs/biome": "^1.9.4",
-        "@types/node": "^22.10.1",
+        "@types/node": "^22.10.9",
         "@types/plotly.js-dist-min": "^2.3.4",
-        "@types/qs": "^6.9.17",
+        "@types/qs": "^6.9.18",
         "@vitejs/plugin-vue": "^5.2.1",
         "autoprefixer": "^10.4.20",
-        "concurrently": "^9.1.0",
+        "concurrently": "^9.1.2",
         "cssnano": "^7.0.6",
-        "nodemon": "^3.1.7",
+        "nodemon": "^3.1.9",
         "pino-pretty": "^13.0.0",
         "rollup-plugin-brotli": "^3.1.0",
         "rollup-plugin-gzip": "^4.0.1",
-        "typescript": "^5.6.3",
-        "vite": "^6.0.3",
-        "vue-tsc": "^2.1.10"
+        "typescript": "^5.7.3",
+        "vite": "^6.0.11",
+        "vue-tsc": "^2.2.0"
       }
     },
     "node_modules/@babel/helper-string-parser": {
@@ -251,9 +251,9 @@
       }
     },
     "node_modules/@esbuild/aix-ppc64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz",
-      "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
+      "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
       "cpu": [
         "ppc64"
       ],
@@ -268,9 +268,9 @@
       }
     },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz",
-      "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
+      "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
       "cpu": [
         "arm"
       ],
@@ -285,9 +285,9 @@
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz",
-      "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
+      "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
       "cpu": [
         "arm64"
       ],
@@ -302,9 +302,9 @@
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz",
-      "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
+      "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
       "cpu": [
         "x64"
       ],
@@ -319,9 +319,9 @@
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz",
-      "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
+      "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
       "cpu": [
         "arm64"
       ],
@@ -336,9 +336,9 @@
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz",
-      "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
+      "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
       "cpu": [
         "x64"
       ],
@@ -353,9 +353,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz",
-      "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
+      "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
       "cpu": [
         "arm64"
       ],
@@ -370,9 +370,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz",
-      "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
+      "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
       "cpu": [
         "x64"
       ],
@@ -387,9 +387,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz",
-      "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
+      "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
       "cpu": [
         "arm"
       ],
@@ -404,9 +404,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz",
-      "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
+      "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
       "cpu": [
         "arm64"
       ],
@@ -421,9 +421,9 @@
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz",
-      "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
+      "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
       "cpu": [
         "ia32"
       ],
@@ -438,9 +438,9 @@
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz",
-      "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
+      "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
       "cpu": [
         "loong64"
       ],
@@ -455,9 +455,9 @@
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz",
-      "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
+      "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
       "cpu": [
         "mips64el"
       ],
@@ -472,9 +472,9 @@
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz",
-      "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
+      "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
       "cpu": [
         "ppc64"
       ],
@@ -489,9 +489,9 @@
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz",
-      "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
+      "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
       "cpu": [
         "riscv64"
       ],
@@ -506,9 +506,9 @@
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz",
-      "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
+      "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
       "cpu": [
         "s390x"
       ],
@@ -523,9 +523,9 @@
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz",
-      "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
+      "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
       "cpu": [
         "x64"
       ],
@@ -539,10 +539,27 @@
         "node": ">=18"
       }
     },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
+      "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz",
-      "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
+      "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
       "cpu": [
         "x64"
       ],
@@ -557,9 +574,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-arm64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz",
-      "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
+      "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
       "cpu": [
         "arm64"
       ],
@@ -574,9 +591,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz",
-      "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
+      "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
       "cpu": [
         "x64"
       ],
@@ -591,9 +608,9 @@
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz",
-      "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
+      "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
       "cpu": [
         "x64"
       ],
@@ -608,9 +625,9 @@
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz",
-      "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
+      "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
       "cpu": [
         "arm64"
       ],
@@ -625,9 +642,9 @@
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz",
-      "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
+      "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
       "cpu": [
         "ia32"
       ],
@@ -642,9 +659,9 @@
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz",
-      "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
+      "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
       "cpu": [
         "x64"
       ],
@@ -690,6 +707,12 @@
         "fast-json-stringify": "^6.0.0"
       }
     },
+    "node_modules/@fastify/forwarded": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz",
+      "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==",
+      "license": "MIT"
+    },
     "node_modules/@fastify/merge-json-schemas": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz",
@@ -700,15 +723,35 @@
       }
     },
     "node_modules/@fastify/mongodb": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/@fastify/mongodb/-/mongodb-9.0.1.tgz",
-      "integrity": "sha512-ysl0jvD76dCFKLtoPSGbsZwyH/iM8qcplg9/PRWoZ0CZy8GYb2f929h4bSoyU1MurrkALBTCCV7x3N02+RNS7A==",
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/@fastify/mongodb/-/mongodb-9.0.2.tgz",
+      "integrity": "sha512-h04HpQ7nVeB2eR4YPJiFWaeFot+E6K6DHP5ymby3WEhExnVMaxd6FUVszDoU+bM3MmK9wtIFgJLUfOKcYU+nKQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
       "license": "MIT",
       "dependencies": {
         "fastify-plugin": "^5.0.0",
         "mongodb": "^6.5.0"
       }
     },
+    "node_modules/@fastify/proxy-addr": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz",
+      "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==",
+      "license": "MIT",
+      "dependencies": {
+        "@fastify/forwarded": "^3.0.0",
+        "ipaddr.js": "^2.1.0"
+      }
+    },
     "node_modules/@fastify/send": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/@fastify/send/-/send-3.3.0.tgz",
@@ -723,9 +766,19 @@
       }
     },
     "node_modules/@fastify/static": {
-      "version": "8.0.3",
-      "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.0.3.tgz",
-      "integrity": "sha512-GHSoOVDIxEYEeVR5l044bRCuAKDErD/+9VE+Z9fnaTRr+DDz0Avrm4kKai1mHbPx6C0U7BVNthjd/gcMquZZUA==",
+      "version": "8.0.4",
+      "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.0.4.tgz",
+      "integrity": "sha512-JdJIlXDYXZxbTFQazWOEfHxyD5uRXqRsLnp4rV9MwJnxadA0rrWBI8ZelPF2TPk/xDi5wunY/6ZmfwHXld13bA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
       "license": "MIT",
       "dependencies": {
         "@fastify/accept-negotiator": "^2.0.0",
@@ -1139,9 +1192,9 @@
       "license": "MIT"
     },
     "node_modules/@types/node": {
-      "version": "22.10.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz",
-      "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==",
+      "version": "22.10.9",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.9.tgz",
+      "integrity": "sha512-Ir6hwgsKyNESl/gLOcEz3krR4CBGgliDqBQ2ma4wIhEx0w+xnoeTq3tdrNw15kU3SxogDjOgv9sqdtLW8mIHaw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -1166,9 +1219,9 @@
       }
     },
     "node_modules/@types/qs": {
-      "version": "6.9.17",
-      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz",
-      "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==",
+      "version": "6.9.18",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
+      "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
       "dev": true,
       "license": "MIT"
     },
@@ -1200,30 +1253,30 @@
       }
     },
     "node_modules/@volar/language-core": {
-      "version": "2.4.8",
-      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.8.tgz",
-      "integrity": "sha512-K/GxMOXGq997bO00cdFhTNuR85xPxj0BEEAy+BaqqayTmy9Tmhfgmq2wpJcVspRhcwfgPoE2/mEJa26emUhG/g==",
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.11.tgz",
+      "integrity": "sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@volar/source-map": "2.4.8"
+        "@volar/source-map": "2.4.11"
       }
     },
     "node_modules/@volar/source-map": {
-      "version": "2.4.8",
-      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.8.tgz",
-      "integrity": "sha512-jeWJBkC/WivdelMwxKkpFL811uH/jJ1kVxa+c7OvG48DXc3VrP7pplSWPP2W1dLMqBxD+awRlg55FQQfiup4cA==",
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.11.tgz",
+      "integrity": "sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==",
       "dev": true,
       "license": "MIT"
     },
     "node_modules/@volar/typescript": {
-      "version": "2.4.8",
-      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.8.tgz",
-      "integrity": "sha512-6xkIYJ5xxghVBhVywMoPMidDDAFT1OoQeXwa27HSgJ6AiIKRe61RXLoik+14Z7r0JvnblXVsjsRLmCr42SGzqg==",
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.11.tgz",
+      "integrity": "sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@volar/language-core": "2.4.8",
+        "@volar/language-core": "2.4.11",
         "path-browserify": "^1.0.1",
         "vscode-uri": "^3.0.8"
       }
@@ -1295,17 +1348,17 @@
       "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
     },
     "node_modules/@vue/language-core": {
-      "version": "2.1.10",
-      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz",
-      "integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz",
+      "integrity": "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@volar/language-core": "~2.4.8",
+        "@volar/language-core": "~2.4.11",
         "@vue/compiler-dom": "^3.5.0",
         "@vue/compiler-vue2": "^2.7.16",
         "@vue/shared": "^3.5.0",
-        "alien-signals": "^0.2.0",
+        "alien-signals": "^0.4.9",
         "minimatch": "^9.0.3",
         "muggle-string": "^0.4.1",
         "path-browserify": "^1.0.1"
@@ -1450,9 +1503,9 @@
       }
     },
     "node_modules/alien-signals": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.0.tgz",
-      "integrity": "sha512-StlonZhBBrsPPwrDjiPAiVTf/rolxffLxVPT60Qv/t88BZ81BvUVzHgGqEFvJ1ii8HXtm1+zU2Icr59tfWEcag==",
+      "version": "0.4.14",
+      "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.14.tgz",
+      "integrity": "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==",
       "dev": true,
       "license": "MIT"
     },
@@ -1687,17 +1740,27 @@
         "ieee754": "^1.2.1"
       }
     },
-    "node_modules/call-bind": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
-      "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
+      "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
       "license": "MIT",
       "dependencies": {
-        "es-define-property": "^1.0.0",
         "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "set-function-length": "^1.2.1"
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
+      "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "get-intrinsic": "^1.2.6"
       },
       "engines": {
         "node": ">= 0.4"
@@ -1874,9 +1937,9 @@
       "dev": true
     },
     "node_modules/concurrently": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.0.tgz",
-      "integrity": "sha512-VxkzwMAn4LP7WyMnJNbHN5mKV9L2IbyDjpzemKr99sXNR3GqRNMMHdm7prV1ws9wg7ETj6WUkNOigZVsptwbgg==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz",
+      "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -2145,23 +2208,6 @@
         }
       }
     },
-    "node_modules/define-data-property": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
-      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0",
-        "es-errors": "^1.3.0",
-        "gopd": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -2253,6 +2299,20 @@
         "node": ">=12"
       }
     },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/eastasianwidth": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2291,9 +2351,20 @@
       }
     },
     "node_modules/env-schema": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/env-schema/-/env-schema-6.0.0.tgz",
-      "integrity": "sha512-/IHp1EmrfubUOfF1wfe8koDWM5/dxUDylHANPNrPyrsYWJ7KRiB8gXbjtqQBujmOhpSpXXOhhnaL+meb+MaGtA==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/env-schema/-/env-schema-6.0.1.tgz",
+      "integrity": "sha512-WRD40Q25pP4NUbI3g3CNU5PPzcaiX7YYcPwiCZlfR4qGsKmTlckRixgHww0/fOXiXSNKA87pwshzq0ULTK/48A==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "MIT",
       "dependencies": {
         "ajv": "^8.12.0",
         "dotenv": "^16.4.5",
@@ -2301,13 +2372,10 @@
       }
     },
     "node_modules/es-define-property": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
-      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
       "license": "MIT",
-      "dependencies": {
-        "get-intrinsic": "^1.2.4"
-      },
       "engines": {
         "node": ">= 0.4"
       }
@@ -2321,10 +2389,22 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/esbuild": {
-      "version": "0.24.0",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz",
-      "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==",
+      "version": "0.24.2",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
+      "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
       "dev": true,
       "hasInstallScript": true,
       "license": "MIT",
@@ -2335,30 +2415,31 @@
         "node": ">=18"
       },
       "optionalDependencies": {
-        "@esbuild/aix-ppc64": "0.24.0",
-        "@esbuild/android-arm": "0.24.0",
-        "@esbuild/android-arm64": "0.24.0",
-        "@esbuild/android-x64": "0.24.0",
-        "@esbuild/darwin-arm64": "0.24.0",
-        "@esbuild/darwin-x64": "0.24.0",
-        "@esbuild/freebsd-arm64": "0.24.0",
-        "@esbuild/freebsd-x64": "0.24.0",
-        "@esbuild/linux-arm": "0.24.0",
-        "@esbuild/linux-arm64": "0.24.0",
-        "@esbuild/linux-ia32": "0.24.0",
-        "@esbuild/linux-loong64": "0.24.0",
-        "@esbuild/linux-mips64el": "0.24.0",
-        "@esbuild/linux-ppc64": "0.24.0",
-        "@esbuild/linux-riscv64": "0.24.0",
-        "@esbuild/linux-s390x": "0.24.0",
-        "@esbuild/linux-x64": "0.24.0",
-        "@esbuild/netbsd-x64": "0.24.0",
-        "@esbuild/openbsd-arm64": "0.24.0",
-        "@esbuild/openbsd-x64": "0.24.0",
-        "@esbuild/sunos-x64": "0.24.0",
-        "@esbuild/win32-arm64": "0.24.0",
-        "@esbuild/win32-ia32": "0.24.0",
-        "@esbuild/win32-x64": "0.24.0"
+        "@esbuild/aix-ppc64": "0.24.2",
+        "@esbuild/android-arm": "0.24.2",
+        "@esbuild/android-arm64": "0.24.2",
+        "@esbuild/android-x64": "0.24.2",
+        "@esbuild/darwin-arm64": "0.24.2",
+        "@esbuild/darwin-x64": "0.24.2",
+        "@esbuild/freebsd-arm64": "0.24.2",
+        "@esbuild/freebsd-x64": "0.24.2",
+        "@esbuild/linux-arm": "0.24.2",
+        "@esbuild/linux-arm64": "0.24.2",
+        "@esbuild/linux-ia32": "0.24.2",
+        "@esbuild/linux-loong64": "0.24.2",
+        "@esbuild/linux-mips64el": "0.24.2",
+        "@esbuild/linux-ppc64": "0.24.2",
+        "@esbuild/linux-riscv64": "0.24.2",
+        "@esbuild/linux-s390x": "0.24.2",
+        "@esbuild/linux-x64": "0.24.2",
+        "@esbuild/netbsd-arm64": "0.24.2",
+        "@esbuild/netbsd-x64": "0.24.2",
+        "@esbuild/openbsd-arm64": "0.24.2",
+        "@esbuild/openbsd-x64": "0.24.2",
+        "@esbuild/sunos-x64": "0.24.2",
+        "@esbuild/win32-arm64": "0.24.2",
+        "@esbuild/win32-ia32": "0.24.2",
+        "@esbuild/win32-x64": "0.24.2"
       }
     },
     "node_modules/escalade": {
@@ -2465,9 +2546,9 @@
       "license": "MIT"
     },
     "node_modules/fastify": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.1.0.tgz",
-      "integrity": "sha512-0SdUC5AoiSgMSc2Vxwv3WyKzyGMDJRAW/PgNsK1kZrnkO6MeqUIW9ovVg9F2UGIqtIcclYMyeJa4rK6OZc7Jxg==",
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.2.1.tgz",
+      "integrity": "sha512-rslrNBF67eg8/Gyn7P2URV8/6pz8kSAscFL4EThZJ8JBMaXacVdVE4hmUcnPNKERl5o/xTiBSLfdowBRhVF1WA==",
       "funding": [
         {
           "type": "github",
@@ -2483,6 +2564,7 @@
         "@fastify/ajv-compiler": "^4.0.0",
         "@fastify/error": "^4.0.0",
         "@fastify/fast-json-stringify-compiler": "^5.0.0",
+        "@fastify/proxy-addr": "^5.0.0",
         "abstract-logging": "^2.0.1",
         "avvio": "^9.0.0",
         "fast-json-stringify": "^6.0.0",
@@ -2490,9 +2572,8 @@
         "light-my-request": "^6.0.0",
         "pino": "^9.0.0",
         "process-warning": "^4.0.0",
-        "proxy-addr": "^2.0.7",
         "rfdc": "^1.3.1",
-        "secure-json-parse": "^2.7.0",
+        "secure-json-parse": "^3.0.1",
         "semver": "^7.6.0",
         "toad-cache": "^3.7.0"
       }
@@ -2503,6 +2584,22 @@
       "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==",
       "license": "MIT"
     },
+    "node_modules/fastify/node_modules/secure-json-parse": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz",
+      "integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
     "node_modules/fastq": {
       "version": "1.17.1",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@@ -2585,14 +2682,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/forwarded": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
-      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/fraction.js": {
       "version": "4.3.7",
       "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -2639,16 +2728,21 @@
       }
     },
     "node_modules/get-intrinsic": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
-      "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
+      "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
       "license": "MIT",
       "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-define-property": "^1.0.1",
         "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
         "function-bind": "^1.1.2",
-        "has-proto": "^1.0.1",
-        "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0"
+        "get-proto": "^1.0.0",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
       },
       "engines": {
         "node": ">= 0.4"
@@ -2657,6 +2751,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/glob": {
       "version": "11.0.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
@@ -2725,33 +2832,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/has-property-descriptors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
-      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
-      "license": "MIT",
-      "dependencies": {
-        "es-define-property": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/has-proto": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.1.0.tgz",
-      "integrity": "sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==",
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/has-symbols": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -2840,11 +2920,12 @@
       "license": "ISC"
     },
     "node_modules/ipaddr.js": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
-      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
+      "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
+      "license": "MIT",
       "engines": {
-        "node": ">= 0.10"
+        "node": ">= 10"
       }
     },
     "node_modules/is-binary-path": {
@@ -2999,6 +3080,15 @@
         "@jridgewell/sourcemap-codec": "^1.5.0"
       }
     },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/mdn-data": {
       "version": "2.0.30",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@@ -3084,13 +3174,13 @@
       }
     },
     "node_modules/mongodb": {
-      "version": "6.11.0",
-      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.11.0.tgz",
-      "integrity": "sha512-yVbPw0qT268YKhG241vAMLaDQAPbRyTgo++odSgGc9kXnzOujQI60Iyj23B9sQQFPSvmNPvMZ3dsFz0aN55KgA==",
+      "version": "6.12.0",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz",
+      "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==",
       "license": "Apache-2.0",
       "dependencies": {
         "@mongodb-js/saslprep": "^1.1.9",
-        "bson": "^6.10.0",
+        "bson": "^6.10.1",
         "mongodb-connection-string-url": "^3.0.0"
       },
       "engines": {
@@ -3098,7 +3188,7 @@
       },
       "peerDependencies": {
         "@aws-sdk/credential-providers": "^3.188.0",
-        "@mongodb-js/zstd": "^1.1.0",
+        "@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
         "gcp-metadata": "^5.2.0",
         "kerberos": "^2.0.1",
         "mongodb-client-encryption": ">=6.0.0 <7",
@@ -3175,9 +3265,9 @@
       "dev": true
     },
     "node_modules/nodemon": {
-      "version": "3.1.7",
-      "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz",
-      "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==",
+      "version": "3.1.9",
+      "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
+      "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -3354,9 +3444,9 @@
       }
     },
     "node_modules/pinia": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.0.tgz",
-      "integrity": "sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==",
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz",
+      "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==",
       "license": "MIT",
       "dependencies": {
         "@vue/devtools-api": "^6.6.3",
@@ -3476,9 +3566,9 @@
       "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
     },
     "node_modules/plotly.js-dist-min": {
-      "version": "2.35.2",
-      "resolved": "https://registry.npmjs.org/plotly.js-dist-min/-/plotly.js-dist-min-2.35.2.tgz",
-      "integrity": "sha512-oWDTf2kYOmTtEw3epeeSBdfH/H3OSktF0suST9oI6fIgKfbyd4MT7TPh8+CVzdHYllYon24Q0HI1hZjOnLqk6g==",
+      "version": "2.35.3",
+      "resolved": "https://registry.npmjs.org/plotly.js-dist-min/-/plotly.js-dist-min-2.35.3.tgz",
+      "integrity": "sha512-sz2HLP8gkysLx/BanM2PtJTtZ1PLPwdHwMWNri2YxLBy3IOeuDsVQtlmWa4hoK3j/fi4naaD3uZJqH5ozM3zGg==",
       "license": "MIT"
     },
     "node_modules/postcss": {
@@ -3953,18 +4043,6 @@
       "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==",
       "license": "MIT"
     },
-    "node_modules/proxy-addr": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
-      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
-      "dependencies": {
-        "forwarded": "0.2.0",
-        "ipaddr.js": "1.9.1"
-      },
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
     "node_modules/proxy-from-env": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -3995,12 +4073,12 @@
       }
     },
     "node_modules/qs": {
-      "version": "6.13.1",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz",
-      "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==",
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
       "license": "BSD-3-Clause",
       "dependencies": {
-        "side-channel": "^1.0.6"
+        "side-channel": "^1.1.0"
       },
       "engines": {
         "node": ">=0.6"
@@ -4198,7 +4276,8 @@
     "node_modules/secure-json-parse": {
       "version": "2.7.0",
       "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
-      "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="
+      "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
+      "dev": true
     },
     "node_modules/semver": {
       "version": "7.6.3",
@@ -4217,23 +4296,6 @@
       "integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==",
       "license": "MIT"
     },
-    "node_modules/set-function-length": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
-      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
-      "license": "MIT",
-      "dependencies": {
-        "define-data-property": "^1.1.4",
-        "es-errors": "^1.3.0",
-        "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.4",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/setprototypeof": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -4269,15 +4331,69 @@
       }
     },
     "node_modules/side-channel": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
-      "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
       "license": "MIT",
       "dependencies": {
-        "call-bind": "^1.0.7",
         "es-errors": "^1.3.0",
-        "get-intrinsic": "^1.2.4",
-        "object-inspect": "^1.13.1"
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4558,9 +4674,9 @@
       "dev": true
     },
     "node_modules/typescript": {
-      "version": "5.6.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
-      "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
+      "version": "5.7.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+      "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
       "devOptional": true,
       "license": "Apache-2.0",
       "bin": {
@@ -4629,13 +4745,13 @@
       "dev": true
     },
     "node_modules/vite": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz",
-      "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==",
+      "version": "6.0.11",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz",
+      "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "esbuild": "^0.24.0",
+        "esbuild": "^0.24.2",
         "postcss": "^8.4.49",
         "rollup": "^4.23.0"
       },
@@ -4744,15 +4860,14 @@
       }
     },
     "node_modules/vue-tsc": {
-      "version": "2.1.10",
-      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz",
-      "integrity": "sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.0.tgz",
+      "integrity": "sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@volar/typescript": "~2.4.8",
-        "@vue/language-core": "2.1.10",
-        "semver": "^7.5.4"
+        "@volar/typescript": "~2.4.11",
+        "@vue/language-core": "2.2.0"
       },
       "bin": {
         "vue-tsc": "bin/vue-tsc.js"
diff --git a/package.json b/package.json
index 031254e5a22d3d22ff7943bf357c8c0ded36c5cd..ca2a2f5190353ddd842c05438477a5d435857898 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,9 @@
     "dev:client": "vite",
     "dev:server": "nodemon --use-strict src/server/app.js | pino-pretty -t 'yyyy-mm-dd HH:MM:ss'",
     "build": "vue-tsc && vite build",
-    "build:db": "node scripts/buildDatabase.js",
-    "stats": "node scripts/buildStatistics",
-    "validate": "node scripts/fastaValidator.js",
+    "build:db": "node src/scripts/buildDatabase.js",
+    "stats": "node src/scripts/displayStatistics",
+    "validate": "node src/scripts/fastaValidator.js",
     "lint": "npm run lint:client; npm run lint:server",
     "lint:fix": "npm run lint:client:fix; npm run lint:server:fix",
     "lint:client": "biome check src/client",
@@ -28,34 +28,34 @@
   "author": "Simon Malesys",
   "license": "GPL-3.0",
   "dependencies": {
-    "@fastify/mongodb": "^9.0.1",
-    "@fastify/static": "^8.0.3",
+    "@fastify/mongodb": "^9.0.2",
+    "@fastify/static": "^8.0.4",
     "axios": "^1.7.9",
-    "env-schema": "^6.0.0",
-    "fastify": "^5.1.0",
+    "env-schema": "^6.0.1",
+    "fastify": "^5.2.1",
     "modern-normalize": "^3.0.1",
-    "mongodb": "^6.11.0",
-    "pinia": "^2.3.0",
-    "plotly.js-dist-min": "^2.35.2",
-    "qs": "^6.13.1",
+    "mongodb": "^6.12.0",
+    "pinia": "^2.3.1",
+    "plotly.js-dist-min": "^2.35.3",
+    "qs": "^6.14.0",
     "vue": "^3.5.13",
     "vue-router": "^4.5.0"
   },
   "devDependencies": {
     "@biomejs/biome": "^1.9.4",
-    "@types/node": "^22.10.1",
+    "@types/node": "^22.10.9",
     "@types/plotly.js-dist-min": "^2.3.4",
-    "@types/qs": "^6.9.17",
+    "@types/qs": "^6.9.18",
     "@vitejs/plugin-vue": "^5.2.1",
     "autoprefixer": "^10.4.20",
-    "concurrently": "^9.1.0",
+    "concurrently": "^9.1.2",
     "cssnano": "^7.0.6",
-    "nodemon": "^3.1.7",
+    "nodemon": "^3.1.9",
     "pino-pretty": "^13.0.0",
     "rollup-plugin-brotli": "^3.1.0",
     "rollup-plugin-gzip": "^4.0.1",
-    "typescript": "^5.6.3",
-    "vite": "^6.0.3",
-    "vue-tsc": "^2.1.10"
+    "typescript": "^5.7.3",
+    "vite": "^6.0.11",
+    "vue-tsc": "^2.2.0"
   }
 }
diff --git a/scripts/antibodySchema.json b/scripts/antibodySchema.json
deleted file mode 100644
index 2402bc28624d9501c7e9627647615d9caaf04dfd..0000000000000000000000000000000000000000
--- a/scripts/antibodySchema.json
+++ /dev/null
@@ -1,95 +0,0 @@
-{
-  "$jsonSchema": {
-    "title": "Antibody validation schema",
-    "required": [
-      "_id",
-      "id",
-      "species",
-      "heavyChain",
-      "lightChain"
-    ],
-    "additionalProperties": false,
-    "properties": {
-      "_id": {
-        "bsonType": "objectId"
-      },
-      "id": {
-        "bsonType": "string"
-      },
-      "hashId": {
-        "bsonType": "string"
-      },
-      "species": {
-        "bsonType": "string"
-      },
-      "heavyChain": {
-        "bsonType": "object",
-        "properties": {
-          "headers": {
-            "bsonType": "array",
-            "items": {
-              "bsonType": "object",
-              "properties": {
-                "id": {
-                  "bsonType": "string"
-                },
-                "source": {
-                  "bsonType": "string"
-                },
-                "header": {
-                  "bsonType": "string"
-                }
-              },
-              "required": [
-                "id",
-                "source",
-                "header"
-              ],
-              "additionalProperties": false
-            }
-          },
-          "sequence": {
-            "bsonType": "string"
-          },
-          "rawFasta": {
-            "bsonType": "string"
-          }
-        }
-      },
-      "lightChain": {
-        "bsonType": "object",
-        "properties": {
-          "headers": {
-            "bsonType": "array",
-            "items": {
-              "bsonType": "object",
-              "properties": {
-                "id": {
-                  "bsonType": "string"
-                },
-                "source": {
-                  "bsonType": "string"
-                },
-                "header": {
-                  "bsonType": "string"
-                }
-              },
-              "required": [
-                "id",
-                "source",
-                "header"
-              ],
-              "additionalProperties": false
-            }
-          },
-          "sequence": {
-            "bsonType": "string"
-          },
-          "rawFasta": {
-            "bsonType": "string"
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/scripts/buildStatistics.js b/scripts/buildStatistics.js
deleted file mode 100644
index 3b63f8d06d8b71876b1512029e66ef25c4abc9c9..0000000000000000000000000000000000000000
--- a/scripts/buildStatistics.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import buildStatistics from './fastaStatsBuilder.js'
-
-/**
- * Wrapper file of the fastaStatsBuilder to compute the
- * statistics from the command line, with the
- * npm script 'npm run stats'.
- */
-buildStatistics().then(result => {
-  // Stringify the stats object with a replacer function
-  // to handle the sets.
-  const stringifiedStats = JSON.stringify(result, (key, value) => {
-    return value instanceof Set ? [...value] : value
-  }, 2)
-
-  console.log(stringifiedStats)
-  process.exit(0)
-}).catch(err => {
-  console.log(err)
-  process.exit(1)
-})
diff --git a/scripts/fastaStatsBuilder.js b/scripts/fastaStatsBuilder.js
deleted file mode 100644
index b1903c741e6df4052f7de49019b8c47ef56ef093..0000000000000000000000000000000000000000
--- a/scripts/fastaStatsBuilder.js
+++ /dev/null
@@ -1,152 +0,0 @@
-import { createReadStream } from 'node:fs'
-import { readdir } from 'node:fs/promises'
-import { dirname, resolve } from 'node:path'
-import { Writable } from 'node:stream'
-import { pipeline } from 'node:stream/promises'
-import { fileURLToPath } from 'node:url'
-import { SplitFastaStream } from './streams/SplitFastaStream.js'
-import { ParseFastaStream } from './streams/ParseFastaStream.js'
-import { SanitizeFastaStream } from './streams/SanitizeFastaStream.js'
-import { BuildAntibodiesStream } from './streams/BuildAntibodiesStream.js'
-
-/**
- * Statistics of the antibodies.
- */
-const stats = {
-  antibodies: 0,
-  species: new Set(),
-  sources: new Set(),
-  antibodiesPerSpecies: {},
-  antibodiesPerSource: {},
-  antibodiesPerHeavySegments: {},
-  antibodiesPerLightSegments: {},
-  antibodiesOnlyInSource: {},
-  antibodiesInMultipleSources: {}
-}
-
-/**
- * Make a unique list of the sources for an antibody.
- * @param {object} antibody - The given antibody
- * @return {Set<string>} The list of sources present in this antibody
- */
-function listSources(antibody) {
-  const sources = new Set()
-  antibody.heavyChain.headers.forEach(header => { sources.add(header.source) })
-  antibody.lightChain.headers.forEach(header => { sources.add(header.source) })
-  return sources
-}
-
-/**
- * Make a unique list of the v gene segments for an antibody.
- * @param {object} antibody - The given antibody
- * @return {object} An object with the two lists of heavy and light chain segments.
- */
-function listSegments(antibody) {
-  const segments = {
-    heavyChain: new Set(),
-    lightChain: new Set()
-  }
-  antibody.heavyChain.headers.forEach(header => { segments.heavyChain.add(header.vGeneSegment) })
-  antibody.lightChain.headers.forEach(header => { segments.lightChain.add(header.vGeneSegment) })
-  return segments
-}
-
-/**
- * Build the statistics
- */
-class StatisticsStream extends Writable {
-  constructor(options) {
-    if (!options) options = {};
-    options.objectMode = true;
-    super(options);
-  }
-
-  _write(antibody, encoding, done) {
-    const species = antibody.species;
-    const sources = listSources(antibody);
-    const segments = listSegments(antibody);
-
-    // Total antibodies
-    stats.antibodies += 1;
-
-    // Total species
-    stats.species.add(antibody.species);
-
-    // Total sources
-    stats.sources = stats.sources.union(sources);
-
-    // Antibodies per species
-    stats.antibodiesPerSpecies[species] = stats.antibodiesPerSpecies[species]
-      ? stats.antibodiesPerSpecies[species] + 1
-      : 1;
-
-    // Antibodies per source
-    sources.forEach(source => {
-      stats.antibodiesPerSource[source] = stats.antibodiesPerSource[source]
-        ? stats.antibodiesPerSource[source] + 1
-        : 1;
-    });
-
-    // Antibodies per heavy segments
-    segments.heavyChain.forEach(segment => {
-      stats.antibodiesPerHeavySegments[segment] = stats.antibodiesPerHeavySegments[segment]
-        ? stats.antibodiesPerHeavySegments[segment] + 1
-        : 1;
-    });
-
-    // Antibodies per light segments
-    segments.lightChain.forEach(segment => {
-      stats.antibodiesPerLightSegments[segment] = stats.antibodiesPerLightSegments[segment]
-        ? stats.antibodiesPerLightSegments[segment] + 1
-        : 1;
-    });
-
-    // Antibodies only in one source
-    if (sources.size === 1) {
-      const source = Array.from(sources)[0];
-      stats.antibodiesOnlyInSource[source] = stats.antibodiesOnlyInSource[source]
-        ? stats.antibodiesOnlyInSource[source] + 1
-        : 1;
-    }
-
-    // Antibodies in multiple sources
-    if (sources.size >= 1) {
-      stats.antibodiesInMultipleSources[sources.size] = stats.antibodiesInMultipleSources[sources.size]
-        ? stats.antibodiesInMultipleSources[sources.size] + 1
-        : 1;
-    }
-
-    return done();
-  }
-}
-
-// =========================================================================
-
-export default async function () {
-  const currentPath = dirname(fileURLToPath(import.meta.url))
-  const dataFolderPath = resolve(currentPath, '../data')
-  const dataFolder = await readdir(dataFolderPath)
-  const fastaList = dataFolder.filter(fileName => {
-    return fileName.endsWith('.fasta')
-  })
-
-  for (const fastaFileName of fastaList) {
-    const fastaFile = resolve(dataFolderPath, fastaFileName)
-    const species = fastaFileName.replace('.fasta', '').replace('_', ' ')
-
-    await pipeline(
-      createReadStream(fastaFile),
-      new SplitFastaStream(),
-      new ParseFastaStream(),
-      new SanitizeFastaStream(),
-      new BuildAntibodiesStream({ species }),
-      new StatisticsStream()
-    ).catch(err => {
-      console.log(`${fastaFile}: \x1B[1;31merror\x1B[0m`)
-      console.log(err.message)
-      process.exit(1)
-    })
-  }
-
-  return stats
-}
diff --git a/scripts/fastaValidator.js b/scripts/fastaValidator.js
deleted file mode 100644
index 74593f62c60614dbeedb3b4d60f046df7cf2c37b..0000000000000000000000000000000000000000
--- a/scripts/fastaValidator.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import { createReadStream } from 'node:fs'
-import { readdir } from 'node:fs/promises'
-import { dirname, resolve } from 'node:path'
-import { createHash } from 'node:crypto'
-import { Writable } from 'node:stream'
-import { pipeline } from 'node:stream/promises'
-import { fileURLToPath } from 'node:url'
-import { SplitFastaStream } from './streams/SplitFastaStream.js'
-import { ParseFastaStream } from './streams/ParseFastaStream.js'
-
-const fileNameFormat = /^[A-Z][a-z]+_[a-z]+\.fasta$/
-const idFormat = /^[^\s^]+$/
-const subHeaderFormat = /.+;[^\s]+;[^\s]+;[^\s]+$/
-const sequenceFormat = /^[ABCDEFGHIKLMNPQRSTUVWXYZ]+$/
-
-/**
- * Stream for validating fasta entries.
- */
-export class ValidateFastaStream extends Writable {
-  constructor(options) {
-    if (!options) options = {};
-    options.objectMode = true;
-    super(options);
-
-    this.sequenceHashes = new Set();
-  }
-
-  _write(fastaObject, encoding, done) {
-    const [id, ...headers] = fastaObject.header.split('|||');
-    const sequence = fastaObject.sequence;
-
-    if (!idFormat.test(id)) {
-      return done(new Error(`\x1B[1;31mError with header: ${header} \nID must not contain any space.\x1B[0m`));
-    }
-
-    for (const subheader of headers) {
-      if (!subHeaderFormat.test(subheader)) {
-        return done(new Error(`\x1B[1;31mError with subheader: ${subheader} \nThe subheaders must have an id, V gene segment and source at their end, separated by semicolons: header;id;vGeneSegment;source. ID, V gene segment and source must not contain any space.\x1B[0m`));
-      }
-    }
-
-    if (!sequenceFormat.test(sequence)) {
-      return done(new Error(`\x1B[1;31mError with sequence: ${sequence} \nSequences must only contain characters among "ABCDEFGHIKLMNPQRSTUVWXYZ".\x1B[0m`));
-    }
-
-    const hash = createHash('sha256').update(sequence).digest('hex');
-
-    // Check the uniqueness of each sequence trough the whole fasta file
-    if (this.sequenceHashes.has(hash)) {
-      return done(new Error(`\x1B[1;31mDuplication error with sequence: ${sequence} \nThis one already exist in the file.`));
-    } else {
-      this.sequenceHashes.add(hash);
-    }
-
-    return done();
-  }
-}
-
-// =========================================================================
-
-const currentPath = dirname(fileURLToPath(import.meta.url))
-const dataFolderPath = resolve(currentPath, '../data')
-const dataFolder = await readdir(dataFolderPath)
-const fastaList = dataFolder.filter(fileName => {
-  return fileName.endsWith('.fasta')
-})
-
-for (const fastaFileName of fastaList) {
-  // Validate that the fasta file name follows the format "Genus_species.fasta".
-  if (!fileNameFormat.test(fastaFileName)) {
-    console.log(`\x1B[1;31mError with file name: ${fastaFileName} \nFasta file names must follow the format "Genus_species.fasta".\x1B[0m`)
-    process.exit(1)
-  }
-
-  const fastaFile = resolve(dataFolderPath, fastaFileName)
-
-  await pipeline(
-    createReadStream(fastaFile, { encoding: 'utf-8' }),
-    new SplitFastaStream(),
-    new ParseFastaStream(),
-    new ValidateFastaStream(),
-  ).then(async () => {
-    console.log(`${fastaFile}: \x1B[1;32mvalidation complete\x1B[0m`)
-  }).catch(err => {
-    console.log(`${fastaFile}: \x1B[1;31merror\x1B[0m`)
-    console.log(err.message)
-    process.exit(1)
-  })
-}
diff --git a/src/client/components/TheStatisticsPage.vue b/src/client/components/TheStatisticsPage.vue
index 403386fed20b0207b9e68b53116ba3a4e1f3d7af..6aeeed698d40a8f4736d3bce36184ae8bfaeca37 100644
--- a/src/client/components/TheStatisticsPage.vue
+++ b/src/client/components/TheStatisticsPage.vue
@@ -119,8 +119,8 @@ onMounted(() => {
 
 watch(() => statistics.value?.antibodiesPerSpecies, () => buildSpeciesPlot())
 watch(() => statistics.value?.antibodiesPerSource, () => buildSourcesPlot())
-watch(() => statistics.value?.antibodiesPerHeavySegments, () => buildHeavySegmentsPlot())
-watch(() => statistics.value?.antibodiesPerLightSegments, () => buildLightSegmentsPlot())
+watch(() => statistics.value?.antibodiesPerHeavySegment, () => buildHeavySegmentsPlot())
+watch(() => statistics.value?.antibodiesPerLightSegment, () => buildLightSegmentsPlot())
 watch(() => statistics.value, () => buildSankeyPlot())
 
 
@@ -168,9 +168,9 @@ function buildHeavySegmentsPlot() {
   data[0].values = []
   data[0].labels = []
 
-  const antibodiesPerHeavySegments = Object.entries(statistics.value?.antibodiesPerHeavySegments)
+  const antibodiesPerHeavySegment = Object.entries(statistics.value?.antibodiesPerHeavySegment)
 
-  for (const [source, number] of antibodiesPerHeavySegments) {
+  for (const [source, number] of antibodiesPerHeavySegment) {
     data[0].labels.push(`${source} - <i>${number.toLocaleString()}<\i>`)
     data[0].values.push(number)
   }
@@ -186,9 +186,9 @@ function buildLightSegmentsPlot() {
   data[0].values = []
   data[0].labels = []
 
-  const antibodiesPerLightSegments = Object.entries(statistics.value?.antibodiesPerLightSegments)
+  const antibodiesPerLightSegment = Object.entries(statistics.value?.antibodiesPerLightSegment)
 
-  for (const [source, number] of antibodiesPerLightSegments) {
+  for (const [source, number] of antibodiesPerLightSegment) {
     data[0].labels.push(`${source} - <i>${number.toLocaleString()}<\i>`)
     data[0].values.push(number)
   }
diff --git a/scripts/buildDatabase.js b/src/scripts/buildDatabase.js
similarity index 90%
rename from scripts/buildDatabase.js
rename to src/scripts/buildDatabase.js
index 8ff878cc3eb02a952d54113d165cdc49e25548ec..c18da80b4e88e3dcfae7f8f163566e362ec5e3bd 100644
--- a/scripts/buildDatabase.js
+++ b/src/scripts/buildDatabase.js
@@ -1,5 +1,5 @@
 import buildDatabase from './databaseBuilder.js'
-import config from '../src/server/config/env.js'
+import config from '../server/config/env.js'
 
 console.log(`--- Start building the \x1B[1m${config.ABSD_DB_NAME}\x1B[0m database`)
 
diff --git a/scripts/databaseBuilder.js b/src/scripts/databaseBuilder.js
similarity index 64%
rename from scripts/databaseBuilder.js
rename to src/scripts/databaseBuilder.js
index e63c2d6342b3c34139b447c847b5962d96042bd6..a13774cb5e7f1244c3dc11fb3195835d0c5ad52f 100644
--- a/scripts/databaseBuilder.js
+++ b/src/scripts/databaseBuilder.js
@@ -1,5 +1,5 @@
 import { createReadStream } from 'node:fs'
-import { readdir } from 'node:fs/promises'
+import { readdir, readFile } from 'node:fs/promises'
 import { dirname, resolve } from 'node:path'
 import { pipeline } from 'node:stream/promises'
 import { fileURLToPath } from 'node:url'
@@ -11,9 +11,9 @@ import { BuildAntibodiesStream } from './streams/BuildAntibodiesStream.js'
 import { GenerateHashIdStream } from './streams/GenerateHashIdStream.js'
 import { GroupAntibodiesStream } from './streams/GroupAntibodiesStream.js'
 import { InsertAntibodiesStream } from './streams/InsertAntibodiesStream.js'
-import AntibodySchema from '../src/server/schemas/Antibodies.json' with { type: "json" }
-import StatisticsSchema from '../src/server/schemas/Statistics.json' with { type: "json" }
-import config from '../src/server/config/env.js'
+import AntibodySchema from '../server/schemas/Antibodies.json' with { type: "json" }
+import StatisticsSchema from '../server/schemas/Statistics.json' with { type: "json" }
+import config from '../server/config/env.js'
 
 /**
  * Create or cleanup existing database to repopulate it
@@ -53,17 +53,63 @@ async function createDatabase() {
   console.log(`Database \x1B[1;32m${config.ABSD_DB_NAME}\x1B[0m created`)
 }
 
+/**
+ * Insert the last statistics file data into the database.
+ * This process is simple enough to not need streams.
+ * @param {object} stats - The statistics object
+ * @param {object} dbConfig - An object with the database configuration
+ * @returns {Promise<object>} - A promise with the results of the insertion.
+ */
+async function insertStatistics(stats, dbConfig) {
+  const uri = `mongodb://${dbConfig.host}:${dbConfig.port}`
+  const mongoClient = new MongoClient(uri)
+  const db = mongoClient.db(dbConfig.name)
+  const collection = db.collection('statistics')
+
+  return collection.insertOne(stats)
+}
+
+/**
+ * Get the last statistics from the stats files.
+ * @returns {Promise<object>} A promise with the statistics object.
+ */
+async function getStatistics() {
+  const currentPath = dirname(fileURLToPath(import.meta.url))
+  const dataFolderPath = resolve(currentPath, '../../data')
+  const dataFolder = await readdir(dataFolderPath)
+
+  const statsFilePath = dataFolder
+    .filter((file) => { return file.endsWith('-stats.json') })
+    .sort()
+    .reverse()
+    .map(file => { return resolve(dataFolderPath, file) })
+    .shift()
+
+  const statsDate = new Date(statsFilePath.match(/(\d{4}-\d{2}-\d{2})-stats/)[1])
+  const statsFile = await readFile(statsFilePath, 'utf8')
+  const stats = JSON.parse(statsFile)
+  stats.versionDate = statsDate
+
+  return stats
+}
+
 // =========================================================================
 
 export default async function buildDatabase() {
   const currentPath = dirname(fileURLToPath(import.meta.url))
-  const dataFolderPath = resolve(currentPath, '../data')
+  const dataFolderPath = resolve(currentPath, '../../data')
   const dataFolder = await readdir(dataFolderPath)
   const fastaList = dataFolder.filter(fileName => {
     return fileName.endsWith('.fasta')
   })
+  const stats = await getStatistics()
 
   await createDatabase()
+  await insertStatistics(stats, {
+    host: config.ABSD_DB_HOST,
+    port: config.ABSD_DB_PORT,
+    name: config.ABSD_DB_NAME
+  })
 
   for (const fastaFileName of fastaList) {
     const fastaFile = resolve(dataFolderPath, fastaFileName)
diff --git a/src/scripts/displayStatistics.js b/src/scripts/displayStatistics.js
new file mode 100644
index 0000000000000000000000000000000000000000..b851c867782f07d4324a7d34a247c512a428d5e4
--- /dev/null
+++ b/src/scripts/displayStatistics.js
@@ -0,0 +1,13 @@
+import buildStatistics from './fastaStatsBuilder.js'
+
+/**
+ * Wrapper file of the fastaStatsBuilder to display the
+ * statistics from the command line, using a npm script.
+ */
+buildStatistics().then(stats => {
+  console.log(stats.toString())
+  process.exit(0)
+}).catch(err => {
+  console.log(err)
+  process.exit(1)
+})
diff --git a/src/scripts/fastaStatsBuilder.js b/src/scripts/fastaStatsBuilder.js
new file mode 100644
index 0000000000000000000000000000000000000000..873be1644d25bda0de326b6739488f1fc8d98a8f
--- /dev/null
+++ b/src/scripts/fastaStatsBuilder.js
@@ -0,0 +1,44 @@
+import { createReadStream } from 'node:fs'
+import { readdir } from 'node:fs/promises'
+import { dirname, resolve } from 'node:path'
+import { pipeline } from 'node:stream/promises'
+import { fileURLToPath } from 'node:url'
+import { SplitFastaStream } from './streams/SplitFastaStream.js'
+import { ParseFastaStream } from './streams/ParseFastaStream.js'
+import { SanitizeFastaStream } from './streams/SanitizeFastaStream.js'
+import { BuildAntibodiesStream } from './streams/BuildAntibodiesStream.js'
+import { StatisticsStream } from './streams/StatisticsStream.js'
+import { Statistics } from '../server/models/Statistics.js'
+
+// =========================================================================
+
+export default async function () {
+  const currentPath = dirname(fileURLToPath(import.meta.url))
+  const dataFolderPath = resolve(currentPath, '../../data')
+  const dataFolder = await readdir(dataFolderPath)
+  const fastaList = dataFolder.filter(fileName => {
+    return fileName.endsWith('.fasta')
+  })
+
+  const stats = new Statistics()
+
+  for (const fastaFileName of fastaList) {
+    const fastaFile = resolve(dataFolderPath, fastaFileName)
+    const species = fastaFileName.replace('.fasta', '').replace('_', ' ')
+
+    await pipeline(
+      createReadStream(fastaFile),
+      new SplitFastaStream(),
+      new ParseFastaStream(),
+      new SanitizeFastaStream(),
+      new BuildAntibodiesStream({ species }),
+      new StatisticsStream({ stats })
+    ).catch(err => {
+      console.log(`${fastaFile}: \x1B[1;31merror\x1B[0m`)
+      console.log(err.message)
+      process.exit(1)
+    })
+  }
+
+  return stats
+}
diff --git a/src/scripts/fastaValidator.js b/src/scripts/fastaValidator.js
new file mode 100644
index 0000000000000000000000000000000000000000..0dbf374e836df3e9060aeb9f364eeeaf33e474b9
--- /dev/null
+++ b/src/scripts/fastaValidator.js
@@ -0,0 +1,40 @@
+import { createReadStream } from 'node:fs'
+import { readdir } from 'node:fs/promises'
+import { dirname, resolve } from 'node:path'
+import { pipeline } from 'node:stream/promises'
+import { fileURLToPath } from 'node:url'
+import { SplitFastaStream } from './streams/SplitFastaStream.js'
+import { ParseFastaStream } from './streams/ParseFastaStream.js'
+import { ValidateFastaStream } from './streams/ValidateFastaStream.js'
+
+// =========================================================================
+
+const currentPath = dirname(fileURLToPath(import.meta.url))
+const dataFolderPath = resolve(currentPath, '../../data')
+const dataFolder = await readdir(dataFolderPath)
+const fastaList = dataFolder.filter(fileName => {
+  return fileName.endsWith('.fasta')
+})
+
+for (const fastaFileName of fastaList) {
+  // Validate that the fasta file name follows the format "Genus_species.fasta".
+  if (!ValidateFastaStream.fileNameFormat.test(fastaFileName)) {
+    console.log(`\x1B[1;31mError with file name: ${fastaFileName} \nFasta file names must follow the format "Genus_species.fasta".\x1B[0m`)
+    process.exit(1)
+  }
+
+  const fastaFile = resolve(dataFolderPath, fastaFileName)
+
+  await pipeline(
+    createReadStream(fastaFile, { encoding: 'utf-8' }),
+    new SplitFastaStream(),
+    new ParseFastaStream(),
+    new ValidateFastaStream(),
+  ).then(async () => {
+    console.log(`${fastaFile}: \x1B[1;32mvalidation complete\x1B[0m`)
+  }).catch(err => {
+    console.log(`${fastaFile}: \x1B[1;31merror\x1B[0m`)
+    console.log(err.message)
+    process.exit(1)
+  })
+}
diff --git a/scripts/streams/BuildAntibodiesStream.js b/src/scripts/streams/BuildAntibodiesStream.js
similarity index 92%
rename from scripts/streams/BuildAntibodiesStream.js
rename to src/scripts/streams/BuildAntibodiesStream.js
index a52392e47e699028af639adecb2dd1f68d70effc..5461dbf9a173f83cffeca33fa36faae333d54906 100644
--- a/scripts/streams/BuildAntibodiesStream.js
+++ b/src/scripts/streams/BuildAntibodiesStream.js
@@ -54,11 +54,15 @@ export class BuildAntibodiesStream extends Transform {
   }
 
   _transform(fastaEntry, encoding, done) {
+    // Light chains always come first, so store it
+    // for the next entry.
     if (!this.lightChain) {
       this.lightChain = fastaEntry;
       return done();
     }
 
+    // If a light chain is stored, then we can build
+    // the antibody.
     this.push({
       id: this.lightChain.header.split('|||')[0],
       species: this.species,
diff --git a/scripts/streams/GenerateHashIdStream.js b/src/scripts/streams/GenerateHashIdStream.js
similarity index 62%
rename from scripts/streams/GenerateHashIdStream.js
rename to src/scripts/streams/GenerateHashIdStream.js
index 5221b835f956b37ea9be278d7dfe364ef198b718..2a70434f0d95a81fcd094f93811f5565524f4b51 100644
--- a/scripts/streams/GenerateHashIdStream.js
+++ b/src/scripts/streams/GenerateHashIdStream.js
@@ -1,5 +1,5 @@
-import { createHash } from "crypto";
 import { Transform } from "stream";
+import { Antibody } from "../../server/models/Antibody.js";
 
 /**
  * Add the hashId field to antibodies object.
@@ -13,14 +13,7 @@ export class GenerateHashIdStream extends Transform {
   }
 
   _transform(antibody, encoding, done) {
-    const hashInput = antibody.species +
-      antibody.heavyChain.sequence +
-      antibody.lightChain.sequence;
-
-    antibody.hashId = createHash('sha256')
-      .update(hashInput, 'ascii')
-      .digest('hex');
-
+    antibody.hashId = Antibody.generateHashId(antibody)
     this.push(antibody);
     return done();
   }
diff --git a/scripts/streams/GroupAntibodiesStream.js b/src/scripts/streams/GroupAntibodiesStream.js
similarity index 100%
rename from scripts/streams/GroupAntibodiesStream.js
rename to src/scripts/streams/GroupAntibodiesStream.js
diff --git a/scripts/streams/InsertAntibodiesStream.js b/src/scripts/streams/InsertAntibodiesStream.js
similarity index 96%
rename from scripts/streams/InsertAntibodiesStream.js
rename to src/scripts/streams/InsertAntibodiesStream.js
index f92129b726812852f51bb1593955c7df6044ca5c..9ed83b21810bc5362bcea3a6b34187d5b8136a76 100644
--- a/scripts/streams/InsertAntibodiesStream.js
+++ b/src/scripts/streams/InsertAntibodiesStream.js
@@ -1,6 +1,6 @@
 import { MongoClient } from "mongodb";
 import { Writable } from "stream";
-import config from "../../src/server/config/env.js";
+import config from "../../server/config/env.js";
 
 /**
  * Insert groups of antibodies into the MongoDB database.
diff --git a/scripts/streams/ParseFastaStream.js b/src/scripts/streams/ParseFastaStream.js
similarity index 100%
rename from scripts/streams/ParseFastaStream.js
rename to src/scripts/streams/ParseFastaStream.js
diff --git a/scripts/streams/SanitizeFastaStream.js b/src/scripts/streams/SanitizeFastaStream.js
similarity index 100%
rename from scripts/streams/SanitizeFastaStream.js
rename to src/scripts/streams/SanitizeFastaStream.js
diff --git a/scripts/streams/SplitFastaStream.js b/src/scripts/streams/SplitFastaStream.js
similarity index 100%
rename from scripts/streams/SplitFastaStream.js
rename to src/scripts/streams/SplitFastaStream.js
diff --git a/src/scripts/streams/StatisticsStream.js b/src/scripts/streams/StatisticsStream.js
new file mode 100644
index 0000000000000000000000000000000000000000..1a68970db2470509a79d804b33f19f7d6e7dcda0
--- /dev/null
+++ b/src/scripts/streams/StatisticsStream.js
@@ -0,0 +1,75 @@
+import { Writable } from 'node:stream'
+import { Antibody } from '../../server/models/Antibody.js'
+
+// =========================================================================
+
+/**
+ * Build the statistics by incrementing a given stats object.
+ */
+export class StatisticsStream extends Writable {
+  constructor(options) {
+    if (!options) options = {};
+    options.objectMode = true;
+    super(options);
+
+    this.stats = options.stats
+  }
+
+  _write(antibody, encoding, done) {
+    const species = antibody.species;
+    const sources = Antibody.listSources(antibody);
+    const segments = Antibody.listSegments(antibody);
+
+    // Total antibodies
+    this.stats.antibodies += 1;
+
+    // Total species
+    this.stats.species.add(antibody.species);
+
+    // Total sources
+    this.stats.sources = this.stats.sources.union(sources);
+
+    // Antibodies per species
+    this.stats.antibodiesPerSpecies[species] = this.stats.antibodiesPerSpecies[species]
+      ? this.stats.antibodiesPerSpecies[species] + 1
+      : 1;
+
+    // Antibodies per source
+    sources.forEach(source => {
+      this.stats.antibodiesPerSource[source] = this.stats.antibodiesPerSource[source]
+        ? this.stats.antibodiesPerSource[source] + 1
+        : 1;
+    });
+
+    // Antibodies per heavy segment
+    segments.heavyChain.forEach(segment => {
+      this.stats.antibodiesPerHeavySegment[segment] = this.stats.antibodiesPerHeavySegment[segment]
+        ? this.stats.antibodiesPerHeavySegment[segment] + 1
+        : 1;
+    });
+
+    // Antibodies per light segment
+    segments.lightChain.forEach(segment => {
+      this.stats.antibodiesPerLightSegment[segment] = this.stats.antibodiesPerLightSegment[segment]
+        ? this.stats.antibodiesPerLightSegment[segment] + 1
+        : 1;
+    });
+
+    // Antibodies only in one source
+    if (sources.size === 1) {
+      const source = Array.from(sources)[0];
+      this.stats.antibodiesOnlyInSource[source] = this.stats.antibodiesOnlyInSource[source]
+        ? this.stats.antibodiesOnlyInSource[source] + 1
+        : 1;
+    }
+
+    // Antibodies in multiple sources
+    if (sources.size >= 1) {
+      this.stats.antibodiesInMultipleSources[sources.size] = this.stats.antibodiesInMultipleSources[sources.size]
+        ? this.stats.antibodiesInMultipleSources[sources.size] + 1
+        : 1;
+    }
+
+    return done();
+  }
+}
diff --git a/src/scripts/streams/ValidateFastaStream.js b/src/scripts/streams/ValidateFastaStream.js
new file mode 100644
index 0000000000000000000000000000000000000000..246364da6dc5e4c1be6443ccd73e9509d48c83bb
--- /dev/null
+++ b/src/scripts/streams/ValidateFastaStream.js
@@ -0,0 +1,50 @@
+import { createHash } from 'crypto';
+import { Writable } from 'stream';
+
+/**
+ * Stream for validating fasta entries.
+ */
+export class ValidateFastaStream extends Writable {
+  static fileNameFormat = /^[A-Z][a-z]+_[a-z]+\.fasta$/
+  static idFormat = /^[^\s^]+$/
+  static subHeaderFormat = /.+;[^\s]+;[^\s]+;[^\s]+$/
+  static sequenceFormat = /^[ABCDEFGHIKLMNPQRSTUVWXYZ]+$/
+
+  constructor(options) {
+    if (!options) options = {};
+    options.objectMode = true;
+    super(options);
+
+    this.sequenceHashes = new Set();
+  }
+
+  _write(fastaObject, encoding, done) {
+    const [id, ...headers] = fastaObject.header.split('|||');
+    const sequence = fastaObject.sequence;
+
+    if (!ValidateFastaStream.idFormat.test(id)) {
+      return done(new Error(`\x1B[1;31mError with header: ${header} \nID must not contain any space.\x1B[0m`));
+    }
+
+    for (const subheader of headers) {
+      if (!ValidateFastaStream.subHeaderFormat.test(subheader)) {
+        return done(new Error(`\x1B[1;31mError with subheader: ${subheader} \nThe subheaders must have an id, V gene segment and source at their end, separated by semicolons: header;id;vGeneSegment;source. ID, V gene segment and source must not contain any space.\x1B[0m`));
+      }
+    }
+
+    if (!ValidateFastaStream.sequenceFormat.test(sequence)) {
+      return done(new Error(`\x1B[1;31mError with sequence: ${sequence} \nSequences must only contain characters among "ABCDEFGHIKLMNPQRSTUVWXYZ".\x1B[0m`));
+    }
+
+    const hash = createHash('sha256').update(sequence).digest('hex');
+
+    // Check the uniqueness of each sequence trough the whole fasta file
+    if (this.sequenceHashes.has(hash)) {
+      return done(new Error(`\x1B[1;31mDuplication error with sequence: ${sequence} \nThis one already exist in the file.`));
+    } else {
+      this.sequenceHashes.add(hash);
+    }
+
+    return done();
+  }
+}
diff --git a/src/server/app.js b/src/server/app.js
index 9891fbbb8af75b16ca42aea91758beafcf6e2383..a635b5ab67400fc56d785b0c4643c49d5cbc0056 100644
--- a/src/server/app.js
+++ b/src/server/app.js
@@ -20,7 +20,7 @@ import { fileURLToPath } from 'node:url'
 import fastifyMongo from '@fastify/mongodb'
 import fastifyStatic from '@fastify/static'
 import Fastify from 'fastify'
-import buildDatabase from "../../scripts/databaseBuilder.js"
+import buildDatabase from "../../src/scripts/databaseBuilder.js"
 import envConfig from './config/env.js'
 import { ArchiveJobs } from './models/ArchiveJobs.js'
 import antibodiesCountRoute from './routes/antibodiesCountRoute.js'
@@ -37,6 +37,7 @@ import downloadsRoute from './routes/downloadsRoute.js'
 import healthCheckRoute from './routes/healthCheckRoute.js'
 import readyCheckRoute from './routes/readyCheckRoute.js'
 import statisticsRoute from './routes/statisticsRoute.js'
+import databaseIsSync from './utils/databaseIsSync.js'
 
 /**
  * Setup the fastify instance with custom
@@ -164,7 +165,9 @@ fastify.listen({
   host: envConfig.ABSD_SERVER_HOST,
   port: envConfig.ABSD_SERVER_PORT
 }).then(async () => {
-  if (envConfig.NODE_ENV === 'production') {
+  const dbShouldBeRebuilt = !await databaseIsSync(fastify)
+
+  if (envConfig.NODE_ENV === 'production' && dbShouldBeRebuilt) {
     return await buildDatabase()
   }
 }).then((dbResults) => {
diff --git a/src/server/models/Antibody.js b/src/server/models/Antibody.js
new file mode 100644
index 0000000000000000000000000000000000000000..8c8e67b869f63ed729d58197335f813ca7f9e581
--- /dev/null
+++ b/src/server/models/Antibody.js
@@ -0,0 +1,90 @@
+// ABSD
+// Copyright (C) 2023 Institut Pasteur
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import { createHash } from "node:crypto";
+
+/**
+ * The main Antibody model.
+ * Purely static class to factorize the Antibody related functions.
+ */
+export class Antibody {
+  /**
+   * Make a unique list of the sources for an antibody.
+   * @param {object} antibody - The given antibody
+   * @return {Set<string>} The list of sources present in this antibody
+   */
+  static listSources(antibody) {
+    const sources = new Set()
+    antibody.heavyChain.headers.forEach(header => { sources.add(header.source) })
+    antibody.lightChain.headers.forEach(header => { sources.add(header.source) })
+    return sources
+  }
+
+  /**
+   * Make a unique list of the v gene segments for an antibody.
+   * @param {object} antibody - The given antibody
+   * @return {object} An object with the two lists of heavy and light chain segments.
+   */
+  static listSegments(antibody) {
+    const segments = {
+      heavyChain: new Set(),
+      lightChain: new Set()
+    }
+    antibody.heavyChain.headers.forEach(header => { segments.heavyChain.add(header.vGeneSegment) })
+    antibody.lightChain.headers.forEach(header => { segments.lightChain.add(header.vGeneSegment) })
+    return segments
+  }
+
+  /**
+   * Generate a sha256 hash for a given antibody,
+   * using the species, light and heavy sequences.
+   * @param {object} antibody - The given antibody
+   * @returns {string} The sha256 hash
+   */
+  static generateHashId(antibody) {
+    const hashInput = antibody.species +
+      antibody.heavyChain.sequence +
+      antibody.lightChain.sequence;
+
+    return createHash('sha256')
+      .update(hashInput, 'ascii')
+      .digest('hex');
+  }
+
+  /**
+   * Turn an antibody object into a FASTA entry,
+   * using the rawFasta property.
+   * @param {object} antibody - The given antibody
+   * @returns {string} The antibody as a FASTA data string
+   */
+  static toFasta(antibody) {
+    return [
+      `>${antibody.lightChain.rawFasta}`,
+      antibody.lightChain.sequence,
+      `>${antibody.heavyChain.rawFasta}`,
+      antibody.heavyChain.sequence
+    ].join('\n')
+  }
+
+  /**
+   * Turn an antibody object into string.
+   * @param {object} antibody - The given antibody
+   * @returns {string} the stringified antibody
+   */
+  static toString(antibody) {
+    return JSON.stringify(antibody)
+  }
+}
diff --git a/src/server/models/AntibodyFastaStream.js b/src/server/models/AntibodyFastaStream.js
index bc68171921d1402e4786a90e5a16710dd8f88784..2e1f61a707868267b91df05e2f84252a6cb289b7 100644
--- a/src/server/models/AntibodyFastaStream.js
+++ b/src/server/models/AntibodyFastaStream.js
@@ -15,7 +15,7 @@
 // along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 import { Transform } from "node:stream"
-import antibodyToFasta from "../utils/antibodyToFasta.js"
+import { Antibody } from "./Antibody.js"
 
 /**
  * Transform stream for turning antibodies entries from the database
@@ -31,10 +31,10 @@ export default class AntibodyFastaStream extends Transform {
     super(options)
   }
 
-  // Use the antibodyToFasta function to simply turn an entry into
+  // Use the toFasta function to simply turn an entry into
   // a FASTA string.
   _transform(antibody, encoding, done) {
-    this.push(`${antibodyToFasta(antibody)}\n`)
+    this.push(`${Antibody.toFasta(antibody)}\n`)
     return done()
   }
 }
diff --git a/src/server/models/AntibodyJsonStream.js b/src/server/models/AntibodyJsonStream.js
index 424b579b0a44158270ea77034bb2efa3a8f7b164..72f55865b26dd744dc1c8e31c451c2f8df38eef7 100644
--- a/src/server/models/AntibodyJsonStream.js
+++ b/src/server/models/AntibodyJsonStream.js
@@ -15,6 +15,7 @@
 // along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 import { Transform } from "node:stream"
+import { Antibody } from "./Antibody.js"
 
 /**
  * Transform stream for turning antibodies entries from the database
@@ -51,12 +52,12 @@ export default class AntibodyJsonStream extends Transform {
   // will be the last one.
   _transform(antibody, encoding, done) {
     if (this.firstEntry) {
-      this.push(JSON.stringify(antibody))
+      this.push(Antibody.toString(antibody))
       this.firstEntry = false
       return done()
     }
 
-    this.push(`,\n${JSON.stringify(antibody)}`)
+    this.push(`,\n${Antibody.toString(antibody)}`)
     return done()
   }
 
diff --git a/src/server/utils/antibodyToFasta.js b/src/server/models/Statistics.js
similarity index 54%
rename from src/server/utils/antibodyToFasta.js
rename to src/server/models/Statistics.js
index 56812e21d1e49e32e0d29b88ff0a26d04c228ff6..d38b5122266020350fe70af4b80ce3b7699c3376 100644
--- a/src/server/utils/antibodyToFasta.js
+++ b/src/server/models/Statistics.js
@@ -15,16 +15,27 @@
 // along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Turn an antibody object into a FASTA entry,
- * using the rawFasta property.
- * @param {object} antibody - The antibody from the database
- * @returns {string} The antibody as a FASTA data string
+ * Statistics about the antibodies.
  */
-export default function antibodyToFasta(antibody) {
-  return [
-    `>${antibody.lightChain.rawFasta}`,
-    antibody.lightChain.sequence,
-    `>${antibody.heavyChain.rawFasta}`,
-    antibody.heavyChain.sequence
-  ].join('\n')
+export class Statistics {
+  antibodies = 0
+  species = new Set()
+  sources = new Set()
+  antibodiesPerSpecies = {}
+  antibodiesPerSource = {}
+  antibodiesPerHeavySegment = {}
+  antibodiesPerLightSegment = {}
+  antibodiesOnlyInSource = {}
+  antibodiesInMultipleSources = {}
+
+  /**
+   * Stringify the stats object with a replacer function
+   * to handle the sets.
+   * @returns {string} The stringified stats object
+   */
+  toString() {
+    return JSON.stringify(this, (key, value) => {
+      return value instanceof Set ? [...value] : value
+    }, 2)
+  }
 }
diff --git a/src/server/routes/antibodiesDownloadRoute.js b/src/server/routes/antibodiesDownloadRoute.js
index 8fbf2587337ba9ff1e92bba17376b003c57631d3..5b2b933695b02b4acbc0b931987faa593b2842dc 100644
--- a/src/server/routes/antibodiesDownloadRoute.js
+++ b/src/server/routes/antibodiesDownloadRoute.js
@@ -58,12 +58,15 @@ export default {
   ],
   handler: function (request, reply) {
     const archiveId = `absd-${randomUUID()}`
+    const requestFields = request.query.format === 'fasta'
+      ? fieldsProjection.fasta
+      : fieldsProjection.json
 
     this.archiveJobs.add(new ArchiveBuilder({
       archiveId,
       archiveFormat: request.query.format,
       requestWhere: request.where,
-      requestFields: fieldsProjection
+      requestFields
     }))
 
     reply.code(202).send({
diff --git a/src/server/routes/readyCheckRoute.js b/src/server/routes/readyCheckRoute.js
index 5f45c42b12621bd4f0a706b8a526cfe5a54d9663..2ec8df6c46b393ef58917fe78b819191a7955d28 100644
--- a/src/server/routes/readyCheckRoute.js
+++ b/src/server/routes/readyCheckRoute.js
@@ -22,6 +22,7 @@
 export default {
   method: 'GET',
   url: '/ready',
+  logLevel: 'warn',
   handler: function (request, reply) {
     if (this.serverIsReady) {
       return reply
diff --git a/src/server/routes/statisticsRoute.js b/src/server/routes/statisticsRoute.js
index b175351cebf056b8e9b9bcfdb8db1554b1d7d215..eb033bcdf0944c0bd496d6f75df5e54b1989d0a1 100644
--- a/src/server/routes/statisticsRoute.js
+++ b/src/server/routes/statisticsRoute.js
@@ -14,15 +14,6 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <http://www.gnu.org/licenses/>.
 
-import { readFileSync, readdirSync } from 'node:fs'
-import { dirname, resolve } from 'node:path'
-import { fileURLToPath } from 'node:url'
-
-/**
- * The absolute path of this file.
- */
-const currentPath = dirname(fileURLToPath(import.meta.url))
-
 /**
  * Route for getting the statistics data.
  */
@@ -30,23 +21,16 @@ export default {
   method: 'GET',
   url: '/api/statistics',
   handler: function (request, reply) {
-    const dirPath = resolve(currentPath, '../../../data')
-    const statisticsList = readdirSync(dirPath)
-      .filter((file) => {
-        return file.endsWith('-stats.json')
+    this.mongo.db
+      .collection('statistics')
+      .findOne()
+      .then(stats => {
+        return reply
+          .code(200)
+          .header('content-type', 'application/json')
+          .send(stats)
+      }).catch(err => {
+        reply.code(500).send(err)
       })
-      .sort()
-      .reverse()
-      .map(file => {
-        return resolve(dirPath, file)
-      })
-      .map(path => {
-        return readFileSync(path, 'utf-8')
-      })
-      .map(fileContent => {
-        return JSON.parse(fileContent)
-      })
-
-    reply.send(statisticsList[0])
   }
 }
diff --git a/src/server/schemas/Statistics.json b/src/server/schemas/Statistics.json
index d9e220a9965640df974b3be65fc30da4ce21b1a0..2c53ea243ee4b4055ce81365dde750859c0d1873 100644
--- a/src/server/schemas/Statistics.json
+++ b/src/server/schemas/Statistics.json
@@ -6,17 +6,29 @@
       "_id": {
         "bsonType": "objectId"
       },
+      "versionDate": {
+        "description": "Date of this database version",
+        "bsonType": "date"
+      },
       "antibodies": {
         "description": "Total number of antibodies.",
         "bsonType": "int"
       },
       "species": {
-        "description": "Total number of species.",
-        "bsonType": "int"
+        "description": "List of species.",
+        "bsonType": "array",
+        "items": {
+          "bsonType": "string",
+          "pattern": "^[A-Z][a-z]+\\s[a-z]+$"
+        }
       },
       "sources": {
-        "description": "Total number of sources.",
-        "bsonType": "int"
+        "description": "List of sources.",
+        "bsonType": "array",
+        "items": {
+          "bsonType": "string",
+          "pattern": "^.+$"
+        }
       },
       "antibodiesPerSpecies": {
         "description": "Total number of antibodies per species. Keys must follow the species name pattern.",
@@ -36,6 +48,24 @@
           }
         }
       },
+      "antibodiesPerHeavySegment": {
+        "description": "Total number of antibodies per heavy segment.",
+        "bsonType": "object",
+        "patternProperties": {
+          "^IGHV[0-9]+$": {
+            "bsonType": "int"
+          }
+        }
+      },
+      "antibodiesPerLightSegment": {
+        "description": "Total number of antibodies per light segment.",
+        "bsonType": "object",
+        "patternProperties": {
+          "^IG[LK]V[0-9]+$": {
+            "bsonType": "int"
+          }
+        }
+      },
       "antibodiesOnlyInSource": {
         "description": "Total number of antibodies that are unique for a given source and thus not repeated elsewhere.",
         "bsonType": "object",
diff --git a/src/server/utils/archiveBuilderWorker.js b/src/server/utils/archiveBuilderWorker.js
index 68fed5cd0357dadbbb8e72c228f94c4b76627417..a13de23a6abbe5868686b45c24d69ce2a5994931 100644
--- a/src/server/utils/archiveBuilderWorker.js
+++ b/src/server/utils/archiveBuilderWorker.js
@@ -1,3 +1,19 @@
+// ABSD
+// Copyright (C) 2023 Institut Pasteur
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
 import { createReadStream, createWriteStream } from "node:fs"
 import { rm } from "node:fs/promises"
 import { tmpdir } from "node:os"
@@ -17,22 +33,17 @@ const dataFormat = workerData.archiveFormat
 const tmpFileExt = dataFormat === 'fasta' ? '.fasta' : '.json'
 const tmpFileName = `${workerData.archiveId}${tmpFileExt}`
 const tmpFilePath = join(tmpDir, tmpFileName)
-
 const archiveName = `${tmpFileName}.gz`
 const archivePath = join(tmpDir, archiveName)
 
-// TO DO: this should not be handled there but in the route,
-// before the ArchiveBuilder intialisation
-const projection = dataFormat === 'fasta'
-  ? workerData.requestFields.fasta
-  : workerData.requestFields.json
-
 const mongoURI = `mongodb://${envConfig.ABSD_DB_HOST}:${envConfig.ABSD_DB_PORT}/${envConfig.ABSD_DB_NAME}`
 const database = await MongoClient.connect(mongoURI)
 const dataStream = database
   .db()
   .collection('antibodies')
-  .find(workerData.requestWhere, { projection })
+  .find(workerData.requestWhere, {
+    projection: workerData.requestFields
+  })
   .stream()
 
 const transformStream = dataFormat === 'fasta'
diff --git a/src/server/utils/databaseIsSync.js b/src/server/utils/databaseIsSync.js
new file mode 100644
index 0000000000000000000000000000000000000000..fbeaacf1f0ea23d2ea6f18ca859642ec989ae9ff
--- /dev/null
+++ b/src/server/utils/databaseIsSync.js
@@ -0,0 +1,52 @@
+// ABSD
+// Copyright (C) 2023 Institut Pasteur
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import { readdir } from 'node:fs/promises'
+import { dirname, resolve } from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+/**
+ * Check if the database is already there and synchronized with the
+ * last version of the data by checking the stats version date.
+ * Used mainly to check if the database should be rebuilt or not.
+ * @param {object} fastify - The Fastify server object
+ * @returns {boolean} If the database is sync or not
+ */
+export default async function databaseIsSync(fastify) {
+  // Get the stats of the current database
+  const dbStats = await fastify.mongo.db
+    .collection('statistics')
+    .findOne()
+
+  // Stop here if the database does not exist
+  if (!dbStats?.versionDate) return false
+
+  // Then fetch the stats of the last stats file for comparison
+  const currentPath = dirname(fileURLToPath(import.meta.url))
+  const dataFolderPath = resolve(currentPath, '../../../data')
+  const dataFolder = await readdir(dataFolderPath)
+
+  const statsFilePath = dataFolder
+    .filter((file) => { return file.endsWith('-stats.json') })
+    .sort()
+    .reverse()
+    .map(file => { return resolve(dataFolderPath, file) })
+    .shift()
+
+  const lastStatsDate = new Date(statsFilePath.match(/(\d{4}-\d{2}-\d{2})-stats/)[1])
+
+  return dbStats.versionDate.getTime() === lastStatsDate.getTime()
+}