cyberia 2.8.885

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (525) hide show
  1. package/.dockerignore +15 -0
  2. package/.env.development +45 -0
  3. package/.env.production +50 -0
  4. package/.env.test +45 -0
  5. package/.github/workflows/engine-cyberia.cd.yml +31 -0
  6. package/.github/workflows/engine-cyberia.ci.yml +60 -0
  7. package/.github/workflows/ghpkg.ci.yml +87 -0
  8. package/.github/workflows/npmpkg.ci.yml +72 -0
  9. package/.github/workflows/publish.ci.yml +84 -0
  10. package/.github/workflows/publish.cyberia.ci.yml +84 -0
  11. package/.github/workflows/pwa-microservices-template-page.cd.yml +72 -0
  12. package/.github/workflows/pwa-microservices-template-test.ci.yml +33 -0
  13. package/.github/workflows/release.cd.yml +37 -0
  14. package/.nycrc +9 -0
  15. package/.prettierignore +13 -0
  16. package/.prettierrc +9 -0
  17. package/.vscode/extensions.json +51 -0
  18. package/.vscode/settings.json +87 -0
  19. package/AUTHORS.md +21 -0
  20. package/CHANGELOG.md +205 -0
  21. package/Dockerfile +28 -0
  22. package/LICENSE +21 -0
  23. package/README.md +85 -0
  24. package/bin/build.js +209 -0
  25. package/bin/cron.js +47 -0
  26. package/bin/cyberia.js +145 -0
  27. package/bin/db.js +199 -0
  28. package/bin/deploy.js +1293 -0
  29. package/bin/file.js +197 -0
  30. package/bin/hwt.js +49 -0
  31. package/bin/index.js +145 -0
  32. package/bin/ssl.js +63 -0
  33. package/bin/util.js +80 -0
  34. package/bin/vs.js +74 -0
  35. package/cli.md +714 -0
  36. package/conf.js +204 -0
  37. package/deployment.yaml +138 -0
  38. package/jsconfig.json +7 -0
  39. package/jsdoc.json +32 -0
  40. package/manifests/deployment/adminer/deployment.yaml +32 -0
  41. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  42. package/manifests/deployment/adminer/service.yaml +13 -0
  43. package/manifests/deployment/dd-default-development/deployment.yaml +167 -0
  44. package/manifests/deployment/dd-default-development/proxy.yaml +46 -0
  45. package/manifests/deployment/dd-test-development/deployment.yaml +174 -0
  46. package/manifests/deployment/dd-test-development/proxy.yaml +51 -0
  47. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  48. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  49. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  50. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  51. package/manifests/deployment/fastapi/initial_data.sh +56 -0
  52. package/manifests/deployment/kafka/deployment.yaml +69 -0
  53. package/manifests/deployment/mongo-express/deployment.yaml +60 -0
  54. package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
  55. package/manifests/deployment/spark/spark-pi-py.yaml +21 -0
  56. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +65 -0
  57. package/manifests/envoy-service-nodeport.yaml +23 -0
  58. package/manifests/grafana/deployment.yaml +57 -0
  59. package/manifests/grafana/kustomization.yaml +7 -0
  60. package/manifests/grafana/pvc.yaml +12 -0
  61. package/manifests/grafana/service.yaml +14 -0
  62. package/manifests/kind-config-dev.yaml +12 -0
  63. package/manifests/kind-config.yaml +12 -0
  64. package/manifests/kubeadm-calico-config.yaml +119 -0
  65. package/manifests/kubelet-config.yaml +65 -0
  66. package/manifests/letsencrypt-prod.yaml +15 -0
  67. package/manifests/lxd/lxd-admin-profile.yaml +17 -0
  68. package/manifests/lxd/lxd-preseed.yaml +30 -0
  69. package/manifests/lxd/underpost-setup.sh +163 -0
  70. package/manifests/mariadb/config.yaml +10 -0
  71. package/manifests/mariadb/kustomization.yaml +9 -0
  72. package/manifests/mariadb/pv.yaml +12 -0
  73. package/manifests/mariadb/pvc.yaml +10 -0
  74. package/manifests/mariadb/secret.yaml +8 -0
  75. package/manifests/mariadb/service.yaml +10 -0
  76. package/manifests/mariadb/statefulset.yaml +56 -0
  77. package/manifests/mariadb/storage-class.yaml +10 -0
  78. package/manifests/mongodb/backup-access.yaml +16 -0
  79. package/manifests/mongodb/backup-cronjob.yaml +42 -0
  80. package/manifests/mongodb/backup-pv-pvc.yaml +22 -0
  81. package/manifests/mongodb/configmap.yaml +26 -0
  82. package/manifests/mongodb/headless-service.yaml +10 -0
  83. package/manifests/mongodb/kustomization.yaml +11 -0
  84. package/manifests/mongodb/pv-pvc.yaml +23 -0
  85. package/manifests/mongodb/statefulset.yaml +126 -0
  86. package/manifests/mongodb/storage-class.yaml +9 -0
  87. package/manifests/mongodb-4.4/kustomization.yaml +7 -0
  88. package/manifests/mongodb-4.4/pv-pvc.yaml +23 -0
  89. package/manifests/mongodb-4.4/service-deployment.yaml +63 -0
  90. package/manifests/mysql/kustomization.yaml +7 -0
  91. package/manifests/mysql/pv-pvc.yaml +27 -0
  92. package/manifests/mysql/statefulset.yaml +55 -0
  93. package/manifests/postgresql/configmap.yaml +9 -0
  94. package/manifests/postgresql/kustomization.yaml +10 -0
  95. package/manifests/postgresql/pv.yaml +15 -0
  96. package/manifests/postgresql/pvc.yaml +13 -0
  97. package/manifests/postgresql/service.yaml +10 -0
  98. package/manifests/postgresql/statefulset.yaml +37 -0
  99. package/manifests/prometheus/deployment.yaml +82 -0
  100. package/manifests/valkey/kustomization.yaml +7 -0
  101. package/manifests/valkey/service.yaml +11 -0
  102. package/manifests/valkey/statefulset.yaml +38 -0
  103. package/nodemon.json +6 -0
  104. package/package.json +118 -0
  105. package/proxy.yaml +35 -0
  106. package/scripts/device-scan.sh +43 -0
  107. package/scripts/gpu-diag.sh +19 -0
  108. package/scripts/maas-setup.sh +120 -0
  109. package/scripts/nat-iptables.sh +26 -0
  110. package/scripts/nvim.sh +91 -0
  111. package/scripts/snap-clean.sh +26 -0
  112. package/scripts/ssh-cluster-info.sh +14 -0
  113. package/scripts/ssl.sh +164 -0
  114. package/src/api/blockchain/blockchain.controller.js +51 -0
  115. package/src/api/blockchain/blockchain.model.js +90 -0
  116. package/src/api/blockchain/blockchain.router.js +21 -0
  117. package/src/api/blockchain/blockchain.service.js +24 -0
  118. package/src/api/core/core.controller.js +69 -0
  119. package/src/api/core/core.model.js +11 -0
  120. package/src/api/core/core.router.js +24 -0
  121. package/src/api/core/core.service.js +35 -0
  122. package/src/api/crypto/crypto.controller.js +51 -0
  123. package/src/api/crypto/crypto.model.js +23 -0
  124. package/src/api/crypto/crypto.router.js +20 -0
  125. package/src/api/crypto/crypto.service.js +64 -0
  126. package/src/api/default/default.controller.js +74 -0
  127. package/src/api/default/default.model.js +20 -0
  128. package/src/api/default/default.router.js +27 -0
  129. package/src/api/default/default.service.js +40 -0
  130. package/src/api/document/document.controller.js +66 -0
  131. package/src/api/document/document.model.js +51 -0
  132. package/src/api/document/document.router.js +24 -0
  133. package/src/api/document/document.service.js +133 -0
  134. package/src/api/file/file.controller.js +67 -0
  135. package/src/api/file/file.model.js +19 -0
  136. package/src/api/file/file.router.js +22 -0
  137. package/src/api/file/file.service.js +100 -0
  138. package/src/api/instance/instance.controller.js +69 -0
  139. package/src/api/instance/instance.model.js +40 -0
  140. package/src/api/instance/instance.router.js +34 -0
  141. package/src/api/instance/instance.service.js +70 -0
  142. package/src/api/ipfs/ipfs.controller.js +51 -0
  143. package/src/api/ipfs/ipfs.model.js +17 -0
  144. package/src/api/ipfs/ipfs.router.js +20 -0
  145. package/src/api/ipfs/ipfs.service.js +25 -0
  146. package/src/api/object-layer/README.md +85 -0
  147. package/src/api/object-layer/object-layer.controller.js +69 -0
  148. package/src/api/object-layer/object-layer.model.js +181 -0
  149. package/src/api/object-layer/object-layer.router.js +29 -0
  150. package/src/api/object-layer/object-layer.service.js +49 -0
  151. package/src/api/test/test.controller.js +59 -0
  152. package/src/api/test/test.model.js +14 -0
  153. package/src/api/test/test.router.js +21 -0
  154. package/src/api/test/test.service.js +35 -0
  155. package/src/api/user/postman_collection.json +216 -0
  156. package/src/api/user/user.build.js +16 -0
  157. package/src/api/user/user.controller.js +35 -0
  158. package/src/api/user/user.model.js +100 -0
  159. package/src/api/user/user.router.js +400 -0
  160. package/src/api/user/user.service.js +500 -0
  161. package/src/api.js +23 -0
  162. package/src/cli/baremetal.js +1310 -0
  163. package/src/cli/cloud-init.js +548 -0
  164. package/src/cli/cluster.js +834 -0
  165. package/src/cli/cron.js +95 -0
  166. package/src/cli/db.js +414 -0
  167. package/src/cli/deploy.js +661 -0
  168. package/src/cli/env.js +101 -0
  169. package/src/cli/fs.js +256 -0
  170. package/src/cli/image.js +156 -0
  171. package/src/cli/index.js +436 -0
  172. package/src/cli/lxd.js +402 -0
  173. package/src/cli/monitor.js +260 -0
  174. package/src/cli/repository.js +274 -0
  175. package/src/cli/run.js +728 -0
  176. package/src/cli/script.js +85 -0
  177. package/src/cli/secrets.js +71 -0
  178. package/src/cli/ssh.js +46 -0
  179. package/src/cli/test.js +159 -0
  180. package/src/client/Cyberia.index.js +50 -0
  181. package/src/client/CyberiaAdmin.index.js +34 -0
  182. package/src/client/CyberiaPortal.index.js +36 -0
  183. package/src/client/Default.index.js +84 -0
  184. package/src/client/components/core/404.js +20 -0
  185. package/src/client/components/core/500.js +20 -0
  186. package/src/client/components/core/Account.js +326 -0
  187. package/src/client/components/core/AgGrid.js +191 -0
  188. package/src/client/components/core/Alert.js +77 -0
  189. package/src/client/components/core/Auth.js +342 -0
  190. package/src/client/components/core/Badge.js +32 -0
  191. package/src/client/components/core/Blockchain.js +41 -0
  192. package/src/client/components/core/Blog.js +9 -0
  193. package/src/client/components/core/BtnIcon.js +111 -0
  194. package/src/client/components/core/CalendarCore.js +464 -0
  195. package/src/client/components/core/Chat.js +64 -0
  196. package/src/client/components/core/ColorPalette.js +5267 -0
  197. package/src/client/components/core/CommonJs.js +1010 -0
  198. package/src/client/components/core/Content.js +196 -0
  199. package/src/client/components/core/Css.js +1099 -0
  200. package/src/client/components/core/CssCore.js +882 -0
  201. package/src/client/components/core/D3Chart.js +44 -0
  202. package/src/client/components/core/Docs.js +376 -0
  203. package/src/client/components/core/DropDown.js +223 -0
  204. package/src/client/components/core/EventsUI.js +133 -0
  205. package/src/client/components/core/FileExplorer.js +707 -0
  206. package/src/client/components/core/FullScreen.js +36 -0
  207. package/src/client/components/core/Input.js +383 -0
  208. package/src/client/components/core/JoyStick.js +80 -0
  209. package/src/client/components/core/Keyboard.js +73 -0
  210. package/src/client/components/core/LoadingAnimation.js +159 -0
  211. package/src/client/components/core/LogIn.js +190 -0
  212. package/src/client/components/core/LogOut.js +63 -0
  213. package/src/client/components/core/Logger.js +29 -0
  214. package/src/client/components/core/Modal.js +2494 -0
  215. package/src/client/components/core/NotificationManager.js +84 -0
  216. package/src/client/components/core/ObjectLayerEngine.js +1229 -0
  217. package/src/client/components/core/ObjectLayerEngineModal.js +443 -0
  218. package/src/client/components/core/Pagination.js +207 -0
  219. package/src/client/components/core/Panel.js +772 -0
  220. package/src/client/components/core/PanelForm.js +627 -0
  221. package/src/client/components/core/Polyhedron.js +162 -0
  222. package/src/client/components/core/Recover.js +207 -0
  223. package/src/client/components/core/Responsive.js +82 -0
  224. package/src/client/components/core/RichText.js +43 -0
  225. package/src/client/components/core/Router.js +317 -0
  226. package/src/client/components/core/Scroll.js +76 -0
  227. package/src/client/components/core/SignUp.js +125 -0
  228. package/src/client/components/core/SocketIo.js +74 -0
  229. package/src/client/components/core/Stream.js +113 -0
  230. package/src/client/components/core/ToggleSwitch.js +101 -0
  231. package/src/client/components/core/ToolTip.js +90 -0
  232. package/src/client/components/core/Translate.js +522 -0
  233. package/src/client/components/core/Validator.js +115 -0
  234. package/src/client/components/core/VanillaJs.js +423 -0
  235. package/src/client/components/core/Wallet.js +106 -0
  236. package/src/client/components/core/WebComponent.js +44 -0
  237. package/src/client/components/core/Webhook.js +25 -0
  238. package/src/client/components/core/Worker.js +371 -0
  239. package/src/client/components/core/windowGetDimensions.js +269 -0
  240. package/src/client/components/cyberia/BagCyberia.js +1253 -0
  241. package/src/client/components/cyberia/BiomeCyberia.js +130 -0
  242. package/src/client/components/cyberia/CharacterCyberia.js +321 -0
  243. package/src/client/components/cyberia/CommonCyberia.js +1834 -0
  244. package/src/client/components/cyberia/CssCyberia.js +816 -0
  245. package/src/client/components/cyberia/ElementPreviewCyberia.js +183 -0
  246. package/src/client/components/cyberia/ElementsCyberia.js +146 -0
  247. package/src/client/components/cyberia/InteractionPanelCyberia.js +1043 -0
  248. package/src/client/components/cyberia/JoyStickCyberia.js +53 -0
  249. package/src/client/components/cyberia/LogInCyberia.js +68 -0
  250. package/src/client/components/cyberia/LogOutCyberia.js +24 -0
  251. package/src/client/components/cyberia/MainUserCyberia.js +424 -0
  252. package/src/client/components/cyberia/MapCyberia.js +160 -0
  253. package/src/client/components/cyberia/MatrixCyberia.js +147 -0
  254. package/src/client/components/cyberia/MenuCyberia.js +575 -0
  255. package/src/client/components/cyberia/PixiCyberia.js +1639 -0
  256. package/src/client/components/cyberia/PointAndClickMovementCyberia.js +146 -0
  257. package/src/client/components/cyberia/QuestCyberia.js +1420 -0
  258. package/src/client/components/cyberia/RoutesCyberia.js +47 -0
  259. package/src/client/components/cyberia/SettingsCyberia.js +16 -0
  260. package/src/client/components/cyberia/SignUpCyberia.js +14 -0
  261. package/src/client/components/cyberia/SkillCyberia.js +124 -0
  262. package/src/client/components/cyberia/SocketIoCyberia.js +211 -0
  263. package/src/client/components/cyberia/TileCyberia.js +685 -0
  264. package/src/client/components/cyberia/TranslateCyberia.js +96 -0
  265. package/src/client/components/cyberia/UniverseCyberia.js +14 -0
  266. package/src/client/components/cyberia/WebhookCyberia.js +13 -0
  267. package/src/client/components/cyberia/WikiCyberia.js +144 -0
  268. package/src/client/components/cyberia/WorldCyberia.js +680 -0
  269. package/src/client/components/cyberia-admin/BiomeCyberiaAdmin.js +978 -0
  270. package/src/client/components/cyberia-admin/CommonCyberiaAdmin.js +29 -0
  271. package/src/client/components/cyberia-admin/CssCyberiaAdmin.js +15 -0
  272. package/src/client/components/cyberia-admin/ElementsCyberiaAdmin.js +38 -0
  273. package/src/client/components/cyberia-admin/InstanceEngineCyberiaAdmin.js +180 -0
  274. package/src/client/components/cyberia-admin/LogInCyberiaAdmin.js +34 -0
  275. package/src/client/components/cyberia-admin/LogOutCyberiaAdmin.js +24 -0
  276. package/src/client/components/cyberia-admin/MenuCyberiaAdmin.js +660 -0
  277. package/src/client/components/cyberia-admin/RoutesCyberiaAdmin.js +57 -0
  278. package/src/client/components/cyberia-admin/ServerCyberiaAdmin.js +129 -0
  279. package/src/client/components/cyberia-admin/SettingsCyberiaAdmin.js +16 -0
  280. package/src/client/components/cyberia-admin/SignUpCyberiaAdmin.js +11 -0
  281. package/src/client/components/cyberia-admin/SocketIoCyberiaAdmin.js +53 -0
  282. package/src/client/components/cyberia-admin/TranslateCyberiaAdmin.js +7 -0
  283. package/src/client/components/cyberia-biome/CityCyberiaBiome.js +209 -0
  284. package/src/client/components/cyberia-biome/CityInteriorCyberiaBiome.js +253 -0
  285. package/src/client/components/cyberia-biome/ColorChaosCyberiaBiome.js +26 -0
  286. package/src/client/components/cyberia-biome/ForestCyberiaBiome.js +191 -0
  287. package/src/client/components/cyberia-biome/GridBaseCyberiaBiome.js +364 -0
  288. package/src/client/components/cyberia-biome/SeedCityCyberiaBiome.js +347 -0
  289. package/src/client/components/cyberia-biome/ShopCyberiaBiome.js +12 -0
  290. package/src/client/components/cyberia-biome/SpaceCyberiaBiome.js +58 -0
  291. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +29 -0
  292. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +132 -0
  293. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +38 -0
  294. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +18 -0
  295. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +12 -0
  296. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +487 -0
  297. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +45 -0
  298. package/src/client/components/cyberia-portal/ServerCyberiaPortal.js +136 -0
  299. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +16 -0
  300. package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +11 -0
  301. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +52 -0
  302. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +12 -0
  303. package/src/client/components/default/CommonDefault.js +29 -0
  304. package/src/client/components/default/CssDefault.js +27 -0
  305. package/src/client/components/default/ElementsDefault.js +38 -0
  306. package/src/client/components/default/LogInDefault.js +14 -0
  307. package/src/client/components/default/LogOutDefault.js +10 -0
  308. package/src/client/components/default/MenuDefault.js +743 -0
  309. package/src/client/components/default/RoutesDefault.js +48 -0
  310. package/src/client/components/default/SettingsDefault.js +16 -0
  311. package/src/client/components/default/SignUpDefault.js +9 -0
  312. package/src/client/components/default/SocketIoDefault.js +54 -0
  313. package/src/client/components/default/TranslateDefault.js +7 -0
  314. package/src/client/public/default/android-chrome-144x144.png +0 -0
  315. package/src/client/public/default/android-chrome-192x192.png +0 -0
  316. package/src/client/public/default/android-chrome-256x256.png +0 -0
  317. package/src/client/public/default/android-chrome-36x36.png +0 -0
  318. package/src/client/public/default/android-chrome-384x384.png +0 -0
  319. package/src/client/public/default/android-chrome-48x48.png +0 -0
  320. package/src/client/public/default/android-chrome-512x512.png +0 -0
  321. package/src/client/public/default/android-chrome-72x72.png +0 -0
  322. package/src/client/public/default/android-chrome-96x96.png +0 -0
  323. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  324. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  325. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  326. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  327. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  328. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  329. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  330. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  331. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  332. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  333. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  334. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  335. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  336. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  337. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  338. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  339. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  340. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  341. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  342. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  343. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  344. package/src/client/public/default/apple-touch-icon.png +0 -0
  345. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  346. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  347. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  348. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  349. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  350. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  351. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  352. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  353. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  354. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  355. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  356. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  357. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  358. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  359. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  360. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  361. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  362. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  363. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  364. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  365. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  366. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  367. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  368. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  369. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  370. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  371. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  372. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  373. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  374. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  375. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  376. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  377. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  378. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  379. package/src/client/public/default/assets/background/dark.jpg +0 -0
  380. package/src/client/public/default/assets/background/dark.svg +557 -0
  381. package/src/client/public/default/assets/background/white.jpg +0 -0
  382. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  383. package/src/client/public/default/assets/background/white0.jpg +0 -0
  384. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  385. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  386. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  387. package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
  388. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  389. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  390. package/src/client/public/default/browserconfig.xml +12 -0
  391. package/src/client/public/default/favicon-16x16.png +0 -0
  392. package/src/client/public/default/favicon-32x32.png +0 -0
  393. package/src/client/public/default/favicon-48x48.png +0 -0
  394. package/src/client/public/default/favicon.ico +0 -0
  395. package/src/client/public/default/manifest.webmanifest +69 -0
  396. package/src/client/public/default/mstile-144x144.png +0 -0
  397. package/src/client/public/default/mstile-150x150.png +0 -0
  398. package/src/client/public/default/mstile-310x150.png +0 -0
  399. package/src/client/public/default/mstile-310x310.png +0 -0
  400. package/src/client/public/default/mstile-70x70.png +0 -0
  401. package/src/client/public/default/plantuml/client-conf.svg +1 -0
  402. package/src/client/public/default/plantuml/client-schema.svg +1 -0
  403. package/src/client/public/default/plantuml/cron-conf.svg +1 -0
  404. package/src/client/public/default/plantuml/cron-schema.svg +1 -0
  405. package/src/client/public/default/plantuml/server-conf.svg +1 -0
  406. package/src/client/public/default/plantuml/server-schema.svg +1 -0
  407. package/src/client/public/default/plantuml/ssr-conf.svg +1 -0
  408. package/src/client/public/default/plantuml/ssr-schema.svg +1 -0
  409. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  410. package/src/client/public/default/site.webmanifest +69 -0
  411. package/src/client/public/default/sitemap +148 -0
  412. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  413. package/src/client/public/default/yandex-browser-manifest.json +9 -0
  414. package/src/client/public/doc/favicon.ico +0 -0
  415. package/src/client/public/doc/sitemap +148 -0
  416. package/src/client/public/test/favicon.ico +0 -0
  417. package/src/client/public/test/sitemap +148 -0
  418. package/src/client/services/blockchain/blockchain.service.js +73 -0
  419. package/src/client/services/core/core.service.js +165 -0
  420. package/src/client/services/crypto/crypto.service.js +73 -0
  421. package/src/client/services/default/default.management.js +450 -0
  422. package/src/client/services/default/default.service.js +98 -0
  423. package/src/client/services/document/document.service.js +97 -0
  424. package/src/client/services/file/file.service.js +72 -0
  425. package/src/client/services/instance/instance.management.js +78 -0
  426. package/src/client/services/instance/instance.service.js +97 -0
  427. package/src/client/services/ipfs/ipfs.service.js +73 -0
  428. package/src/client/services/object-layer/object-layer.service.js +93 -0
  429. package/src/client/services/test/test.service.js +73 -0
  430. package/src/client/services/user/user.management.js +56 -0
  431. package/src/client/services/user/user.service.js +108 -0
  432. package/src/client/ssr/Render.js +237 -0
  433. package/src/client/ssr/body/404.js +73 -0
  434. package/src/client/ssr/body/500.js +72 -0
  435. package/src/client/ssr/body/CacheControl.js +114 -0
  436. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +90 -0
  437. package/src/client/ssr/body/CyberiaSplashScreenLore.js +424 -0
  438. package/src/client/ssr/body/DefaultSplashScreen.js +90 -0
  439. package/src/client/ssr/email/DefaultRecoverEmail.js +21 -0
  440. package/src/client/ssr/email/DefaultVerifyEmail.js +17 -0
  441. package/src/client/ssr/head/Css.js +241 -0
  442. package/src/client/ssr/head/CyberiaAdminScripts.js +6 -0
  443. package/src/client/ssr/head/CyberiaPortalScripts.js +6 -0
  444. package/src/client/ssr/head/CyberiaScripts.js +6 -0
  445. package/src/client/ssr/head/DefaultScripts.js +6 -0
  446. package/src/client/ssr/head/Microdata.js +11 -0
  447. package/src/client/ssr/head/Production.js +1 -0
  448. package/src/client/ssr/head/Pwa.js +146 -0
  449. package/src/client/ssr/head/Seo.js +15 -0
  450. package/src/client/ssr/mailer/DefaultRecoverEmail.js +21 -0
  451. package/src/client/ssr/mailer/DefaultVerifyEmail.js +17 -0
  452. package/src/client/ssr/offline/Maintenance.js +63 -0
  453. package/src/client/ssr/offline/NoNetworkConnection.js +67 -0
  454. package/src/client/ssr/pages/404.js +12 -0
  455. package/src/client/ssr/pages/500.js +12 -0
  456. package/src/client/ssr/pages/Test.js +198 -0
  457. package/src/client/ssr/pages/maintenance.js +14 -0
  458. package/src/client/ssr/pages/offline.js +21 -0
  459. package/src/client/sw/default.sw.js +108 -0
  460. package/src/client/sw/template.sw.js +84 -0
  461. package/src/client.build.js +22 -0
  462. package/src/client.dev.js +24 -0
  463. package/src/db/DataBaseProvider.js +98 -0
  464. package/src/db/mariadb/MariaDB.js +66 -0
  465. package/src/db/mongo/MongooseDB.js +70 -0
  466. package/src/index.js +198 -0
  467. package/src/mailer/EmailRender.js +116 -0
  468. package/src/mailer/MailerProvider.js +213 -0
  469. package/src/monitor.js +24 -0
  470. package/src/proxy.js +22 -0
  471. package/src/runtime/express/Express.js +256 -0
  472. package/src/runtime/lampp/Dockerfile +50 -0
  473. package/src/runtime/lampp/Lampp.js +343 -0
  474. package/src/server/auth.js +689 -0
  475. package/src/server/backup.js +96 -0
  476. package/src/server/client-build-docs.js +205 -0
  477. package/src/server/client-build-live.js +109 -0
  478. package/src/server/client-build.js +690 -0
  479. package/src/server/client-dev-server.js +87 -0
  480. package/src/server/client-formatted.js +87 -0
  481. package/src/server/client-icons.js +108 -0
  482. package/src/server/conf.js +1071 -0
  483. package/src/server/crypto.js +210 -0
  484. package/src/server/dns.js +276 -0
  485. package/src/server/downloader.js +74 -0
  486. package/src/server/json-schema.js +77 -0
  487. package/src/server/logger.js +197 -0
  488. package/src/server/network.js +72 -0
  489. package/src/server/object-layer.js +294 -0
  490. package/src/server/peer.js +69 -0
  491. package/src/server/process.js +171 -0
  492. package/src/server/proxy.js +110 -0
  493. package/src/server/runtime.js +170 -0
  494. package/src/server/ssr.js +127 -0
  495. package/src/server/start.js +161 -0
  496. package/src/server/tls.js +251 -0
  497. package/src/server/valkey.js +293 -0
  498. package/src/server.js +25 -0
  499. package/src/ws/IoInterface.js +139 -0
  500. package/src/ws/IoServer.js +88 -0
  501. package/src/ws/core/channels/core.ws.chat.js +23 -0
  502. package/src/ws/core/channels/core.ws.mailer.js +35 -0
  503. package/src/ws/core/channels/core.ws.stream.js +31 -0
  504. package/src/ws/core/core.ws.connection.js +62 -0
  505. package/src/ws/core/core.ws.emit.js +53 -0
  506. package/src/ws/core/core.ws.server.js +76 -0
  507. package/src/ws/core/management/core.ws.chat.js +8 -0
  508. package/src/ws/core/management/core.ws.mailer.js +16 -0
  509. package/src/ws/core/management/core.ws.stream.js +8 -0
  510. package/src/ws/cyberia/channels/cyberia.ws.bot.js +56 -0
  511. package/src/ws/cyberia/channels/cyberia.ws.skill.js +51 -0
  512. package/src/ws/cyberia/channels/cyberia.ws.user.js +437 -0
  513. package/src/ws/cyberia/cyberia.ws.connection.js +36 -0
  514. package/src/ws/cyberia/cyberia.ws.emit.js +14 -0
  515. package/src/ws/cyberia/cyberia.ws.server.js +67 -0
  516. package/src/ws/cyberia/management/cyberia.ws.bot.js +669 -0
  517. package/src/ws/cyberia/management/cyberia.ws.skill.js +441 -0
  518. package/src/ws/cyberia/management/cyberia.ws.user.js +188 -0
  519. package/src/ws/default/channels/default.ws.main.js +16 -0
  520. package/src/ws/default/default.ws.connection.js +22 -0
  521. package/src/ws/default/default.ws.emit.js +14 -0
  522. package/src/ws/default/default.ws.server.js +20 -0
  523. package/src/ws/default/management/default.ws.main.js +8 -0
  524. package/test/api.test.js +53 -0
  525. package/test/crypto.test.js +117 -0
@@ -0,0 +1,689 @@
1
+ /**
2
+ * Module for managing identity and authorization
3
+ * @module src/server/auth.js
4
+ * @namespace Auth
5
+ */
6
+
7
+ import dotenv from 'dotenv';
8
+ import jwt from 'jsonwebtoken';
9
+ import { loggerFactory } from './logger.js';
10
+ import crypto from 'crypto';
11
+ import { promisify } from 'util';
12
+ import { UserDto } from '../api/user/user.model.js';
13
+ import { commonAdminGuard, commonModeratorGuard, validatePassword } from '../client/components/core/CommonJs.js';
14
+ import helmet from 'helmet';
15
+ import rateLimit from 'express-rate-limit';
16
+ import slowDown from 'express-slow-down';
17
+ import cors from 'cors';
18
+ import cookieParser from 'cookie-parser';
19
+ import { DataBaseProvider } from '../db/DataBaseProvider.js';
20
+
21
+ dotenv.config();
22
+ const logger = loggerFactory(import.meta);
23
+
24
+ // Promisified crypto functions
25
+ const pbkdf2 = promisify(crypto.pbkdf2);
26
+
27
+ // Config with sane defaults and parsing
28
+ const config = {
29
+ hashBytes: Number(process.env.PBKDF2_HASH_BYTES) || 32,
30
+ saltBytes: Number(process.env.PBKDF2_SALT_BYTES) || 16,
31
+ iterations: Number(process.env.PBKDF2_ITERATIONS) || 150_000,
32
+ digest: process.env.PBKDF2_DIGEST || 'sha512',
33
+ refreshTokenBytes: Number(process.env.REFRESH_TOKEN_BYTES) || 48,
34
+ jwtAlgorithm: process.env.JWT_ALGORITHM || 'HS512', // consider RS256 with keys
35
+ };
36
+
37
+ // ---------- Password hashing (async) ----------
38
+ /**
39
+ * Hash password asynchronously using PBKDF2.
40
+ * Stored format: iterations$salt$hash
41
+ * @param {string} password The password to hash.
42
+ * @returns {Promise<string>} The hashed password string.
43
+ * @memberof Auth
44
+ */
45
+ async function hashPassword(password) {
46
+ const salt = crypto.randomBytes(config.saltBytes).toString('hex');
47
+ const derived = await pbkdf2(password, salt, config.iterations, config.hashBytes, config.digest);
48
+ return `${config.iterations}$${salt}$${derived.toString('hex')}`;
49
+ }
50
+
51
+ /**
52
+ * Verify password using constant-time comparison
53
+ * @param {string} password The password to verify.
54
+ * @param {string} combined The stored hashed password string (iterations$salt$hash).
55
+ * @returns {Promise<boolean>} True if the password is valid, false otherwise.
56
+ * @memberof Auth
57
+ */
58
+ async function verifyPassword(password, combined) {
59
+ if (!combined) return false;
60
+ const parts = combined.split('$');
61
+ if (parts.length !== 3) return false;
62
+ const [itersStr, salt, originalHex] = parts;
63
+ const iterations = parseInt(itersStr, 10);
64
+ const derived = await pbkdf2(password, salt, iterations, Buffer.from(originalHex, 'hex').length, config.digest);
65
+ const original = Buffer.from(originalHex, 'hex');
66
+ const ok = crypto.timingSafeEqual(derived, original);
67
+ return ok;
68
+ }
69
+
70
+ // ---------- Token hashing & utilities ----------
71
+ /**
72
+ * Hashes a token using SHA256.
73
+ * @param {string} token The token to hash.
74
+ * @returns {string|null} The hashed token as a hex string, or null if token is falsy.
75
+ * @memberof Auth
76
+ */
77
+ function hashToken(token) {
78
+ if (!token) return null;
79
+ return crypto.createHash('sha256').update(token).digest('hex');
80
+ }
81
+
82
+ /**
83
+ * Generates a cryptographically secure random hex string.
84
+ * @param {number} [bytes=config.refreshTokenBytes] The number of bytes to generate.
85
+ * @returns {string} The random hex string.
86
+ * @memberof Auth
87
+ */
88
+ function generateRandomHex(bytes = config.refreshTokenBytes) {
89
+ return crypto.randomBytes(bytes).toString('hex');
90
+ }
91
+
92
+ /**
93
+ * Generates a JWT issuer and audience based on the host and path.
94
+ * @param {object} options The options object.
95
+ * @param {string} options.host The host name.
96
+ * @param {string} options.path The path name.
97
+ * @returns {object} The issuer and audience.
98
+ * @memberof Auth
99
+ */
100
+ function jwtIssuerAudienceFactory(options = { host: '', path: '' }) {
101
+ const audience = `${options.host}${options.path === '/' ? '' : options.path}`;
102
+ const issuer = `${audience}/api`;
103
+ return { issuer, audience };
104
+ }
105
+
106
+ // ---------- JWT helpers ----------
107
+ /**
108
+ * Signs a JWT payload.
109
+ * @param {object} payload The payload to sign.
110
+ * @param {object} [options={}] Additional JWT sign options.
111
+ * @param {string} options.host The host name.
112
+ * @param {string} options.path The path name.
113
+ * @param {number} accessExpireMinutes The access token expiration in minutes.
114
+ * @param {number} refreshExpireMinutes The refresh token expiration in minutes.
115
+ * @returns {string} The signed JWT.
116
+ * @throws {Error} If JWT key is not configured.
117
+ * @memberof Auth
118
+ */
119
+ function jwtSign(
120
+ payload,
121
+ options = { host: '', path: '' },
122
+ accessExpireMinutes = process.env.ACCESS_EXPIRE_MINUTES,
123
+ refreshExpireMinutes = process.env.REFRESH_EXPIRE_MINUTES,
124
+ ) {
125
+ const { issuer, audience } = jwtIssuerAudienceFactory(options);
126
+ const signOptions = {
127
+ algorithm: config.jwtAlgorithm,
128
+ expiresIn: `${accessExpireMinutes}m`,
129
+ issuer,
130
+ audience,
131
+ };
132
+
133
+ if (!payload.jwtid) signOptions.jwtid = generateRandomHex();
134
+
135
+ if (!process.env.JWT_SECRET) throw new Error('JWT key not configured');
136
+
137
+ // Add refreshExpiresAt to the payload, which is the same as the token's own expiry.
138
+ const now = Date.now();
139
+ payload.refreshExpiresAt = now + refreshExpireMinutes * 60 * 1000;
140
+
141
+ logger.info('JWT signed', { payload, signOptions, accessExpireMinutes, refreshExpireMinutes });
142
+
143
+ return jwt.sign(payload, process.env.JWT_SECRET, signOptions);
144
+ }
145
+
146
+ /**
147
+ * Verifies a JWT.
148
+ * @param {string} token The JWT to verify.
149
+ * @param {object} [options={}] Additional JWT verify options.
150
+ * @param {string} options.host The host name.
151
+ * @param {string} options.path The path name.
152
+ * @returns {object} The decoded payload.
153
+ * @throws {jwt.JsonWebTokenError} If the token is invalid or expired.
154
+ * @memberof Auth
155
+ */
156
+ function jwtVerify(token, options = { host: '', path: '' }) {
157
+ try {
158
+ const { issuer, audience } = jwtIssuerAudienceFactory(options);
159
+ const verifyOptions = {
160
+ algorithms: [config.jwtAlgorithm],
161
+ issuer,
162
+ audience,
163
+ };
164
+ return jwt.verify(token, process.env.JWT_SECRET, verifyOptions);
165
+ } catch (err) {
166
+ throw err;
167
+ }
168
+ }
169
+
170
+ // ---------- Request helpers ----------
171
+ /**
172
+ * Extracts the Bearer token from the request headers.
173
+ * @param {import('express').Request} req The Express request object.
174
+ * @returns {string} The token, or an empty string if not found.
175
+ * @memberof Auth
176
+ */
177
+ const getBearerToken = (req) => {
178
+ const header = String(req.headers['authorization'] || req.headers['Authorization'] || '');
179
+ if (header.startsWith('Bearer ')) return header.slice(7).trim();
180
+ return '';
181
+ };
182
+
183
+ /**
184
+ * Checks if the request is a refresh token request.
185
+ * @param {import('express').Request} req The Express request object.
186
+ * @returns {boolean} True if the request is a refresh token request, false otherwise.
187
+ * @memberof Auth
188
+ */
189
+ const isRefreshTokenReq = (req) => req.method === 'GET' && req.params.id === 'auth';
190
+
191
+ // ---------- Middleware ----------
192
+ /**
193
+ * Creates a middleware to authenticate requests using a JWT Bearer token.
194
+ * @param {object} options The options object.
195
+ * @param {string} options.host The host name.
196
+ * @param {string} options.path The path name.
197
+ * @returns {function} The middleware function.
198
+ * @memberof Auth
199
+ */
200
+ const authMiddlewareFactory = (options = { host: '', path: '' }) => {
201
+ /**
202
+ * Express middleware to authenticate requests using a JWT Bearer token.
203
+ * @param {import('express').Request} req The Express request object.
204
+ * @param {import('express').Response} res The Express response object.
205
+ * @param {import('express').NextFunction} next The next middleware function.
206
+ * @memberof Auth
207
+ */
208
+ const authMiddleware = async (req, res, next) => {
209
+ try {
210
+ const token = getBearerToken(req);
211
+ if (!token) return res.status(401).json({ status: 'error', message: 'unauthorized: token missing' });
212
+
213
+ const payload = jwtVerify(token, options);
214
+
215
+ // Validate IP and User-Agent to mitigate token theft
216
+ if (payload.ip && payload.ip !== req.ip) {
217
+ logger.warn(`IP mismatch for ${payload._id}: jwt(${payload.ip}) !== req(${req.ip})`);
218
+ return res.status(401).json({ status: 'error', message: 'unauthorized: ip mismatch' });
219
+ }
220
+
221
+ if (payload.userAgent && payload.userAgent !== req.headers['user-agent']) {
222
+ logger.warn(`UA mismatch for ${payload._id}`);
223
+ return res.status(401).json({ status: 'error', message: 'unauthorized: user-agent mismatch' });
224
+ }
225
+
226
+ // Non-guest verify session exists
227
+ if (payload.jwtid && payload.role !== 'guest') {
228
+ const User = DataBaseProvider.instance[`${payload.host}${payload.path}`].mongoose.models.User;
229
+ const user = await User.findOne({ _id: payload._id, 'activeSessions._id': payload.jwtid }).lean();
230
+
231
+ if (!user) {
232
+ return res.status(401).json({ status: 'error', message: 'unauthorized: invalid session' });
233
+ }
234
+ const session = user.activeSessions.find((s) => s._id.toString() === payload.jwtid);
235
+
236
+ if (!session) {
237
+ return res.status(401).json({ status: 'error', message: 'unauthorized: invalid session' });
238
+ }
239
+
240
+ // check session ip
241
+ if (session.ip !== req.ip) {
242
+ logger.warn(`IP mismatch for ${payload._id}: jwt(${session.ip}) !== req(${req.ip})`);
243
+ return res.status(401).json({ status: 'error', message: 'unauthorized: ip mismatch' });
244
+ }
245
+
246
+ // check session userAgent
247
+ if (session.userAgent !== req.headers['user-agent']) {
248
+ logger.warn(`UA mismatch for ${payload._id}`);
249
+ return res.status(401).json({ status: 'error', message: 'unauthorized: user-agent mismatch' });
250
+ }
251
+
252
+ // compare payload host and path with session host and path
253
+ if (payload.host !== session.host || payload.path !== session.path) {
254
+ logger.warn(`Host or path mismatch for ${payload._id}`);
255
+ return res.status(401).json({ status: 'error', message: 'unauthorized: host or path mismatch' });
256
+ }
257
+
258
+ if (!isRefreshTokenReq(req) && session.expiresAt < new Date()) {
259
+ return res.status(401).json({ status: 'error', message: 'unauthorized: session expired' });
260
+ }
261
+ }
262
+
263
+ req.auth = { user: payload };
264
+ return next();
265
+ } catch (err) {
266
+ logger.warn('authMiddleware error', err && err.message);
267
+ return res.status(401).json({ status: 'error', message: 'unauthorized' });
268
+ }
269
+ };
270
+ return authMiddleware;
271
+ };
272
+
273
+ /**
274
+ * Express middleware to guard routes for admin users.
275
+ * @param {import('express').Request} req The Express request object.
276
+ * @param {import('express').Response} res The Express response object.
277
+ * @param {import('express').NextFunction} next The next middleware function.
278
+ * @memberof Auth
279
+ */
280
+ const adminGuard = (req, res, next) => {
281
+ try {
282
+ if (!req.auth || !commonAdminGuard(req.auth.user.role))
283
+ return res.status(403).json({ status: 'error', message: 'Insufficient permission' });
284
+ return next();
285
+ } catch (err) {
286
+ logger.error(err);
287
+ return res.status(400).json({ status: 'error', message: 'bad request' });
288
+ }
289
+ };
290
+ /**
291
+ * Express middleware to guard routes for moderator or admin users.
292
+ * @param {import('express').Request} req The Express request object.
293
+ * @param {import('express').Response} res The Express response object.
294
+ * @param {import('express').NextFunction} next The next middleware function.
295
+ * @memberof Auth
296
+ */
297
+ const moderatorGuard = (req, res, next) => {
298
+ try {
299
+ if (!req.auth || !commonModeratorGuard(req.auth.user.role))
300
+ return res.status(403).json({ status: 'error', message: 'Insufficient permission' });
301
+ return next();
302
+ } catch (err) {
303
+ logger.error(err);
304
+ return res.status(400).json({ status: 'error', message: 'bad request' });
305
+ }
306
+ };
307
+
308
+ // ---------- Password validation middleware (server-side) ----------
309
+ /**
310
+ * Validates the password from the request body.
311
+ * @param {import('express').Request} req The Express request object.
312
+ * @returns {{status: 'success'}|{status: 'error', message: string}} Validation result.
313
+ * @memberof Auth
314
+ */
315
+ const validatePasswordMiddleware = (req) => {
316
+ const errors = req.body && 'password' in req.body ? validatePassword(req.body.password) : [];
317
+ if (errors.length) {
318
+ return { status: 'error', message: 'Password: ' + errors.map((e) => e[req.lang] || e.en || e).join(', ') };
319
+ }
320
+ return { status: 'success' };
321
+ };
322
+
323
+ // ---------- Session & Refresh token management ----------
324
+
325
+ /**
326
+ * Creates cookie options for the refresh token.
327
+ * @param {import('express').Request} req The Express request object.
328
+ * @param {string} host The host name.
329
+ * @returns {object} Cookie options.
330
+ * @memberof Auth
331
+ */
332
+ const cookieOptionsFactory = (req, host) => {
333
+ const isProduction = process.env.NODE_ENV === 'production';
334
+
335
+ // Determine if request is secure: respect X-Forwarded-Proto when behind proxy
336
+ const forwardedProto = (req.headers && req.headers['x-forwarded-proto']) || '';
337
+ const reqIsSecure = Boolean(req.secure || forwardedProto.split(',')[0] === 'https');
338
+
339
+ // secure must be true for SameSite=None to work across sites
340
+ const secure = isProduction ? reqIsSecure : req.protocol === 'https';
341
+ const sameSite = secure ? 'None' : 'Lax';
342
+
343
+ // Safe parse of maxAge minutes
344
+ const maxAge = parseInt(process.env.ACCESS_EXPIRE_MINUTES) * 60 * 1000;
345
+
346
+ const opts = {
347
+ httpOnly: true,
348
+ secure,
349
+ sameSite,
350
+ path: '/',
351
+ domain: process.env.NODE_ENV === 'production' ? host : 'localhost',
352
+ maxAge,
353
+ };
354
+
355
+ return opts;
356
+ };
357
+
358
+ /**
359
+ * Create session and set refresh cookie. Rotating and hashed stored token.
360
+ * @param {object} user The user object.
361
+ * @param {import('mongoose').Model} User The Mongoose User model.
362
+ * @param {import('express').Request} req The Express request object.
363
+ * @param {import('express').Response} res The Express response object.
364
+ * @param {object} options Additional options.
365
+ * @param {string} options.host The host name.
366
+ * @param {string} options.path The path name.
367
+ * @returns {Promise<{jwtid: string}>} The session ID.
368
+ * @memberof Auth
369
+ */
370
+ async function createSessionAndUserToken(user, User, req, res, options = { host: '', path: '' }) {
371
+ const refreshToken = hashToken(generateRandomHex());
372
+ const now = Date.now();
373
+ const expiresAt = new Date(now + parseInt(process.env.REFRESH_EXPIRE_MINUTES) * 60 * 1000);
374
+
375
+ const newSession = {
376
+ tokenHash: refreshToken,
377
+ ip: req.ip,
378
+ userAgent: req.headers['user-agent'],
379
+ host: options.host,
380
+ path: options.path,
381
+ createdAt: new Date(now),
382
+ expiresAt,
383
+ };
384
+
385
+ // push session
386
+ const updatedUser = await User.findByIdAndUpdate(user._id, { $push: { activeSessions: newSession } }, { new: true });
387
+ const session = updatedUser.activeSessions[updatedUser.activeSessions.length - 1];
388
+ const jwtid = session._id.toString();
389
+
390
+ // Secure cookie settings
391
+ res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req, options.host));
392
+
393
+ return { jwtid };
394
+ }
395
+
396
+ /**
397
+ * Removes a session by its ID for a given user.
398
+ * @param {import('mongoose').Model} User The Mongoose User model.
399
+ * @param {string} userId The ID of the user.
400
+ * @param {string} sessionId The ID of the session to remove.
401
+ * @returns {Promise<void>}
402
+ * @memberof Auth
403
+ */
404
+ async function removeSession(User, userId, sessionId) {
405
+ return await User.updateOne({ _id: userId }, { $pull: { activeSessions: { _id: sessionId } } });
406
+ }
407
+
408
+ /**
409
+ * Logs out a user session by removing it from the database and clearing the refresh token cookie.
410
+ * @param {import('mongoose').Model} User The Mongoose User model.
411
+ * @param {import('express').Request} req The Express request object.
412
+ * @param {import('express').Response} res The Express response object.
413
+ * @returns {Promise<boolean>} True if a session was found and removed, false otherwise.
414
+ * @memberof Auth
415
+ */
416
+ async function logoutSession(User, req, res) {
417
+ const refreshToken = req.cookies?.refreshToken;
418
+
419
+ if (!refreshToken) {
420
+ return false;
421
+ }
422
+
423
+ const user = await User.findOne({ 'activeSessions.tokenHash': refreshToken });
424
+
425
+ if (!user) {
426
+ return false;
427
+ }
428
+
429
+ const session = user.activeSessions.find((s) => s.tokenHash === refreshToken);
430
+
431
+ const result = await removeSession(User, user._id, session._id);
432
+
433
+ res.clearCookie('refreshToken', { path: '/' });
434
+
435
+ return result.modifiedCount > 0;
436
+ }
437
+
438
+ /**
439
+ * Create user and immediate session + access token
440
+ * @param {import('express').Request} req The Express request object.
441
+ * @param {import('express').Response} res The Express response object.
442
+ * @param {import('mongoose').Model} User The Mongoose User model.
443
+ * @param {import('mongoose').Model} File The Mongoose File model.
444
+ * @param {object} [options={}] Additional options.
445
+ * @param {Function} options.getDefaultProfileImageId Function to get the default profile image ID.
446
+ * @param {string} options.host The host name.
447
+ * @param {string} options.path The path name.
448
+ * @returns {Promise<{token: string, user: object}>} The access token and user object.
449
+ * @throws {Error} If password validation fails.
450
+ * @memberof Auth
451
+ */
452
+ async function createUserAndSession(req, res, User, File, options = { host: '', path: '' }) {
453
+ const pwdCheck = validatePasswordMiddleware(req);
454
+ if (pwdCheck.status === 'error') throw new Error(pwdCheck.message);
455
+
456
+ req.body.password = await hashPassword(req.body.password);
457
+ req.body.role = req.body.role === 'guest' ? 'guest' : 'user';
458
+ req.body.profileImageId = await options.getDefaultProfileImageId(File);
459
+
460
+ const saved = await new User(req.body).save();
461
+ const user = await User.findOne({ _id: saved._id }).select(UserDto.select.get());
462
+
463
+ const { jwtid } = await createSessionAndUserToken(user, User, req, res, options);
464
+ const token = jwtSign(
465
+ UserDto.auth.payload(user, jwtid, req.ip, req.headers['user-agent'], options.host, options.path),
466
+ options,
467
+ );
468
+ return { token, user };
469
+ }
470
+
471
+ /**
472
+ * Refresh session and rotate refresh token.
473
+ * Detect token reuse: if a refresh token is presented but not found, consider
474
+ * it a possible theft and revoke all sessions for that user.
475
+ * @param {import('express').Request} req The Express request object.
476
+ * @param {import('express').Response} res The Express response object.
477
+ * @param {import('mongoose').Model} User The Mongoose User model.
478
+ * @param {object} options Additional options.
479
+ * @param {string} options.host The host name.
480
+ * @param {string} options.path The path name.
481
+ * @returns {Promise<{token: string}>} The new access token.
482
+ * @throws {Error} If the refresh token is missing, invalid, or expired.
483
+ * @memberof Auth
484
+ */
485
+ async function refreshSessionAndToken(req, res, User, options = { host: '', path: '' }) {
486
+ const currentRefreshToken = req.cookies.refreshToken;
487
+ if (!currentRefreshToken) throw new Error('Refresh token missing');
488
+
489
+ // Find user owning that token
490
+ const user = await User.findOne({ 'activeSessions.tokenHash': currentRefreshToken });
491
+
492
+ if (!user) {
493
+ // Possible token reuse: look up user by some other signals? If not possible, log and throw.
494
+ // TODO: on cors requests, this will throw an error, because the cookie is not sent.
495
+ logger.warn('Refresh token reuse or invalid token detected');
496
+ // Optional: revoke by clearing cookie and returning unauthorized
497
+ res.clearCookie('refreshToken', { path: '/' });
498
+ throw new Error('Invalid refresh token');
499
+ }
500
+
501
+ // Locate session
502
+ const session = user.activeSessions.find((s) => s.tokenHash === currentRefreshToken);
503
+ if (!session) {
504
+ // Shouldn't happen, but safe-guard
505
+ res.clearCookie('refreshToken', { path: '/' });
506
+ throw new Error('Session not found');
507
+ }
508
+
509
+ // Check expiry
510
+ if (!isRefreshTokenReq(req) && session.expiresAt && session.expiresAt < new Date()) {
511
+ const result = await removeSession(User, user._id, session._id);
512
+ if (result) throw new Error('Refresh token expired');
513
+ else throw new Error('Session not found');
514
+ }
515
+
516
+ // Rotate: generate new token, update stored hash and metadata
517
+ const refreshToken = hashToken(generateRandomHex());
518
+ session.tokenHash = refreshToken;
519
+ session.expiresAt = new Date(Date.now() + parseInt(process.env.REFRESH_EXPIRE_MINUTES) * 60 * 1000);
520
+ session.ip = req.ip;
521
+ session.userAgent = req.headers['user-agent'];
522
+ await user.save({ validateBeforeSave: false });
523
+
524
+ logger.warn('Refreshed session for user ' + user.email);
525
+
526
+ res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req, options.host));
527
+
528
+ return jwtSign(
529
+ UserDto.auth.payload(user, session._id.toString(), req.ip, req.headers['user-agent'], options.host, options.path),
530
+ options,
531
+ );
532
+ }
533
+
534
+ // ---------- Security middleware composition ----------
535
+ /**
536
+ * Applies a set of security-related middleware to an Express app.
537
+ * @param {import('express').Application} app The Express application.
538
+ * @param {object} [opts={}] Options for security middleware.
539
+ * @param {string[]} opts.origin Allowed origins for CORS.
540
+ * @param {object} opts.rate Rate limiting options for `express-rate-limit`.
541
+ * @param {object} opts.slowdown Slow down options for `express-slow-down`.
542
+ * @param {object} opts.cookie Cookie options.
543
+ * @param {string[]} opts.frameAncestors Allowed frame ancestors for CSP.
544
+ * @memberof Auth
545
+ */
546
+ function applySecurity(app, opts = {}) {
547
+ const {
548
+ origin,
549
+ rate = { windowMs: 15 * 60 * 1000, max: 500 },
550
+ slowdown = { windowMs: 15 * 60 * 1000, delayAfter: 50, delayMs: () => 500 },
551
+ cookie = { secure: process.env.NODE_ENV === 'production', sameSite: 'Strict' },
552
+ frameAncestors = ["'self'"],
553
+ } = opts;
554
+
555
+ app.disable('x-powered-by');
556
+
557
+ // Generate nonce per request and attach to res.locals for templates
558
+ app.use((req, res, next) => {
559
+ res.locals.nonce = crypto.randomBytes(16).toString('base64');
560
+ next();
561
+ });
562
+
563
+ // Basic header hardening with Helmet
564
+ app.use(
565
+ helmet({
566
+ // We'll customize CSP below because many apps need tailored directives
567
+ crossOriginEmbedderPolicy: true,
568
+ crossOriginOpenerPolicy: { policy: 'same-origin' },
569
+ crossOriginResourcePolicy: { policy: 'same-origin' },
570
+ originAgentCluster: false,
571
+ // Permissions-Policy (formerly Feature-Policy) — limit powerful features
572
+ permissionsPolicy: {
573
+ // example: disable geolocation, camera, microphone, payment
574
+ features: {
575
+ fullscreen: ["'self'"],
576
+ geolocation: [],
577
+ camera: [],
578
+ microphone: [],
579
+ payment: [],
580
+ },
581
+ },
582
+ }),
583
+ );
584
+
585
+ // Strict HSTS (only enable in production over TLS)
586
+ // maxAge in seconds (e.g. 31536000 = 1 year). Use includeSubDomains and preload carefully.
587
+ if (process.env.NODE_ENV === 'production') {
588
+ app.use(
589
+ helmet.hsts({
590
+ maxAge: 31536000,
591
+ includeSubDomains: true,
592
+ preload: true,
593
+ }),
594
+ );
595
+ }
596
+
597
+ // Other helpful Helmet policies
598
+ app.use(helmet.noSniff()); // X-Content-Type-Options: nosniff
599
+ app.use(helmet.frameguard({ action: 'deny' })); // X-Frame-Options: DENY
600
+ app.use(helmet.referrerPolicy({ policy: 'no-referrer-when-downgrade' }));
601
+
602
+ // Content-Security-Policy: include nonce from res.locals
603
+ // Note: We avoid 'unsafe-inline' on script/style. Use nonces or hashes.
604
+ const httpDirective = process.env.NODE_ENV === 'production' ? 'https:' : 'http:';
605
+ app.use(
606
+ helmet.contentSecurityPolicy({
607
+ useDefaults: true,
608
+ directives: {
609
+ defaultSrc: ["'self'"],
610
+ baseUri: ["'self'"],
611
+ blockAllMixedContent: [],
612
+ fontSrc: ["'self'", httpDirective, 'data:'],
613
+ frameAncestors: frameAncestors,
614
+ imgSrc: ["'self'", 'data:', httpDirective, 'https:', 'blob:'],
615
+ objectSrc: ["'none'"],
616
+ // script-src and script-src-elem include dynamic nonce
617
+ scriptSrc: [
618
+ "'self'",
619
+ (req, res) => `'nonce-${res.locals.nonce}'`,
620
+ (req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : ''),
621
+ ],
622
+ scriptSrcElem: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
623
+ // style-src: avoid 'unsafe-inline' when possible; if you must inline styles,
624
+ // use a nonce for them too (or hash).
625
+ styleSrc: [
626
+ "'self'",
627
+ httpDirective,
628
+ (req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : `'nonce-${res.locals.nonce}'`),
629
+ ],
630
+ // deny plugins
631
+ objectSrc: ["'none'"],
632
+ },
633
+ }),
634
+ );
635
+
636
+ // CORS - be explicit. In production, pass allowed origin(s) as opts.origin
637
+ app.use(
638
+ cors({
639
+ origin: origin || false,
640
+ methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
641
+ credentials: true,
642
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept', 'withcredentials', 'credentials'],
643
+ maxAge: 600,
644
+ }),
645
+ );
646
+ logger.info('Cors origin', origin);
647
+
648
+ // Rate limiting + slow down
649
+ const limiter = rateLimit({
650
+ windowMs: rate.windowMs,
651
+ max: rate.max,
652
+ standardHeaders: true,
653
+ legacyHeaders: false,
654
+ message: { error: 'Too many requests, please try again later.' },
655
+ });
656
+ app.use(limiter);
657
+
658
+ const speedLimiter = slowDown({
659
+ windowMs: slowdown.windowMs,
660
+ delayAfter: slowdown.delayAfter,
661
+ delayMs: () => slowdown.delayMs,
662
+ });
663
+ app.use(speedLimiter);
664
+
665
+ // Cookie parsing
666
+ app.use(cookieParser(process.env.JWT_SECRET));
667
+ }
668
+
669
+ export {
670
+ authMiddlewareFactory,
671
+ hashPassword,
672
+ verifyPassword,
673
+ hashToken,
674
+ jwtSign,
675
+ jwtVerify,
676
+ jwtSign as hashJWT,
677
+ jwtVerify as verifyJWT,
678
+ adminGuard,
679
+ moderatorGuard,
680
+ validatePasswordMiddleware,
681
+ getBearerToken,
682
+ createSessionAndUserToken,
683
+ createUserAndSession,
684
+ refreshSessionAndToken,
685
+ logoutSession,
686
+ removeSession,
687
+ applySecurity,
688
+ isRefreshTokenReq,
689
+ };