@vandenberghinc/volt 1.1.2

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 (451) hide show
  1. package/.vrepo +28 -0
  2. package/.vscode/tasks.json +87 -0
  3. package/README.md +67 -0
  4. package/backend/dist/cjs/blacklist.d.ts +10 -0
  5. package/backend/dist/cjs/blacklist.js +53 -0
  6. package/backend/dist/cjs/cli.d.ts +2 -0
  7. package/backend/dist/cjs/cli.js +263 -0
  8. package/backend/dist/cjs/database.d.ts +364 -0
  9. package/backend/dist/cjs/database.js +1962 -0
  10. package/backend/dist/cjs/endpoint.d.ts +57 -0
  11. package/backend/dist/cjs/endpoint.js +425 -0
  12. package/backend/dist/cjs/file_watcher.d.ts +44 -0
  13. package/backend/dist/cjs/file_watcher.js +348 -0
  14. package/backend/dist/cjs/frontend.d.ts +13 -0
  15. package/backend/dist/cjs/frontend.js +30 -0
  16. package/backend/dist/cjs/image_endpoint.d.ts +24 -0
  17. package/backend/dist/cjs/image_endpoint.js +210 -0
  18. package/backend/dist/cjs/logger.d.ts +5 -0
  19. package/backend/dist/cjs/logger.js +16 -0
  20. package/backend/dist/cjs/meta.d.ts +50 -0
  21. package/backend/dist/cjs/meta.js +153 -0
  22. package/backend/dist/cjs/mutex.d.ts +24 -0
  23. package/backend/dist/cjs/mutex.js +52 -0
  24. package/backend/dist/cjs/package.json +1 -0
  25. package/backend/dist/cjs/payments/paddle.d.ts +161 -0
  26. package/backend/dist/cjs/payments/paddle.js +2301 -0
  27. package/backend/dist/cjs/plugins/browser.d.ts +36 -0
  28. package/backend/dist/cjs/plugins/browser.js +183 -0
  29. package/backend/dist/cjs/plugins/communication.d.ts +70 -0
  30. package/backend/dist/cjs/plugins/communication.js +177 -0
  31. package/backend/dist/cjs/plugins/css.d.ts +10 -0
  32. package/backend/dist/cjs/plugins/css.js +71 -0
  33. package/backend/dist/cjs/plugins/mail.d.ts +277 -0
  34. package/backend/dist/cjs/plugins/mail.js +1419 -0
  35. package/backend/dist/cjs/plugins/pdf.d.ts +757 -0
  36. package/backend/dist/cjs/plugins/pdf.js +1694 -0
  37. package/backend/dist/cjs/plugins/thread_monitor.d.ts +18 -0
  38. package/backend/dist/cjs/plugins/thread_monitor.js +127 -0
  39. package/backend/dist/cjs/plugins/ts/compiler.d.ts +132 -0
  40. package/backend/dist/cjs/plugins/ts/compiler.js +944 -0
  41. package/backend/dist/cjs/plugins/ts/preprocessing.d.ts +14 -0
  42. package/backend/dist/cjs/plugins/ts/preprocessing.js +762 -0
  43. package/backend/dist/cjs/rate_limit.d.ts +65 -0
  44. package/backend/dist/cjs/rate_limit.js +463 -0
  45. package/backend/dist/cjs/request.deprc.d.ts +48 -0
  46. package/backend/dist/cjs/request.deprc.js +572 -0
  47. package/backend/dist/cjs/response.deprc.d.ts +55 -0
  48. package/backend/dist/cjs/response.deprc.js +275 -0
  49. package/backend/dist/cjs/server.d.ts +311 -0
  50. package/backend/dist/cjs/server.js +3475 -0
  51. package/backend/dist/cjs/splash_screen.d.ts +35 -0
  52. package/backend/dist/cjs/splash_screen.js +152 -0
  53. package/backend/dist/cjs/status.d.ts +60 -0
  54. package/backend/dist/cjs/status.js +199 -0
  55. package/backend/dist/cjs/stream.d.ts +75 -0
  56. package/backend/dist/cjs/stream.js +954 -0
  57. package/backend/dist/cjs/users.d.ts +111 -0
  58. package/backend/dist/cjs/users.js +1945 -0
  59. package/backend/dist/cjs/utils.d.ts +27 -0
  60. package/backend/dist/cjs/utils.js +329 -0
  61. package/backend/dist/cjs/view.d.ts +52 -0
  62. package/backend/dist/cjs/view.js +568 -0
  63. package/backend/dist/cjs/vinc.d.ts +2 -0
  64. package/backend/dist/cjs/vinc.dev.d.ts +2 -0
  65. package/backend/dist/cjs/vinc.dev.js +42 -0
  66. package/backend/dist/cjs/vinc.js +42 -0
  67. package/backend/dist/cjs/volt.d.ts +15 -0
  68. package/backend/dist/cjs/volt.js +64 -0
  69. package/backend/dist/css/adyen.css +92 -0
  70. package/backend/dist/css/volt.css +65 -0
  71. package/backend/dist/esm/blacklist.d.ts +10 -0
  72. package/backend/dist/esm/blacklist.js +49 -0
  73. package/backend/dist/esm/cli.d.ts +2 -0
  74. package/backend/dist/esm/cli.js +228 -0
  75. package/backend/dist/esm/database.d.ts +364 -0
  76. package/backend/dist/esm/database.js +1957 -0
  77. package/backend/dist/esm/endpoint.d.ts +57 -0
  78. package/backend/dist/esm/endpoint.js +421 -0
  79. package/backend/dist/esm/file_watcher.d.ts +44 -0
  80. package/backend/dist/esm/file_watcher.js +313 -0
  81. package/backend/dist/esm/frontend.d.ts +13 -0
  82. package/backend/dist/esm/frontend.js +27 -0
  83. package/backend/dist/esm/image_endpoint.d.ts +24 -0
  84. package/backend/dist/esm/image_endpoint.js +206 -0
  85. package/backend/dist/esm/logger.d.ts +5 -0
  86. package/backend/dist/esm/logger.js +13 -0
  87. package/backend/dist/esm/meta.d.ts +50 -0
  88. package/backend/dist/esm/meta.js +149 -0
  89. package/backend/dist/esm/mutex.d.ts +24 -0
  90. package/backend/dist/esm/mutex.js +48 -0
  91. package/backend/dist/esm/payments/paddle.d.ts +161 -0
  92. package/backend/dist/esm/payments/paddle.js +2261 -0
  93. package/backend/dist/esm/plugins/browser.d.ts +36 -0
  94. package/backend/dist/esm/plugins/browser.js +176 -0
  95. package/backend/dist/esm/plugins/communication.d.ts +70 -0
  96. package/backend/dist/esm/plugins/communication.js +169 -0
  97. package/backend/dist/esm/plugins/css.d.ts +10 -0
  98. package/backend/dist/esm/plugins/css.js +64 -0
  99. package/backend/dist/esm/plugins/mail.d.ts +277 -0
  100. package/backend/dist/esm/plugins/mail.js +1403 -0
  101. package/backend/dist/esm/plugins/pdf.d.ts +757 -0
  102. package/backend/dist/esm/plugins/pdf.js +1694 -0
  103. package/backend/dist/esm/plugins/thread_monitor.d.ts +18 -0
  104. package/backend/dist/esm/plugins/thread_monitor.js +120 -0
  105. package/backend/dist/esm/plugins/ts/compiler.d.ts +132 -0
  106. package/backend/dist/esm/plugins/ts/compiler.js +907 -0
  107. package/backend/dist/esm/plugins/ts/preprocessing.d.ts +14 -0
  108. package/backend/dist/esm/plugins/ts/preprocessing.js +724 -0
  109. package/backend/dist/esm/rate_limit.d.ts +65 -0
  110. package/backend/dist/esm/rate_limit.js +425 -0
  111. package/backend/dist/esm/request.deprc.d.ts +48 -0
  112. package/backend/dist/esm/request.deprc.js +572 -0
  113. package/backend/dist/esm/response.deprc.d.ts +55 -0
  114. package/backend/dist/esm/response.deprc.js +275 -0
  115. package/backend/dist/esm/server.d.ts +311 -0
  116. package/backend/dist/esm/server.js +3435 -0
  117. package/backend/dist/esm/splash_screen.d.ts +35 -0
  118. package/backend/dist/esm/splash_screen.js +148 -0
  119. package/backend/dist/esm/status.d.ts +60 -0
  120. package/backend/dist/esm/status.js +196 -0
  121. package/backend/dist/esm/stream.d.ts +75 -0
  122. package/backend/dist/esm/stream.js +947 -0
  123. package/backend/dist/esm/users.d.ts +111 -0
  124. package/backend/dist/esm/users.js +1908 -0
  125. package/backend/dist/esm/utils.d.ts +27 -0
  126. package/backend/dist/esm/utils.js +324 -0
  127. package/backend/dist/esm/view.d.ts +52 -0
  128. package/backend/dist/esm/view.js +561 -0
  129. package/backend/dist/esm/vinc.d.ts +2 -0
  130. package/backend/dist/esm/vinc.dev.d.ts +2 -0
  131. package/backend/dist/esm/vinc.dev.js +6 -0
  132. package/backend/dist/esm/vinc.js +6 -0
  133. package/backend/dist/esm/volt.d.ts +15 -0
  134. package/backend/dist/esm/volt.js +23 -0
  135. package/backend/dist/esm-dev/blacklist.d.ts +10 -0
  136. package/backend/dist/esm-dev/blacklist.js +49 -0
  137. package/backend/dist/esm-dev/cli.d.ts +2 -0
  138. package/backend/dist/esm-dev/cli.js +228 -0
  139. package/backend/dist/esm-dev/database.d.ts +364 -0
  140. package/backend/dist/esm-dev/database.js +1957 -0
  141. package/backend/dist/esm-dev/endpoint.d.ts +57 -0
  142. package/backend/dist/esm-dev/endpoint.js +421 -0
  143. package/backend/dist/esm-dev/file_watcher.d.ts +44 -0
  144. package/backend/dist/esm-dev/file_watcher.js +313 -0
  145. package/backend/dist/esm-dev/frontend.d.ts +13 -0
  146. package/backend/dist/esm-dev/frontend.js +27 -0
  147. package/backend/dist/esm-dev/image_endpoint.d.ts +24 -0
  148. package/backend/dist/esm-dev/image_endpoint.js +206 -0
  149. package/backend/dist/esm-dev/logger.d.ts +5 -0
  150. package/backend/dist/esm-dev/logger.js +13 -0
  151. package/backend/dist/esm-dev/meta.d.ts +50 -0
  152. package/backend/dist/esm-dev/meta.js +149 -0
  153. package/backend/dist/esm-dev/mutex.d.ts +24 -0
  154. package/backend/dist/esm-dev/mutex.js +48 -0
  155. package/backend/dist/esm-dev/payments/paddle.d.ts +161 -0
  156. package/backend/dist/esm-dev/payments/paddle.js +2261 -0
  157. package/backend/dist/esm-dev/plugins/browser.d.ts +36 -0
  158. package/backend/dist/esm-dev/plugins/browser.js +176 -0
  159. package/backend/dist/esm-dev/plugins/communication.d.ts +70 -0
  160. package/backend/dist/esm-dev/plugins/communication.js +169 -0
  161. package/backend/dist/esm-dev/plugins/css.d.ts +10 -0
  162. package/backend/dist/esm-dev/plugins/css.js +64 -0
  163. package/backend/dist/esm-dev/plugins/mail.d.ts +277 -0
  164. package/backend/dist/esm-dev/plugins/mail.js +1403 -0
  165. package/backend/dist/esm-dev/plugins/pdf.d.ts +757 -0
  166. package/backend/dist/esm-dev/plugins/pdf.js +1694 -0
  167. package/backend/dist/esm-dev/plugins/thread_monitor.d.ts +18 -0
  168. package/backend/dist/esm-dev/plugins/thread_monitor.js +120 -0
  169. package/backend/dist/esm-dev/plugins/ts/compiler.d.ts +132 -0
  170. package/backend/dist/esm-dev/plugins/ts/compiler.js +907 -0
  171. package/backend/dist/esm-dev/plugins/ts/preprocessing.d.ts +14 -0
  172. package/backend/dist/esm-dev/plugins/ts/preprocessing.js +724 -0
  173. package/backend/dist/esm-dev/rate_limit.d.ts +65 -0
  174. package/backend/dist/esm-dev/rate_limit.js +425 -0
  175. package/backend/dist/esm-dev/request.deprc.d.ts +48 -0
  176. package/backend/dist/esm-dev/request.deprc.js +572 -0
  177. package/backend/dist/esm-dev/response.deprc.d.ts +55 -0
  178. package/backend/dist/esm-dev/response.deprc.js +275 -0
  179. package/backend/dist/esm-dev/server.d.ts +311 -0
  180. package/backend/dist/esm-dev/server.js +3435 -0
  181. package/backend/dist/esm-dev/splash_screen.d.ts +35 -0
  182. package/backend/dist/esm-dev/splash_screen.js +148 -0
  183. package/backend/dist/esm-dev/status.d.ts +60 -0
  184. package/backend/dist/esm-dev/status.js +196 -0
  185. package/backend/dist/esm-dev/stream.d.ts +75 -0
  186. package/backend/dist/esm-dev/stream.js +947 -0
  187. package/backend/dist/esm-dev/users.d.ts +111 -0
  188. package/backend/dist/esm-dev/users.js +1908 -0
  189. package/backend/dist/esm-dev/utils.d.ts +27 -0
  190. package/backend/dist/esm-dev/utils.js +324 -0
  191. package/backend/dist/esm-dev/view.d.ts +52 -0
  192. package/backend/dist/esm-dev/view.js +561 -0
  193. package/backend/dist/esm-dev/vinc.d.ts +2 -0
  194. package/backend/dist/esm-dev/vinc.dev.d.ts +2 -0
  195. package/backend/dist/esm-dev/vinc.dev.js +6 -0
  196. package/backend/dist/esm-dev/vinc.js +6 -0
  197. package/backend/dist/esm-dev/volt.d.ts +15 -0
  198. package/backend/dist/esm-dev/volt.js +23 -0
  199. package/backend/src/blacklist.ts +69 -0
  200. package/backend/src/cli.js +245 -0
  201. package/backend/src/database.ts +2241 -0
  202. package/backend/src/endpoint.ts +494 -0
  203. package/backend/src/file_watcher.ts +359 -0
  204. package/backend/src/frontend.ts +35 -0
  205. package/backend/src/globals.d.ts +8 -0
  206. package/backend/src/image_endpoint.ts +258 -0
  207. package/backend/src/logger.ts +18 -0
  208. package/backend/src/meta.ts +202 -0
  209. package/backend/src/mutex.ts +51 -0
  210. package/backend/src/payments/paddle.ts +2659 -0
  211. package/backend/src/plugins/browser.ts +188 -0
  212. package/backend/src/plugins/communication.ts +204 -0
  213. package/backend/src/plugins/css.ts +84 -0
  214. package/backend/src/plugins/fonts/Menlo-Bold.ttf +0 -0
  215. package/backend/src/plugins/fonts/Menlo-Regular.ttf +0 -0
  216. package/backend/src/plugins/mail.ts +1720 -0
  217. package/backend/src/plugins/pdf.js +1932 -0
  218. package/backend/src/plugins/thread_monitor.ts +164 -0
  219. package/backend/src/plugins/ts/compiler.ts +1242 -0
  220. package/backend/src/plugins/ts/preprocessing.ts +812 -0
  221. package/backend/src/rate_limit.ts +503 -0
  222. package/backend/src/request.deprc.js +626 -0
  223. package/backend/src/response.deprc.js +354 -0
  224. package/backend/src/server.ts +4149 -0
  225. package/backend/src/splash_screen.ts +192 -0
  226. package/backend/src/status.ts +199 -0
  227. package/backend/src/stream.ts +1070 -0
  228. package/backend/src/users.ts +2077 -0
  229. package/backend/src/utils.ts +359 -0
  230. package/backend/src/view.ts +655 -0
  231. package/backend/src/vinc.dev.js +6 -0
  232. package/backend/src/vinc.ts +6 -0
  233. package/backend/src/volt.js +25 -0
  234. package/backend/tsconfig.cjs.json +29 -0
  235. package/backend/tsconfig.esm.dev.json +34 -0
  236. package/backend/tsconfig.esm.json +30 -0
  237. package/backend/tsconfig.json +2 -0
  238. package/frontend/compile.js +436 -0
  239. package/frontend/dist/elements/base.d.ts +9891 -0
  240. package/frontend/dist/elements/base.js +8818 -0
  241. package/frontend/dist/elements/module.d.ts +16 -0
  242. package/frontend/dist/elements/module.js +178 -0
  243. package/frontend/dist/modules/array.d.ts +37 -0
  244. package/frontend/dist/modules/array.js +284 -0
  245. package/frontend/dist/modules/auth.d.ts +45 -0
  246. package/frontend/dist/modules/auth.js +138 -0
  247. package/frontend/dist/modules/colors.d.ts +26 -0
  248. package/frontend/dist/modules/colors.js +340 -0
  249. package/frontend/dist/modules/compression.d.ts +6 -0
  250. package/frontend/dist/modules/compression.js +999 -0
  251. package/frontend/dist/modules/cookies.d.ts +17 -0
  252. package/frontend/dist/modules/cookies.js +166 -0
  253. package/frontend/dist/modules/date.d.ts +142 -0
  254. package/frontend/dist/modules/date.js +493 -0
  255. package/frontend/dist/modules/events.d.ts +7 -0
  256. package/frontend/dist/modules/events.js +90 -0
  257. package/frontend/dist/modules/google.d.ts +10 -0
  258. package/frontend/dist/modules/google.js +53 -0
  259. package/frontend/dist/modules/meta.d.ts +9 -0
  260. package/frontend/dist/modules/meta.js +45 -0
  261. package/frontend/dist/modules/mutex.d.ts +8 -0
  262. package/frontend/dist/modules/mutex.js +52 -0
  263. package/frontend/dist/modules/number.d.ts +12 -0
  264. package/frontend/dist/modules/number.js +8 -0
  265. package/frontend/dist/modules/object.d.ts +50 -0
  266. package/frontend/dist/modules/object.js +147 -0
  267. package/frontend/dist/modules/paddle.d.ts +1403 -0
  268. package/frontend/dist/modules/paddle.js +2641 -0
  269. package/frontend/dist/modules/scheme.d.ts +207 -0
  270. package/frontend/dist/modules/scheme.js +649 -0
  271. package/frontend/dist/modules/settings.d.ts +3 -0
  272. package/frontend/dist/modules/settings.js +4 -0
  273. package/frontend/dist/modules/statics.d.ts +4 -0
  274. package/frontend/dist/modules/statics.js +45 -0
  275. package/frontend/dist/modules/string.d.ts +163 -0
  276. package/frontend/dist/modules/string.js +291 -0
  277. package/frontend/dist/modules/support.d.ts +18 -0
  278. package/frontend/dist/modules/support.js +102 -0
  279. package/frontend/dist/modules/themes.d.ts +8 -0
  280. package/frontend/dist/modules/themes.js +17 -0
  281. package/frontend/dist/modules/user.d.ts +58 -0
  282. package/frontend/dist/modules/user.js +279 -0
  283. package/frontend/dist/modules/utils.d.ts +58 -0
  284. package/frontend/dist/modules/utils.js +1159 -0
  285. package/frontend/dist/types/gradient.d.ts +12 -0
  286. package/frontend/dist/types/gradient.js +79 -0
  287. package/frontend/dist/ui/border_button.d.ts +177 -0
  288. package/frontend/dist/ui/border_button.js +235 -0
  289. package/frontend/dist/ui/button.d.ts +42 -0
  290. package/frontend/dist/ui/button.js +114 -0
  291. package/frontend/dist/ui/canvas.d.ts +56 -0
  292. package/frontend/dist/ui/canvas.js +411 -0
  293. package/frontend/dist/ui/checkbox.d.ts +72 -0
  294. package/frontend/dist/ui/checkbox.js +277 -0
  295. package/frontend/dist/ui/code.d.ts +232 -0
  296. package/frontend/dist/ui/code.js +977 -0
  297. package/frontend/dist/ui/color.d.ts +1 -0
  298. package/frontend/dist/ui/color.js +110 -0
  299. package/frontend/dist/ui/context_menu.d.ts +30 -0
  300. package/frontend/dist/ui/context_menu.js +211 -0
  301. package/frontend/dist/ui/css.d.ts +10 -0
  302. package/frontend/dist/ui/css.js +44 -0
  303. package/frontend/dist/ui/divider.d.ts +18 -0
  304. package/frontend/dist/ui/divider.js +82 -0
  305. package/frontend/dist/ui/dropdown.d.ts +115 -0
  306. package/frontend/dist/ui/dropdown.js +446 -0
  307. package/frontend/dist/ui/for_each.d.ts +38 -0
  308. package/frontend/dist/ui/for_each.js +97 -0
  309. package/frontend/dist/ui/form.d.ts +25 -0
  310. package/frontend/dist/ui/form.js +227 -0
  311. package/frontend/dist/ui/frame_modes.d.ts +28 -0
  312. package/frontend/dist/ui/frame_modes.js +116 -0
  313. package/frontend/dist/ui/google_map.d.ts +31 -0
  314. package/frontend/dist/ui/google_map.js +111 -0
  315. package/frontend/dist/ui/gradient.d.ts +24 -0
  316. package/frontend/dist/ui/gradient.js +115 -0
  317. package/frontend/dist/ui/image.d.ts +138 -0
  318. package/frontend/dist/ui/image.js +570 -0
  319. package/frontend/dist/ui/input.d.ts +316 -0
  320. package/frontend/dist/ui/input.js +1187 -0
  321. package/frontend/dist/ui/link.d.ts +39 -0
  322. package/frontend/dist/ui/link.js +146 -0
  323. package/frontend/dist/ui/list.d.ts +33 -0
  324. package/frontend/dist/ui/list.js +161 -0
  325. package/frontend/dist/ui/loader_button.d.ts +108 -0
  326. package/frontend/dist/ui/loader_button.js +207 -0
  327. package/frontend/dist/ui/loaders.d.ts +60 -0
  328. package/frontend/dist/ui/loaders.js +150 -0
  329. package/frontend/dist/ui/popup.d.ts +84 -0
  330. package/frontend/dist/ui/popup.js +331 -0
  331. package/frontend/dist/ui/pseudo.d.ts +16 -0
  332. package/frontend/dist/ui/pseudo.js +81 -0
  333. package/frontend/dist/ui/scroller.d.ts +131 -0
  334. package/frontend/dist/ui/scroller.js +1251 -0
  335. package/frontend/dist/ui/slider.d.ts +35 -0
  336. package/frontend/dist/ui/slider.js +203 -0
  337. package/frontend/dist/ui/spacer.d.ts +20 -0
  338. package/frontend/dist/ui/spacer.js +83 -0
  339. package/frontend/dist/ui/span.d.ts +11 -0
  340. package/frontend/dist/ui/span.js +75 -0
  341. package/frontend/dist/ui/stack.d.ts +123 -0
  342. package/frontend/dist/ui/stack.js +344 -0
  343. package/frontend/dist/ui/steps.d.ts +72 -0
  344. package/frontend/dist/ui/steps.js +306 -0
  345. package/frontend/dist/ui/style.d.ts +12 -0
  346. package/frontend/dist/ui/style.js +78 -0
  347. package/frontend/dist/ui/switch.d.ts +44 -0
  348. package/frontend/dist/ui/switch.js +280 -0
  349. package/frontend/dist/ui/table.d.ts +118 -0
  350. package/frontend/dist/ui/table.js +411 -0
  351. package/frontend/dist/ui/tabs.d.ts +85 -0
  352. package/frontend/dist/ui/tabs.js +392 -0
  353. package/frontend/dist/ui/text.d.ts +19 -0
  354. package/frontend/dist/ui/text.js +88 -0
  355. package/frontend/dist/ui/theme.d.ts +25 -0
  356. package/frontend/dist/ui/theme.js +237 -0
  357. package/frontend/dist/ui/title.d.ts +36 -0
  358. package/frontend/dist/ui/title.js +127 -0
  359. package/frontend/dist/ui/ui.d.ts +38 -0
  360. package/frontend/dist/ui/ui.js +41 -0
  361. package/frontend/dist/ui/view.d.ts +25 -0
  362. package/frontend/dist/ui/view.js +93 -0
  363. package/frontend/dist/volt.d.ts +22 -0
  364. package/frontend/dist/volt.js +27 -0
  365. package/frontend/exports.json +1340 -0
  366. package/frontend/src/css/adyen.css +92 -0
  367. package/frontend/src/css/volt.css +65 -0
  368. package/frontend/src/elements/base.ts +16790 -0
  369. package/frontend/src/elements/module.ts +184 -0
  370. package/frontend/src/elements/types.d.ts +155 -0
  371. package/frontend/src/modules/array.ts +366 -0
  372. package/frontend/src/modules/auth.ts +188 -0
  373. package/frontend/src/modules/colors.ts +449 -0
  374. package/frontend/src/modules/compression.ts +67 -0
  375. package/frontend/src/modules/cookies.ts +182 -0
  376. package/frontend/src/modules/date.js +535 -0
  377. package/frontend/src/modules/date.ts +583 -0
  378. package/frontend/src/modules/events.ts +96 -0
  379. package/frontend/src/modules/google.ts +60 -0
  380. package/frontend/src/modules/meta.ts +59 -0
  381. package/frontend/src/modules/mutex.ts +59 -0
  382. package/frontend/src/modules/number.ts +20 -0
  383. package/frontend/src/modules/object.ts +212 -0
  384. package/frontend/src/modules/paddle.ts +2990 -0
  385. package/frontend/src/modules/scheme.ts +740 -0
  386. package/frontend/src/modules/settings.ts +5 -0
  387. package/frontend/src/modules/statics.ts +47 -0
  388. package/frontend/src/modules/string.ts +500 -0
  389. package/frontend/src/modules/support.ts +118 -0
  390. package/frontend/src/modules/themes.ts +24 -0
  391. package/frontend/src/modules/user.ts +321 -0
  392. package/frontend/src/modules/utils.ts +1260 -0
  393. package/frontend/src/static/admin/admin.png +0 -0
  394. package/frontend/src/static/admin/password.webp +0 -0
  395. package/frontend/src/static/icons/copy.webp +0 -0
  396. package/frontend/src/static/payments/arrow.long.webp +0 -0
  397. package/frontend/src/static/payments/arrow.long2.webp +0 -0
  398. package/frontend/src/static/payments/cancelled.webp +0 -0
  399. package/frontend/src/static/payments/check.sign.webp +0 -0
  400. package/frontend/src/static/payments/check.webp +0 -0
  401. package/frontend/src/static/payments/close.webp +0 -0
  402. package/frontend/src/static/payments/error.webp +0 -0
  403. package/frontend/src/static/payments/exclamation.webp +0 -0
  404. package/frontend/src/static/payments/minus.webp +0 -0
  405. package/frontend/src/static/payments/party.webp +0 -0
  406. package/frontend/src/static/payments/plus.webp +0 -0
  407. package/frontend/src/static/payments/shopping_cart.webp +0 -0
  408. package/frontend/src/static/payments/trash.webp +0 -0
  409. package/frontend/src/types/global.d.ts +4 -0
  410. package/frontend/src/types/gradient.ts +87 -0
  411. package/frontend/src/ui/any_element.d.ts +5 -0
  412. package/frontend/src/ui/border_button.ts +320 -0
  413. package/frontend/src/ui/button.ts +62 -0
  414. package/frontend/src/ui/canvas.ts +431 -0
  415. package/frontend/src/ui/checkbox.ts +284 -0
  416. package/frontend/src/ui/code.ts +1049 -0
  417. package/frontend/src/ui/color.ts +117 -0
  418. package/frontend/src/ui/context_menu.ts +194 -0
  419. package/frontend/src/ui/css.ts +57 -0
  420. package/frontend/src/ui/divider.ts +28 -0
  421. package/frontend/src/ui/dropdown.ts +503 -0
  422. package/frontend/src/ui/for_each.ts +71 -0
  423. package/frontend/src/ui/form.ts +208 -0
  424. package/frontend/src/ui/frame_modes.ts +140 -0
  425. package/frontend/src/ui/google_map.ts +70 -0
  426. package/frontend/src/ui/gradient.ts +73 -0
  427. package/frontend/src/ui/image.ts +587 -0
  428. package/frontend/src/ui/input.ts +1284 -0
  429. package/frontend/src/ui/link.ts +77 -0
  430. package/frontend/src/ui/list.ts +88 -0
  431. package/frontend/src/ui/loader_button.ts +192 -0
  432. package/frontend/src/ui/loaders.ts +126 -0
  433. package/frontend/src/ui/popup.ts +370 -0
  434. package/frontend/src/ui/pseudo.ts +33 -0
  435. package/frontend/src/ui/scroller.ts +1324 -0
  436. package/frontend/src/ui/slider.ts +215 -0
  437. package/frontend/src/ui/spacer.ts +29 -0
  438. package/frontend/src/ui/span.ts +23 -0
  439. package/frontend/src/ui/stack.ts +238 -0
  440. package/frontend/src/ui/steps.ts +334 -0
  441. package/frontend/src/ui/style.ts +26 -0
  442. package/frontend/src/ui/switch.ts +286 -0
  443. package/frontend/src/ui/table.ts +323 -0
  444. package/frontend/src/ui/tabs.ts +441 -0
  445. package/frontend/src/ui/text.ts +38 -0
  446. package/frontend/src/ui/theme.ts +279 -0
  447. package/frontend/src/ui/title.ts +64 -0
  448. package/frontend/src/ui/ui.ts +47 -0
  449. package/frontend/src/ui/view.ts +44 -0
  450. package/frontend/src/volt.ts +31 -0
  451. package/package.json +58 -0
@@ -0,0 +1,3475 @@
1
+ "use strict";
2
+ /*
3
+ * Author: Daan van den Bergh
4
+ * Copyright: © 2022 - 2024 Daan van den Bergh.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.Server = void 0;
44
+ // ---------------------------------------------------------
45
+ // Libraries.
46
+ const http = __importStar(require("http"));
47
+ const http2 = __importStar(require("http2"));
48
+ const crypto = __importStar(require("crypto"));
49
+ const nodemailer = __importStar(require("nodemailer"));
50
+ // import * as libcluster from 'cluster';
51
+ const cluster_1 = __importDefault(require("cluster"));
52
+ const os = __importStar(require("os"));
53
+ // ---------------------------------------------------------
54
+ // Imports.
55
+ const _vinc_1 = require("./vinc.js");
56
+ const _vinc_2 = require("./vinc.js");
57
+ const utils_js_1 = require("./utils.js");
58
+ const meta_js_1 = require("./meta.js");
59
+ const Mail = __importStar(require("./plugins/mail.js"));
60
+ const status_js_1 = require("./status.js");
61
+ const endpoint_js_1 = require("./endpoint.js");
62
+ const image_endpoint_js_1 = require("./image_endpoint.js");
63
+ const stream_js_1 = require("./stream.js");
64
+ const database_js_1 = require("./database.js");
65
+ const file_watcher_js_1 = require("./file_watcher.js");
66
+ const users_js_1 = require("./users.js");
67
+ const paddle_js_1 = require("./payments/paddle.js");
68
+ const rate_limit_js_1 = require("./rate_limit.js");
69
+ const logger_js_1 = __importStar(require("./logger.js"));
70
+ const TSCompiler = __importStar(require("./plugins/ts/compiler.js"));
71
+ const TSPreprocessing = __importStar(require("./plugins/ts/preprocessing.js"));
72
+ const browser_js_1 = require("./plugins/browser.js");
73
+ const log_source = new logger_js_1.LogSource("Server");
74
+ const url_1 = require("url");
75
+ const path_1 = require("path");
76
+ var __dirname = typeof __dirname !== 'undefined' ? __dirname : (0, path_1.dirname)((0, url_1.fileURLToPath)(new URL('./package.json', import.meta.url)));
77
+ const thread_monitor_js_1 = require("./plugins/thread_monitor.js");
78
+ const thread_monitor = new thread_monitor_js_1.ThreadMonitor();
79
+ thread_monitor.start();
80
+ // ---------------------------------------------------------
81
+ // The server object.
82
+ // @todo redirect to https on http also important for seo.
83
+ // @todo convert throw new Error to frontend errors.
84
+ // @todo figure out with what settings nodejs should be started for heavy servers, for example larger memory size `--max-old-space-size`
85
+ // @todo implement usage of multiple cpu's using lib `cluster`.
86
+ // @todo when rendering pages the user could use a special OptimizeText() function which will be optimized for copy writing and seo when adding loading static files. Quite hard but would be sublime (writesonic is a good platform).
87
+ /* @docs:
88
+ @nav: Backend
89
+ @chapter: Server
90
+ @title: Server
91
+ @description:
92
+ The backend server class.
93
+ When the https parameters `certificate` and `private_key` are defined, the server will run automatically on http and https.
94
+ @parameter:
95
+ @name: production
96
+ @description: Whether the server is in production more, or in development mode.
97
+ @type: boolean
98
+ @required: true
99
+ @parameter:
100
+ @name: ip
101
+ @description: The ip where the server will run on.
102
+ @type: string
103
+ @required: true
104
+ @parameter:
105
+ @name: port
106
+ @description: The port where the server will run on. Leave the port `null` to run on port `80` for http and on port `443` for https.
107
+ @type: number
108
+ @parameter:
109
+ @name: tls
110
+ @description: The tls settings for HTTPS.
111
+ @type: object
112
+ @attribute:
113
+ @name: cert
114
+ @description: The path to the certificate.
115
+ @type: string
116
+ @attribute:
117
+ @name: key
118
+ @description: The path to the private key file.
119
+ @type: string
120
+ @attribute:
121
+ @name: ca
122
+ @description: The path to the ca bundle file.
123
+ @type: null, string
124
+ @attribute:
125
+ @name: passphrase
126
+ @description: The passphrase of the private key.
127
+ @type: string
128
+ @parameter:
129
+ @name: domain
130
+ @description: The full domain url without `http://` or `https://`.
131
+ @type: string
132
+ @required: true
133
+ @parameter:
134
+ @name: source
135
+ @description: The path to the source directory of your website. This may either be the source directory of your code, or the source directory where files will be stored for your website.
136
+ @type: string
137
+ @required: true
138
+ @parameter:
139
+ @name: is_primary
140
+ @description: Used to indicate if the current server is the primary node.
141
+ @type: string
142
+ @required: true
143
+ @parameter:
144
+ @name: statics
145
+ @description: Array with paths to static directories or static directory objects.
146
+ @type: string[], vlib.Path[], StaticDirectory
147
+ @required: true
148
+ @attributes_type: StaticDirectory
149
+ @attr:
150
+ @name: path
151
+ @descr: The path to the static directory or file.
152
+ @required: true
153
+ @attr:
154
+ @name: endpoint
155
+ @descr: The base endpoint of the static directory, by default the path's name will be used.
156
+ @required: false
157
+ @attr:
158
+ @name: cache
159
+ @descr: Enable caching for the static endpoints, this value will be used for parameter `Endpoint.cache`.
160
+ @type: boolean | number
161
+ @default: true
162
+ @required: false
163
+ @attr:
164
+ @name: endpoints_cache
165
+ @descr: This attribute can be used to define a specific cache policy per endpoint from this static directory. Must be formatted as `{<endpoint>: <cache>}`, the cache value will be used for parameter `Endpoint.cache`.
166
+ @default: {}
167
+ @required: false
168
+ @attr:
169
+ @name: exclude
170
+ @descr: An array of paths to exlude. The array may contain regexes.
171
+ @default: {}
172
+ @required: false
173
+ @parameter:
174
+ @name: database
175
+ @description:
176
+ The mongodb database settings.
177
+
178
+ The parameter can be defined as a `string` type as the database uri, or as an object with parameters for the <Link #Database>Database</Link> object.
179
+
180
+ When parameter `Server.is_primary` is defined as `false`, the database should always be defined as the database uri `string`. Since the secondary node will connect with the primary node.
181
+ @type: string, object, boolean
182
+ @required: true
183
+ @parameter:
184
+ @name: default_headers
185
+ @description: Used to override the default headers generated by volt. Leave parameter `default_headers` as `null` to let volt automatically generate the default headers.
186
+ @type: object
187
+ @parameter:
188
+ @name: favicon
189
+ @description: The path to the favicon.
190
+ @type: string
191
+ @parameter:
192
+ @name: token_expiration
193
+ @description: The token a sign in token will be valid in seconds.
194
+ @type: number
195
+ @parameter:
196
+ @name: enable_2fa
197
+ @description: Enable 2fa for user authentication.
198
+ @type: boolean
199
+ @required: true
200
+ @parameter:
201
+ @name: enable_account_activation
202
+ @description: Enable account activation by email after a user signs up.
203
+ @type: boolean
204
+ @required: true
205
+ @parameter:
206
+ @name: meta
207
+ @description: The default meta object.
208
+ @type: object, volt.Meta
209
+ @parameter:
210
+ @name: company
211
+ @type: object
212
+ @description: Your company information.
213
+ @attribute:
214
+ @name: name
215
+ @type: string
216
+ @required: true
217
+ @description: The name of your company.
218
+ @attribute:
219
+ @name: legal_name
220
+ @type: string
221
+ @required: true
222
+ @description: The legal name of your company.
223
+ @attribute:
224
+ @name: street
225
+ @type: string
226
+ @required: true
227
+ @description: The street name of your company's address.
228
+ @attribute:
229
+ @name: house_number
230
+ @type: string
231
+ @required: true
232
+ @description: The house number or house name of your company's address.
233
+ @attribute:
234
+ @name: postal_code
235
+ @type: string
236
+ @required: true
237
+ @description: The postal code or zip code of your company's address.
238
+ @attribute:
239
+ @name: city
240
+ @type: string
241
+ @required: true
242
+ @description: The city of your company's address.
243
+ @attribute:
244
+ @name: province
245
+ @type: string
246
+ @required: true
247
+ @description: The province or state of your company's address.
248
+ @attribute:
249
+ @name: country
250
+ @type: string
251
+ @required: true
252
+ @description: The country name of your company's address.
253
+ @attribute:
254
+ @name: country_code
255
+ @type: string
256
+ @required: true
257
+ @description: The two-letter ISO country code of your company's location.
258
+ @attribute:
259
+ @name: tax_id
260
+ @type: string
261
+ @required: false
262
+ @description: The tax id of your company.
263
+ @attribute:
264
+ @name: type
265
+ @type: string
266
+ @description: The type of company.
267
+ @attribute:
268
+ @name: icon
269
+ @type: string
270
+ @required: true
271
+ @description: The endpoint url path of your company's icon, png format. This must be an endpoint url since access to the file path is also required for creating invoices.
272
+ @attribute:
273
+ @name: stroke_icon
274
+ @type: string
275
+ @required: true
276
+ @description: The endpoint url path of your company's stroke icon, png format. In payment invoices the stroke icon precedes the default icon. This must be an endpoint url since access to the file path is also required for creating invoices.
277
+ @parameter:
278
+ @name: smtp
279
+ @description:
280
+ The smpt arguments object.
281
+ More information about the arguments can be found at the nodemailer <Link https://nodemailer.com/smtp/>documentation</Link>.
282
+ @type: object
283
+ @attribute:
284
+ @name: sender
285
+ @description:
286
+ The smtp sender address may either be a string with the email address, e.g. `your@email.com`.
287
+ Or an array with the sender name and email address, e.g. `["Sender", "your@email.com"]`.
288
+ @type: string, array
289
+ @attribute:
290
+ @name: host
291
+ @description: The mail server's host address.
292
+ @type: string
293
+ @attribute:
294
+ @name: port
295
+ @description: The mail server's port.
296
+ @type: number
297
+ @attribute:
298
+ @name: secure
299
+ @description: Enable secure options.
300
+ @type: boolean
301
+ @attr:
302
+ @name: auth
303
+ @description: The authentication settings.
304
+ @type: object
305
+ @attribute:
306
+ @name: user
307
+ @description: The email used for authentication.
308
+ @type: string
309
+ @attribute:
310
+ @name: pass
311
+ @description: The password used for authentication.
312
+ @type: string
313
+ @parameter:
314
+ @name: payments
315
+ @type: object
316
+ @description: The arguments for the payment class. The `type` attribute is used to indicate the payment provider, the other attributes are arguments for the payment class <Link #Paddle>Paddle</Link>.
317
+ @attribute:
318
+ @name: type
319
+ @type: string
320
+ @description: The payment provider name.
321
+ @required: true
322
+ @enum:
323
+ @value: paddle
324
+ @desc: Payment provider Paddle.
325
+ @parameter:
326
+ @name: google_tag
327
+ @description: The google tag id.
328
+ @type: string
329
+ @parameter:
330
+ @name: file_watcher
331
+ @description: The file watcher arguments, define to enable file watching. The parameter may either be an FileWatcher object, an object with arguments. The process argument `--no-file-watcher` or environment variable `VOLT_NO_FILE_WATCHER="1"` can always be used to (temporarily) disable the file watcher.
332
+ @type: FileWatcher, object
333
+ @parameter:
334
+ @name: mail_style
335
+ @description: The mail settings to customize automatically generated mails.
336
+ @type: object
337
+ @attribute:
338
+ @name: font
339
+ @description: The font family.
340
+ @type: string
341
+ @attribute:
342
+ @name: button_bg
343
+ @description: The background color of the button's in your mails.
344
+ @type: string
345
+ @parameter:
346
+ @name: offline
347
+ @description: Boolean indicating if the development server is being run offline.
348
+ @type: boolean
349
+ @parameter:
350
+ @name: multiprocessing
351
+ @description: Enable multiprocessing when in production mode.
352
+ @type: boolean
353
+ @def: true
354
+ @parameter:
355
+ @name: processes
356
+ @description: The number of processes when multiprocessing is enabled. By default the number of CPU's will be used for the amount of processes.
357
+ @type: null, number
358
+ @def: null
359
+ @parameter:
360
+ @name: rate_limit
361
+ @description:
362
+ The rate limit server and client settings. Rate limiting works with a centralizer websocket server and secondary clients.
363
+ @type: object
364
+ @required: false
365
+ @attribute:
366
+ @name: server
367
+ @type: object
368
+ @description:
369
+ The server configuration.
370
+
371
+ By default the primary server instance will start the rate limit service.
372
+
373
+ However, when parameter `rate_limit.server` is `false`. All rate limit instances will use a client to connect to an already running rate limit instance. If so, you must manually set up this rate limt server.
374
+ @attribute:
375
+ @name: ip
376
+ @description:
377
+ The ip to which the rate limiting server will bind. By default the rate limit server will run on localhost only.
378
+ @type: null, string
379
+ @attribute:
380
+ @name: port
381
+ @description:
382
+ The port to which the rate limiting server will bind. The default port is `51234`.
383
+ @type: number
384
+ @def: 51234
385
+ @attribute:
386
+ @name: https
387
+ @description:
388
+ To enable https on the server you must define a `https.createServer` configuration. Otherwise, the rate limit server will run on http.
389
+ @type: null, object
390
+ @attribute:
391
+ @name: client
392
+ @description:
393
+ The client configuration.
394
+ @attribute:
395
+ @name: ip
396
+ @description:
397
+ The ip address of the primary node with the rate limiting server. The primary node is indicated by the `Server.is_primary` parameter.
398
+
399
+ When `Server.is_primary` is true, the rate limiting server will listen on the private ip address of your current machine.
400
+ @type: null, string
401
+ @attribute:
402
+ @name: port
403
+ @description:
404
+ The port of the primary node with the rate limiting server. The default port is `51234`.
405
+ @type: number
406
+ @def: 51234
407
+ @attribute:
408
+ @name: url
409
+ @description:
410
+ The full websocket url of the server. If defined this takes precedence over parameters `ip` and `port`.
411
+
412
+ This can be useful when `rate_limit.server` is `false`.
413
+ @type: null, string
414
+ @parameter:
415
+ @name: keys
416
+ @description:
417
+ The array with names of crypto keys. The keys will be generated and stored in the database when they do not exist. The keys will be accessable as `Server.keys.$name`.
418
+
419
+ The array items may be a string representing the name of the key, or an object containing the name and the length of the key.
420
+ @type: array[string], array[object]
421
+ @required: false
422
+ @attribute:
423
+ @name: name
424
+ @description:
425
+ The name of the key.
426
+ @type: string
427
+ @attribute:
428
+ @name: length
429
+ @description:
430
+ The length of the key.
431
+ @type: number
432
+ @parameter:
433
+ @name: additional_sitemap_endpoints
434
+ @description:
435
+ An array with additional endpoints that will be added to the sitemap. By default all endpoints where attribute `view` is defined will be added the sitemap.
436
+ @type: array[string]
437
+ @parameter:
438
+ @name: daemon
439
+ @description:
440
+ The optional settings for the service daemons. The service daemons can be disabled by passing value `false` to parameter `daemon`.
441
+
442
+ By default this settings will also partially be used for the database service daemon.
443
+ @type: object
444
+ @attr:
445
+ @name: user
446
+ @desc: The executing user of the service daemon.
447
+ @type: string
448
+ @attr:
449
+ @name: group
450
+ @desc: The executing group of the service daemon.
451
+ @type: string
452
+ @attr:
453
+ @name: args
454
+ @desc: The arguments for the start command.
455
+ @type: array[string]
456
+ @attr:
457
+ @name: env
458
+ @desc: The environment variables for the service daemon.
459
+ @type: object
460
+ @attr:
461
+ @name: description
462
+ @desc: The description of the service daemon.
463
+ @type: string
464
+ @attr:
465
+ @name: logs
466
+ @desc: The path to the log file.
467
+ @type: string
468
+ @attr:
469
+ @name: errors
470
+ @desc: The path to the error log file.
471
+ @type: string
472
+ @parameter:
473
+ @name: admin
474
+ @description:
475
+ Administrator settings used for protected administrator endpoints.
476
+ @type: object
477
+ @attr:
478
+ @name: password
479
+ @desc: The password used for administrator endpoints.
480
+ @type: string
481
+ @attr:
482
+ @name: ips
483
+ @desc: IP addresses used by the website administrator. These ip's will be used to create a whitelist for administrator endpoints.
484
+ @type: string[]
485
+ @parameter:
486
+ @name: ts
487
+ @description:
488
+ Specify typescript options.
489
+ @type: object
490
+ @attr:
491
+ @name: output
492
+ @desc: The output directory for typescript endpoint source files.
493
+ @type: string
494
+ @attr:
495
+ @name: compiler_options
496
+ @desc: The compiler options for the typescript files.
497
+ @type: object
498
+ @required: false
499
+
500
+ @attribute:
501
+ @name: users
502
+ @type: object
503
+ @attribute:
504
+ @name: public
505
+ @type: UIDCollection
506
+ @desc:
507
+ The database collection for public data of users.
508
+
509
+ More information about the collection's functions can be found at <Type>UIDCollection</Type>
510
+ @warning:
511
+ The authenticated user always has read and write access to all data inside the user's protected directory through the backend rest api. Any other users or unauthenticated users do not have access to this data.
512
+ @attribute:
513
+ @name: protected
514
+ @type: UIDCollection
515
+ @desc:
516
+ The database collection for public data of users.
517
+
518
+ More information about the collection's functions can be found at <Type>UIDCollection</Type>
519
+ @warning:
520
+ The authenticated user always has read access to all data inside the user's protected directory through the backend rest api. Any other users or unauthenticated users do not have access to this data.
521
+ @attribute:
522
+ @name: private
523
+ @type: UIDCollection
524
+ @desc:
525
+ The database collection for public data of users.
526
+
527
+ More information about the collection's functions can be found at <Type>UIDCollection</Type>
528
+ @note:
529
+ The user has no read or write access to the private directory.
530
+ @attribute:
531
+ @name: storage
532
+ @type: Collection
533
+ @desc:
534
+ The database storage collection for the website's system backend data.
535
+
536
+ More information about the collection's functions can be found at <Type>Collection</Type>
537
+
538
+ */
539
+ // @tdo implement 3D secure "requires_action" status for a refund and payment intent.
540
+ // https://stripe.com/docs/payments/3d-secure
541
+ // @ts-ignore
542
+ class Server {
543
+ // Static attributes.
544
+ static content_type_mimes = [
545
+ [".html", "text/html"],
546
+ [".htm", "text/html"],
547
+ [".shtml", "text/html"],
548
+ [".css", "text/css"],
549
+ [".xml", "application/xml"],
550
+ [".gif", "image/gif"],
551
+ [".jpeg", "image/jpeg"],
552
+ [".jpg", "image/jpeg"],
553
+ [".js", "application/javascript"],
554
+ [".atom", "application/atom+xml"],
555
+ [".rss", "application/rss+xml"],
556
+ [".mml", "text/mathml"],
557
+ [".txt", "text/plain"],
558
+ [".jad", "text/vnd.sun.j2me.app-descriptor"],
559
+ [".wml", "text/vnd.wap.wml"],
560
+ [".htc", "text/x-component"],
561
+ [".png", "image/png"],
562
+ [".tif", "image/tiff"],
563
+ [".tiff", "image/tiff"],
564
+ [".wbmp", "image/vnd.wap.wbmp"],
565
+ [".ico", "image/x-icon"],
566
+ [".jng", "image/x-jng"],
567
+ [".bmp", "image/x-ms-bmp"],
568
+ [".svg", "image/svg+xml"],
569
+ [".svgz", "image/svg+xml"],
570
+ [".webp", "image/webp"],
571
+ [".woff", "font/woff"],
572
+ [".woff2", "font/woff2"],
573
+ [".jar", "application/java-archive"],
574
+ [".war", "application/java-archive"],
575
+ [".ear", "application/java-archive"],
576
+ [".json", "application/json"],
577
+ [".hqx", "application/mac-binhex40"],
578
+ [".doc", "application/msword"],
579
+ [".pdf", "application/pdf"],
580
+ [".ps", "application/postscript"],
581
+ [".eps", "application/postscript"],
582
+ [".ai", "application/postscript"],
583
+ [".rtf", "application/rtf"],
584
+ [".m3u8", "application/vnd.apple.mpegurl"],
585
+ [".xls", "application/vnd.ms-excel"],
586
+ [".eot", "application/vnd.ms-fontobject"],
587
+ [".ppt", "application/vnd.ms-powerpoint"],
588
+ [".wmlc", "application/vnd.wap.wmlc"],
589
+ [".kml", "application/vnd.google-earth.kml+xml"],
590
+ [".kmz", "application/vnd.google-earth.kmz"],
591
+ [".7z", "application/x-7z-compressed"],
592
+ [".cco", "application/x-cocoa"],
593
+ [".jardiff", "application/x-java-archive-diff"],
594
+ [".jnlp", "application/x-java-jnlp-file"],
595
+ [".run", "application/x-makeself"],
596
+ [".pl", "application/x-perl"],
597
+ [".pm", "application/x-perl"],
598
+ [".prc", "application/x-pilot"],
599
+ [".pdb", "application/x-pilot"],
600
+ [".rar", "application/x-rar-compressed"],
601
+ [".rpm", "application/x-redhat-package-manager"],
602
+ [".sea", "application/x-sea"],
603
+ [".swf", "application/x-shockwave-flash"],
604
+ [".sit", "application/x-stuffit"],
605
+ [".tcl", "application/x-tcl"],
606
+ [".tk", "application/x-tcl"],
607
+ [".der", "application/x-x509-ca-cert"],
608
+ [".pem", "application/x-x509-ca-cert"],
609
+ [".crt", "application/x-x509-ca-cert"],
610
+ [".xpi", "application/x-xpinstall"],
611
+ [".xhtml", "application/xhtml+xml"],
612
+ [".xspf", "application/xspf+xml"],
613
+ [".zip", "application/zip"],
614
+ [".bin", "application/octet-stream"],
615
+ [".exe", "application/octet-stream"],
616
+ [".dll", "application/octet-stream"],
617
+ [".deb", "application/octet-stream"],
618
+ [".dmg", "application/octet-stream"],
619
+ [".iso", "application/octet-stream"],
620
+ [".img", "application/octet-stream"],
621
+ [".msi", "application/octet-stream"],
622
+ [".msp", "application/octet-stream"],
623
+ [".msm", "application/octet-stream"],
624
+ [".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
625
+ [".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
626
+ [".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"],
627
+ [".mid", "audio/midi"],
628
+ [".midi", "audio/midi"],
629
+ [".kar", "audio/midi"],
630
+ [".mp3", "audio/mpeg"],
631
+ [".ogg", "audio/ogg"],
632
+ [".m4a", "audio/x-m4a"],
633
+ [".ra", "audio/x-realaudio"],
634
+ [".3gpp", "video/3gpp"],
635
+ [".3gp", "video/3gpp"],
636
+ [".ts", "video/mp2t"],
637
+ [".mp4", "video/mp4"],
638
+ [".mpeg", "video/mpeg"],
639
+ [".mpg", "video/mpeg"],
640
+ [".mov", "video/quicktime"],
641
+ [".webm", "video/webm"],
642
+ [".flv", "video/x-flv"],
643
+ [".m4v", "video/x-m4v"],
644
+ [".mng", "video/x-mng"],
645
+ [".asx", "video/x-ms-asf"],
646
+ [".asf", "video/x-ms-asf"],
647
+ [".wmv", "video/x-ms-wmv"],
648
+ [".avi", "video/x-msvideo"],
649
+ ];
650
+ log;
651
+ error;
652
+ static compressed_extensions = [
653
+ ".png",
654
+ ".jpg",
655
+ ".jpeg",
656
+ ".gif",
657
+ ".webp",
658
+ ".bmp",
659
+ ".tiff",
660
+ ".ico",
661
+ ".svg",
662
+ ".svgz",
663
+ ".mng",
664
+ ".apng",
665
+ ".jfif",
666
+ ".jp2",
667
+ ".jpx",
668
+ ".j2k",
669
+ ".jpm",
670
+ ".jpf",
671
+ ".heif",
672
+ ".mp3",
673
+ ".ogg",
674
+ ".wav",
675
+ ".flac",
676
+ ".m4a",
677
+ ".aac",
678
+ ".wma",
679
+ ".ra",
680
+ ".mid",
681
+ ".mp4",
682
+ ".webm",
683
+ ".mkv",
684
+ ".mov",
685
+ ".avi",
686
+ ".wmv",
687
+ ".mpg",
688
+ ".mpeg",
689
+ ".flv",
690
+ ];
691
+ // Instance properties
692
+ ip;
693
+ port;
694
+ domain;
695
+ full_domain;
696
+ source; // vlib.Path type
697
+ is_primary;
698
+ statics;
699
+ statics_aspect_ratios;
700
+ favicon;
701
+ enable_2fa;
702
+ enable_account_activation;
703
+ token_expiration;
704
+ google_tag;
705
+ production;
706
+ multiprocessing;
707
+ processes;
708
+ company;
709
+ meta;
710
+ mail_style;
711
+ online;
712
+ offline;
713
+ // private honey_pot_key: string | null;
714
+ _keys;
715
+ additional_sitemap_endpoints;
716
+ log_level;
717
+ tls;
718
+ file_watcher;
719
+ admin;
720
+ ts;
721
+ performance;
722
+ csp;
723
+ default_headers;
724
+ http;
725
+ https;
726
+ endpoints;
727
+ err_endpoints;
728
+ db;
729
+ _sys_db; // needs to be public for the RateLimit classes.
730
+ storage;
731
+ smtp;
732
+ smtp_sender;
733
+ rate_limit;
734
+ blacklist;
735
+ _hash_key = null;
736
+ keys;
737
+ _on_start;
738
+ _on_stop;
739
+ browser_preview;
740
+ static_file_watcher;
741
+ is_file_watcher;
742
+ daemon;
743
+ _stop_tscompiler_watcher;
744
+ users;
745
+ payments;
746
+ status;
747
+ rate_limits;
748
+ logger;
749
+ constructor({ ip = "127.0.0.1", port = 8000, domain, is_primary = true, source, database = "mongodb://localhost:27017/main", statics = [], favicon = undefined, company, meta = new meta_js_1.Meta(), tls = undefined, smtp = undefined, mail_style = {
750
+ font: '"Helvetica", sans-serif',
751
+ title_fg: "#121B23",
752
+ subtitle_fg: "#121B23",
753
+ text_fg: "#1F2F3D",
754
+ button_fg: "#FFFFFF",
755
+ footer_fg: "#686B80",
756
+ bg: "#EEEEEE",
757
+ widget_bg: "#FFFFFF",
758
+ widget_border: "#E6E6E6",
759
+ button_bg: "#1F2F3D",
760
+ divider_bg: "#706780",
761
+ }, rate_limit = {
762
+ server: {
763
+ ip: null,
764
+ port: rate_limit_js_1.RateLimitServer.default_port,
765
+ https: null,
766
+ },
767
+ client: {
768
+ ip: null,
769
+ port: rate_limit_js_1.RateLimitServer.default_port,
770
+ url: null,
771
+ },
772
+ }, keys = [], payments = null, default_headers = null, google_tag = undefined, token_expiration = 86400, enable_2fa = false, enable_account_activation = true,
773
+ // honey_pot_key = null,
774
+ production = false, multiprocessing = true, processes = null, file_watcher = {}, offline = false, additional_sitemap_endpoints = [], log_level = 0, daemon = {}, admin = {
775
+ password: null,
776
+ ips: [],
777
+ }, ts = {
778
+ compiler_opts: {},
779
+ output: undefined,
780
+ }, browser_preview = undefined, }) {
781
+ // @debug
782
+ // Async hook for tracking active processes during stop().
783
+ // const async_resource_map = new Map();
784
+ // this.async_hook = require('async_hooks').createHook({
785
+ // init(async_id, type, trigger_async_id, resource) {
786
+ // const ignoredTypes = ['TickObject', 'PROMISE'];
787
+ // if (ignoredTypes.includes(type)) {
788
+ // return; // Skip logging for these async types
789
+ // }
790
+ // // Capture the stack trace of the function that initiated the async operation
791
+ // const stack = new Error("SKIPAFTER").stack.split("SKIPAFTER")[1].trim();
792
+ // // Log async_id and initiating function call stack
793
+ // // console.log(`Init async_id: ${async_id}`)
794
+ // // console.log(`Init async_id: ${async_id}, type: ${type}, trigger: ${trigger_async_id}\nStack: ${stack}`);
795
+ // // Store the async resource and stack trace
796
+ // async_resource_map.set(async_id, { type, stack });
797
+ // },
798
+ // destroy(async_id) {
799
+ // // When an async resource is destroyed, remove it from the map
800
+ // // console.log(`Destroy async_id: ${async_id}`);
801
+ // async_resource_map.delete(async_id);
802
+ // },
803
+ // // before(async_id) {
804
+ // // console.log(`Before async_id: ${async_id}`);
805
+ // // },
806
+ // // after(async_id) {
807
+ // // console.log(`After async_id: ${async_id}`);
808
+ // // },
809
+ // })
810
+ // this.async_hook.resource_map = async_resource_map;
811
+ // this.async_hook.enable();
812
+ // Verify args.
813
+ _vinc_1.vlib.Scheme.verify({ object: arguments[0], err_prefix: "Server: ", check_unknown: true, scheme: {
814
+ ip: "string",
815
+ port: "number",
816
+ domain: "string",
817
+ statics: { type: "array", default: [] },
818
+ is_primary: { type: "boolean", default: true },
819
+ source: "string",
820
+ database: {
821
+ type: ["string", "object", "boolean"],
822
+ required: false,
823
+ scheme: { ...database_js_1.Database.constructor_scheme, _server: undefined },
824
+ },
825
+ favicon: { type: "string", required: false },
826
+ // honey_pot_key: {type: "string", default: null},
827
+ company: {
828
+ type: "object",
829
+ default: {},
830
+ scheme: {
831
+ name: "string",
832
+ legal_name: "string",
833
+ street: "string",
834
+ house_number: "string",
835
+ postal_code: "string",
836
+ city: "string",
837
+ province: "string",
838
+ country: "string",
839
+ country_code: "string",
840
+ tax_id: { type: "string", default: null },
841
+ icon: { type: "string", default: null },
842
+ icon_path: { type: "string", default: null },
843
+ stroke_icon: { type: "string", default: null },
844
+ stroke_icon_path: { type: "string", default: null },
845
+ }
846
+ },
847
+ meta: { type: "object", required: false },
848
+ tls: {
849
+ type: ["object"],
850
+ required: false,
851
+ scheme: {
852
+ cert: "string",
853
+ key: "string",
854
+ ca: { type: "string", default: null },
855
+ passphrase: { type: "string", default: null },
856
+ }
857
+ },
858
+ rate_limit: {
859
+ type: "object",
860
+ default: {
861
+ ip: null,
862
+ port: rate_limit_js_1.RateLimitServer.default_port,
863
+ },
864
+ scheme: {
865
+ server: { type: "object", default: {}, scheme: {
866
+ ip: { type: "string", default: null },
867
+ port: { type: "number", default: rate_limit_js_1.RateLimitServer.default_port },
868
+ https: { type: "object", default: null },
869
+ } },
870
+ client: { type: "object", default: {}, scheme: {
871
+ ip: { type: "string", default: null },
872
+ port: { type: "number", default: rate_limit_js_1.RateLimitServer.default_port },
873
+ url: { type: "string", default: null },
874
+ } },
875
+ },
876
+ },
877
+ keys: { type: "array", default: [] },
878
+ smtp: { type: ["null", "object"], required: false },
879
+ mail_style: {
880
+ type: "object",
881
+ required: false,
882
+ scheme: {
883
+ font: { type: "string", default: '"Helvetica", sans-serif' },
884
+ title_fg: { type: "string", default: "#121B23" },
885
+ subtitle_fg: { type: "string", default: "#121B23" },
886
+ text_fg: { type: "string", default: "#1F2F3D" },
887
+ button_fg: { type: "string", default: "#FFFFFF" },
888
+ footer_fg: { type: "string", default: "#686B80" },
889
+ bg: { type: "string", default: "#EEEEEE" },
890
+ widget_bg: { type: "string", default: "#FFFFFF" },
891
+ button_bg: { type: "string", default: "#421959" },
892
+ widget_border: { type: "string", default: "#E6E6E6" },
893
+ divider_bg: { type: "string", default: "#E6E6E6" },
894
+ }
895
+ },
896
+ payments: { type: ["null", "object"], required: false },
897
+ default_headers: { type: ["null", "object"], required: false },
898
+ google_tag: { type: "string", required: false },
899
+ token_expiration: { type: "number", required: false },
900
+ enable_2fa: { type: "boolean", required: false },
901
+ enable_account_activation: { type: "boolean", required: false },
902
+ production: { type: "boolean", required: false },
903
+ multiprocessing: { type: "boolean", required: false, default: true },
904
+ processes: { type: "number", required: false, default: null },
905
+ file_watcher: { type: ["null", "boolean", "object", file_watcher_js_1.FileWatcher], required: false },
906
+ offline: { type: "boolean", default: false },
907
+ additional_sitemap_endpoints: { type: "array", default: [] },
908
+ log_level: { type: "number", default: 0 },
909
+ daemon: { type: ["object", "boolean"], default: {} },
910
+ admin: { type: "object", default: {}, attributes: {
911
+ ips: { type: "array", default: [] },
912
+ password: {
913
+ type: "string",
914
+ verify: (param, attrs) => (param.length < 10 ? `Parameter "Server.admin.password" must have a length of at least 10 characters.` : undefined),
915
+ },
916
+ } },
917
+ ts: {
918
+ type: "object",
919
+ required: false,
920
+ scheme: {
921
+ compiler_opts: { type: "object", default: {} },
922
+ output: "string",
923
+ },
924
+ },
925
+ browser_preview: { type: "string", required: false, default: undefined },
926
+ } });
927
+ // Assign attributes directly.
928
+ this.port = port;
929
+ this.ip = ip;
930
+ this.is_primary = is_primary && cluster_1.default.isPrimary;
931
+ this.source = new _vinc_1.vlib.Path(source);
932
+ this.favicon = favicon;
933
+ this.enable_2fa = enable_2fa;
934
+ this.enable_account_activation = enable_account_activation;
935
+ this.token_expiration = token_expiration;
936
+ this.google_tag = google_tag;
937
+ this.production = production;
938
+ this.multiprocessing = multiprocessing;
939
+ this.processes = processes == null ? os.cpus().length : processes;
940
+ this.company = company;
941
+ this.mail_style = mail_style;
942
+ this.offline = offline;
943
+ this.online = !offline;
944
+ // this.honey_pot_key = honey_pot_key;
945
+ this._keys = keys;
946
+ this.additional_sitemap_endpoints = additional_sitemap_endpoints;
947
+ this.log_level = log_level;
948
+ this.tls = tls;
949
+ // this.file_watcher = file_watcher;
950
+ this.admin = admin;
951
+ this.ts = ts;
952
+ this.endpoints = new Map();
953
+ this.err_endpoints = new Map();
954
+ /* @performance */ this.performance = new _vinc_1.vlib.Performance("Server performance");
955
+ // Assign objects to server so it is easy to access.
956
+ this.status = status_js_1.Status;
957
+ this.logger = logger_js_1.default;
958
+ this.rate_limits = rate_limit_js_1.RateLimits;
959
+ // Add global rate limit.
960
+ this.rate_limits.add({ group: "global", interval: 60, limit: 1000 });
961
+ // Check source.
962
+ if (!this.source.exists()) {
963
+ throw Error(`Source directory "${this.source.str()}" does not exist.`);
964
+ }
965
+ this.source = this.source.abs();
966
+ // Set domain.
967
+ this.domain = domain.replace("https://", "").replace("http://", "");
968
+ while (this.domain.length > 0 && this.domain.charAt(this.domain.length - 1) === "/") {
969
+ this.domain = this.domain.substr(0, this.domain.length - 1);
970
+ }
971
+ // Set full domain.
972
+ this.full_domain = `http${tls == null || tls.key == null ? "" : "s"}://${domain}`; // also required for Stripe.
973
+ while (this.full_domain.charAt(this.full_domain.length - 1) === "/") {
974
+ this.full_domain = this.full_domain.substr(0, this.full_domain.length - 1);
975
+ }
976
+ // Set statics.
977
+ this.statics = statics;
978
+ this.statics_aspect_ratios = new Map();
979
+ // Add the default static to statics.
980
+ this.statics.push({
981
+ path: `${__dirname}/../../../frontend/src/static/`,
982
+ endpoint: "/volt_static",
983
+ });
984
+ // Set meta.
985
+ if (!(meta instanceof meta_js_1.Meta)) {
986
+ meta = new meta_js_1.Meta(meta);
987
+ }
988
+ if (favicon != null && meta.favicon == null) {
989
+ meta.favicon = this.full_domain + "/favicon.ico";
990
+ }
991
+ if (favicon != null && meta.image == null) {
992
+ meta.image = this.full_domain + "/favicon.ico";
993
+ }
994
+ else if (meta.image != null && !meta.image.startsWith("http")) {
995
+ meta.image = this.full_domain + meta.image;
996
+ }
997
+ this.meta = meta;
998
+ // Default headers.
999
+ const base_default_headers = {
1000
+ "Vary": "Origin",
1001
+ "Referrer-Policy": "same-origin",
1002
+ "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
1003
+ "Access-Control-Allow-Origin": "*",
1004
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
1005
+ 'Access-Control-Allow-Credentials': 'true',
1006
+ "X-XSS-Protection": "1; mode=block",
1007
+ "X-Content-Type-Options": "nosniff",
1008
+ "frame-ancestors": 'none',
1009
+ "X-Frame-Options": "DENY",
1010
+ "Strict-Transport-Security": "max-age=31536000",
1011
+ };
1012
+ const default_csp = {
1013
+ "default-src": "'self' https://*.google-analytics.com",
1014
+ "img-src": `'self' http://${this.domain} https://${this.domain} https://*.google-analytics.com https://raw.githubusercontent.com/vandenberghinc/ `,
1015
+ "script-src": "'self' 'unsafe-inline' https://ajax.googleapis.com https://www.googletagmanager.com https://googletagmanager.com https://*.google-analytics.com https://code.jquery.com https://cdn.jsdelivr.net/npm/@vandenberghinc/",
1016
+ "style-src": "'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/@vandenberghinc/",
1017
+ };
1018
+ if (default_headers == null) {
1019
+ this.csp = default_csp;
1020
+ this.default_headers = { ...base_default_headers };
1021
+ }
1022
+ else {
1023
+ if (default_headers["Content-Security-Policy"] != null && typeof default_headers["Content-Security-Policy"] !== "object") {
1024
+ throw Error("The Content-Security-Policy of the default headers must be an object with values for each csp key, e.g. \"{'script-src': '...'}\".");
1025
+ }
1026
+ this.csp = default_headers["Content-Security-Policy"] != null ? default_headers["Content-Security-Policy"] : default_csp;
1027
+ Object.keys(base_default_headers).forEach(key => {
1028
+ if (default_headers[key] === undefined) {
1029
+ default_headers[key] = base_default_headers[key];
1030
+ }
1031
+ });
1032
+ this.default_headers = default_headers;
1033
+ }
1034
+ // Initialize payments.
1035
+ if (payments) {
1036
+ // if (payments.type === "adyen") {
1037
+ // this.payments = new Adyen({
1038
+ // _server: this,
1039
+ // ...payments,
1040
+ // })
1041
+ // } else
1042
+ if (payments.type === "paddle") {
1043
+ this.payments = new paddle_js_1.Paddle({
1044
+ _server: this,
1045
+ ...payments,
1046
+ });
1047
+ }
1048
+ else {
1049
+ throw Error(`Invalid payment processor type "${payments.type}", valid types are "paddle" or "adyen".`);
1050
+ }
1051
+ }
1052
+ // Define your list of endpoints
1053
+ this.endpoints = new Map();
1054
+ this.err_endpoints = new Map();
1055
+ // Browser preview.
1056
+ if (browser_preview) {
1057
+ this.browser_preview = new browser_js_1.BrowserPreview(browser_preview);
1058
+ }
1059
+ // Static file watcher.
1060
+ this.static_file_watcher = new file_watcher_js_1.StaticFileWatcher(this);
1061
+ // Initialize file watcher.
1062
+ if (file_watcher !== false) {
1063
+ if (file_watcher == null) {
1064
+ // Null.
1065
+ file_watcher = {};
1066
+ }
1067
+ if (!file_watcher.source) {
1068
+ // Source.
1069
+ file_watcher.source = this.source.str();
1070
+ }
1071
+ if (!(file_watcher instanceof file_watcher_js_1.FileWatcher)) {
1072
+ // Initialize.
1073
+ file_watcher = new file_watcher_js_1.FileWatcher(file_watcher);
1074
+ }
1075
+ this.file_watcher = file_watcher;
1076
+ }
1077
+ else {
1078
+ this.file_watcher = undefined;
1079
+ }
1080
+ // File watcher.
1081
+ if (process.env.VOLT_NO_FILE_WATCHER == null &&
1082
+ !process.argv.includes("--no-file-watcher") &&
1083
+ this.production === false &&
1084
+ file_watcher !== false &&
1085
+ process.env.VOLT_FILE_WATCHER !== '1') {
1086
+ // Disable primary for when users access is_primary.
1087
+ this.is_primary = false;
1088
+ // Enable file watcher.
1089
+ this.is_file_watcher = true;
1090
+ }
1091
+ // No file watcher.
1092
+ else {
1093
+ // Disable file watcher.
1094
+ this.is_file_watcher = false;
1095
+ // Set logger.
1096
+ logger_js_1.default.log_level = this.log_level;
1097
+ this.log = logger_js_1.default.log.bind(logger_js_1.default);
1098
+ this.error = logger_js_1.default.error.bind(logger_js_1.default);
1099
+ // Initialize the service daemon.
1100
+ // Must be initialized before initializing the database.
1101
+ if (daemon !== false) {
1102
+ const log_source = this.source.join(".logs");
1103
+ if (!log_source.exists()) {
1104
+ log_source.mkdir_sync();
1105
+ }
1106
+ this.daemon = new _vinc_1.vlib.Daemon({
1107
+ name: this.domain.replaceAll(".", ""),
1108
+ user: daemon.user || os.userInfo().username,
1109
+ group: daemon.group || null,
1110
+ command: "volt --service --start",
1111
+ cwd: this.source.str(),
1112
+ args: daemon.args || [],
1113
+ env: daemon.env || {},
1114
+ description: daemon.description || `Service daemon for website ${this.domain}.`,
1115
+ auto_restart: true,
1116
+ logs: daemon.logs || log_source.join("logs").str(),
1117
+ errors: daemon.errors || log_source.join("errors").str(),
1118
+ });
1119
+ }
1120
+ // Initialize the database class.
1121
+ if (typeof database === "string") {
1122
+ this.db = new database_js_1.Database({ uri: database, _server: this });
1123
+ }
1124
+ else if (database !== false) {
1125
+ this.db = new database_js_1.Database({ ...database, _server: this });
1126
+ }
1127
+ // Initialize the users class.
1128
+ this.users = new users_js_1.Users(this);
1129
+ // The smtp instance.
1130
+ if (smtp) {
1131
+ this.smtp_sender = smtp.sender;
1132
+ this.smtp = nodemailer.createTransport(smtp);
1133
+ }
1134
+ // The rate limit server/client.
1135
+ if (this.is_primary) {
1136
+ this.rate_limit = new rate_limit_js_1.RateLimitServer({ ...(rate_limit.server ?? {}), _server: this });
1137
+ }
1138
+ else {
1139
+ if (rate_limit.server?.https) {
1140
+ rate_limit.client.https = true;
1141
+ }
1142
+ this.rate_limit = new rate_limit_js_1.RateLimitClient({ ...(rate_limit.client ?? {}), _server: this });
1143
+ }
1144
+ // Blacklist class.
1145
+ // if (this.honey_pot_key) {
1146
+ // this.blacklist = new Blacklist({api_key: this.honey_pot_key});
1147
+ // }
1148
+ }
1149
+ // Other keys.
1150
+ this.keys = {};
1151
+ // Start callbacks.
1152
+ this._on_start = [];
1153
+ this._on_stop = [];
1154
+ }
1155
+ // ---------------------------------------------------------
1156
+ // Utils.
1157
+ // Get a content type from an extension.
1158
+ get_content_type(extension) {
1159
+ let content_type = Server.content_type_mimes.find((item) => {
1160
+ if (item[0] == extension) {
1161
+ return item[1];
1162
+ }
1163
+ })?.[1];
1164
+ if (content_type == null) {
1165
+ content_type = "application/octet-stream";
1166
+ }
1167
+ return content_type;
1168
+ }
1169
+ // Set log level.
1170
+ set_log_level(level) {
1171
+ this.log_level = level;
1172
+ logger_js_1.default.log_level = level;
1173
+ }
1174
+ // ---------------------------------------------------------
1175
+ // Crypto (private).
1176
+ // Generate a crypto key.
1177
+ generate_crypto_key(length = 32) {
1178
+ return crypto.randomBytes(length).toString('hex');
1179
+ }
1180
+ // Create a sha hmac with the master key.
1181
+ hmac(key, data, algo = "sha256") {
1182
+ const hmac = crypto.createHmac(algo, key);
1183
+ hmac.update(data);
1184
+ return hmac.digest("hex");
1185
+ }
1186
+ _hmac(data) {
1187
+ if (!this._hash_key) {
1188
+ throw new Error("Hash key not initialized");
1189
+ }
1190
+ const hmac = crypto.createHmac("sha256", this._hash_key);
1191
+ hmac.update(data);
1192
+ return hmac.digest("hex");
1193
+ }
1194
+ // Hash without a key.
1195
+ hash(data, algo = "sha256") {
1196
+ if (typeof data !== "string") {
1197
+ data = JSON.stringify(data);
1198
+ }
1199
+ return crypto.createHash(algo).update(data).digest('hex');
1200
+ }
1201
+ // ---------------------------------------------------------
1202
+ // Headers (private).
1203
+ // Initialize the default headers.
1204
+ _init_default_headers() {
1205
+ let csp = [];
1206
+ Object.entries(this.csp).forEach(([key, value]) => {
1207
+ csp.push(key);
1208
+ if (typeof value === "string" && value.length > 0) {
1209
+ csp.push(" ");
1210
+ csp.push(value);
1211
+ }
1212
+ csp.push(";");
1213
+ });
1214
+ this.default_headers["Content-Security-Policy"] = csp.join("");
1215
+ }
1216
+ // Add header defaults.
1217
+ _set_header_defaults(stream) {
1218
+ stream.set_headers(this.default_headers);
1219
+ }
1220
+ // ---------------------------------------------------------
1221
+ // Endpoints (private).
1222
+ // Find endpoint.
1223
+ _find_endpoint(endpoint, method = null) {
1224
+ const result = this.endpoints.get(endpoint + ":" + method);
1225
+ if (!result) {
1226
+ for (const e of this.endpoints.values()) {
1227
+ if (e.endpoint instanceof RegExp &&
1228
+ e.method === method &&
1229
+ e.endpoint.test(endpoint)) {
1230
+ return e;
1231
+ }
1232
+ }
1233
+ }
1234
+ return result;
1235
+ }
1236
+ // Create default endpoints.
1237
+ _create_default_endpoints() {
1238
+ // Vars.
1239
+ const additional_file_watcher_paths = [];
1240
+ // Add favicon.
1241
+ if (this.favicon != null) {
1242
+ const favicon = new _vinc_1.vlib.Path(this.favicon);
1243
+ if (favicon.exists() === false) {
1244
+ throw Error(`Specified favicon path "${favicon}" does not exist.`);
1245
+ }
1246
+ this.endpoint(new endpoint_js_1.Endpoint({
1247
+ method: "GET",
1248
+ endpoint: "/favicon.ico",
1249
+ data: favicon.load_sync({ type: "buffer" }),
1250
+ content_type: this.get_content_type(favicon.extension()),
1251
+ _is_static: true,
1252
+ }));
1253
+ // additional_file_watcher_paths.push(favicon.str());
1254
+ }
1255
+ // Create status endpoint.
1256
+ const status_dir = this.source.join(".status");
1257
+ if (!status_dir.exists()) {
1258
+ status_dir.mkdir_sync();
1259
+ }
1260
+ const status_key_path = status_dir.join("key");
1261
+ let status_key;
1262
+ if (!status_key_path.exists()) {
1263
+ status_key = this.generate_crypto_key(32);
1264
+ status_key_path.save_sync(status_key);
1265
+ }
1266
+ else {
1267
+ status_key = status_key_path.load_sync();
1268
+ }
1269
+ this.endpoint(new endpoint_js_1.Endpoint({
1270
+ method: "GET",
1271
+ endpoint: "/.status",
1272
+ content_type: "application/json",
1273
+ params: {
1274
+ key: "string",
1275
+ },
1276
+ callback: async (stream, params) => {
1277
+ // Check key.
1278
+ if (params.key !== status_key) {
1279
+ return stream.send({
1280
+ status: 403,
1281
+ headers: { "Content-Type": "text/plain" },
1282
+ data: "Access Denied",
1283
+ });
1284
+ }
1285
+ // Default status info.
1286
+ const status = {};
1287
+ status.ip = this.ip;
1288
+ if (this.http) {
1289
+ status.http_port = this.port == null ? 80 : (this.port);
1290
+ }
1291
+ if (this.https) {
1292
+ status.https_port = this.port == null ? 443 : (this.port + 1);
1293
+ }
1294
+ // Load data.
1295
+ const data = await this._sys_db.load("status", {
1296
+ default: {
1297
+ running_since: null,
1298
+ running_threads: 0,
1299
+ total_threads: 0,
1300
+ }
1301
+ });
1302
+ Object.assign(status, data);
1303
+ // Response.
1304
+ return stream.send({
1305
+ status: 200,
1306
+ headers: { "Content-Type": "application/json" },
1307
+ data: status,
1308
+ });
1309
+ },
1310
+ }));
1311
+ // Default static endpoints.
1312
+ const defaults = [
1313
+ {
1314
+ method: "GET",
1315
+ endpoint: "/volt/volt.css",
1316
+ content_type: "text/css",
1317
+ path: new _vinc_1.vlib.Path(`${__dirname}/../css/volt.css`),
1318
+ },
1319
+ {
1320
+ method: "GET",
1321
+ endpoint: "/vhighlight/vhighlight.css",
1322
+ content_type: "text/css",
1323
+ path: new _vinc_1.vlib.Path(_vinc_2.vhighlight.web_exports.css),
1324
+ },
1325
+ {
1326
+ method: "GET",
1327
+ endpoint: "/vhighlight/vhighlight.js",
1328
+ content_type: "application/javascript",
1329
+ path: new _vinc_1.vlib.Path(_vinc_2.vhighlight.web_exports.js),
1330
+ },
1331
+ ];
1332
+ defaults.forEach((item) => {
1333
+ this.endpoint(new endpoint_js_1.Endpoint({
1334
+ method: item.method,
1335
+ endpoint: item.endpoint,
1336
+ content_type: item.content_type,
1337
+ compress: item.compress,
1338
+ _path: item.path.str(),
1339
+ _templates: item.templates,
1340
+ })
1341
+ ._load_data_by_path(this));
1342
+ });
1343
+ // Handler.
1344
+ return additional_file_watcher_paths;
1345
+ }
1346
+ // Create the sitemap endpoint.
1347
+ _create_sitemap() {
1348
+ let sitemap = "";
1349
+ sitemap += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1350
+ sitemap += "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n";
1351
+ for (const endpoint of this.endpoints.values()) {
1352
+ if (endpoint.sitemap) {
1353
+ sitemap += `<url>\n <loc>${this.full_domain}/${endpoint.endpoint}</loc>\n</url>\n`; // @todo not compatiable with regex endpoints
1354
+ }
1355
+ }
1356
+ this.additional_sitemap_endpoints.forEach((endpoint) => {
1357
+ while (endpoint.length > 0 && endpoint.charAt(0) === "/") {
1358
+ endpoint = endpoint.substr(1);
1359
+ }
1360
+ sitemap += `<url>\n <loc>${this.full_domain}/${endpoint}</loc>\n</url>\n`;
1361
+ });
1362
+ sitemap += "</urlset>\n";
1363
+ this.endpoint(new endpoint_js_1.Endpoint({
1364
+ method: "GET",
1365
+ endpoint: "/sitemap.xml",
1366
+ data: sitemap,
1367
+ content_type: "application/xml",
1368
+ compress: false,
1369
+ }));
1370
+ }
1371
+ // Create the robots.txt endpoint.
1372
+ _create_robots_txt() {
1373
+ let robots = "User-agent: *\n";
1374
+ let disallowed = 0;
1375
+ for (const endpoint of this.endpoints.values()) {
1376
+ if (!endpoint.robots) {
1377
+ robots += `Disallow: ${endpoint.endpoint}\n`; // @todo not compatiable with regex endpoints
1378
+ disallowed++;
1379
+ }
1380
+ }
1381
+ if (disallowed === 0) {
1382
+ robots += `Disallow: \n`;
1383
+ }
1384
+ robots += `\nSitemap: ${this.full_domain}/sitemap.xml`;
1385
+ this.endpoint(new endpoint_js_1.Endpoint({
1386
+ method: "GET",
1387
+ endpoint: "/robots.txt",
1388
+ content_type: "text/plain",
1389
+ data: robots,
1390
+ compress: false,
1391
+ }));
1392
+ }
1393
+ // Create admin endpoint.
1394
+ _create_admin_endpoint() {
1395
+ // Add admin tokens.
1396
+ this.admin.tokens = [];
1397
+ // Verify token.
1398
+ const verify_token = (token) => {
1399
+ const now = Date.now();
1400
+ let new_tokens = [];
1401
+ let verified = false;
1402
+ this.admin.tokens.forEach((i) => {
1403
+ if (now < i.expiration) {
1404
+ if (i.token === token) {
1405
+ verified = true;
1406
+ }
1407
+ new_tokens.push(i);
1408
+ }
1409
+ });
1410
+ this.admin.tokens = new_tokens;
1411
+ return verified;
1412
+ };
1413
+ // Admin data.
1414
+ this.endpoint(new endpoint_js_1.Endpoint({
1415
+ method: "POST",
1416
+ endpoint: "/admin/auth",
1417
+ content_type: "application/json",
1418
+ rate_limit: {
1419
+ group: "volt.admin.auth",
1420
+ limit: 5,
1421
+ interval: 60,
1422
+ },
1423
+ params: {
1424
+ password: "string",
1425
+ },
1426
+ ip_whitelist: this.admin.ips,
1427
+ callback: async (stream, params) => {
1428
+ // Check key.
1429
+ if (params.password !== this.admin.password) {
1430
+ return stream.send({
1431
+ status: 403,
1432
+ headers: { "Content-Type": "text/plain" },
1433
+ data: "Access Denied",
1434
+ });
1435
+ }
1436
+ // Generate token.
1437
+ const token = {
1438
+ token: String.random(32),
1439
+ expiration: Date.now() + 3600 * 1000,
1440
+ };
1441
+ this.admin.tokens.push(token);
1442
+ // Response.
1443
+ return stream.send({
1444
+ status: 200,
1445
+ headers: { "Content-Type": "application/json" },
1446
+ data: token,
1447
+ });
1448
+ },
1449
+ }));
1450
+ // Admin data.
1451
+ this.endpoint(new endpoint_js_1.Endpoint({
1452
+ method: "GET",
1453
+ endpoint: "/admin/data",
1454
+ content_type: "application/json",
1455
+ rate_limit: "global",
1456
+ params: {
1457
+ token: "string",
1458
+ },
1459
+ ip_whitelist: this.admin.ips,
1460
+ callback: async (stream, params) => {
1461
+ // Verify token.
1462
+ if (!verify_token(params.token)) {
1463
+ return stream.send({
1464
+ status: 403,
1465
+ headers: { "Content-Type": "text/plain" },
1466
+ data: "Access Denied",
1467
+ });
1468
+ }
1469
+ // Data.
1470
+ const data = {};
1471
+ // Parse subscriptions.
1472
+ const subscriptions = await this.payments._get_all_active_subscriptions();
1473
+ data.subscriptions = subscriptions.length;
1474
+ // Load data.
1475
+ const status = await this._sys_db.load("status", {
1476
+ default: {
1477
+ running_since: null,
1478
+ running_threads: 0,
1479
+ total_threads: 0,
1480
+ }
1481
+ });
1482
+ Object.assign(data, status);
1483
+ // System data.
1484
+ data.cpu_usage = _vinc_1.vlib.System.cpu_usage();
1485
+ data.memory_usage = _vinc_1.vlib.System.memory_usage();
1486
+ data.network_usage = await _vinc_1.vlib.System.network_usage();
1487
+ // Users.
1488
+ data.users = (await this.users.list()).length;
1489
+ // Response.
1490
+ return stream.send({
1491
+ status: 200,
1492
+ headers: { "Content-Type": "application/json" },
1493
+ data: data,
1494
+ });
1495
+ },
1496
+ }));
1497
+ // Admin view.
1498
+ this.endpoint(new endpoint_js_1.Endpoint({
1499
+ method: "GET",
1500
+ endpoint: "/admin",
1501
+ content_type: "application/json",
1502
+ rate_limit: "global",
1503
+ params: {
1504
+ password: "string",
1505
+ },
1506
+ ip_whitelist: this.admin.ips,
1507
+ sitemap: false,
1508
+ robots: false,
1509
+ view: {
1510
+ templates: {
1511
+ DOMAIN: this.domain,
1512
+ },
1513
+ callback: () => {
1514
+ // Style.
1515
+ const style = {
1516
+ bg: "#F2F3F6",
1517
+ sub_bg: "#FAFAFA",
1518
+ fg: "#000000",
1519
+ sub_fg: "#9099B4",
1520
+ border: "#D6D6D6",
1521
+ tint: "#64B878", //"#8EB8EB", //"#4E9CF7",
1522
+ };
1523
+ // ... rest of the admin view implementation remains the same as it's client-side JavaScript ...
1524
+ },
1525
+ },
1526
+ }));
1527
+ }
1528
+ // Initialize statics.
1529
+ async _initialize_statics() {
1530
+ // Static paths for the file watcher.
1531
+ const static_paths = [];
1532
+ // Add static file.
1533
+ const add_static_file = async (path, // vlib.Path type
1534
+ endpoint, cache = true) => {
1535
+ // Add to static paths.
1536
+ static_paths.push(path.str());
1537
+ // Get content type.
1538
+ const content_type = this.get_content_type(path.extension());
1539
+ // console.log("Add static file", endpoint, path.str())
1540
+ // Image endpoint with supported transformation.
1541
+ if (image_endpoint_js_1.ImageEndpoint.supported_images.includes(path.extension())) {
1542
+ const e = new image_endpoint_js_1.ImageEndpoint({
1543
+ endpoint,
1544
+ content_type,
1545
+ path,
1546
+ cache,
1547
+ rate_limit: "global",
1548
+ _is_static: true,
1549
+ });
1550
+ const aspect_ratio = await e.get_aspect_ratio();
1551
+ if (aspect_ratio != null) {
1552
+ this.statics_aspect_ratios.set(e.endpoint, aspect_ratio);
1553
+ }
1554
+ this.endpoint(e);
1555
+ }
1556
+ // Default static endpoint.
1557
+ else {
1558
+ // Create endpoint.
1559
+ this.endpoint(new endpoint_js_1.Endpoint({
1560
+ method: "GET",
1561
+ endpoint,
1562
+ content_type,
1563
+ compress: !Server.compressed_extensions.includes(path.extension()),
1564
+ cache,
1565
+ rate_limit: "global",
1566
+ _path: path.str(),
1567
+ _is_static: true,
1568
+ })
1569
+ ._load_data_by_path(this));
1570
+ }
1571
+ };
1572
+ // Initialize statics.
1573
+ const add_static = async (opts) => {
1574
+ if (opts == null) {
1575
+ return;
1576
+ }
1577
+ if (typeof opts === "object") {
1578
+ // Check object.
1579
+ _vinc_1.vlib.Scheme.verify({
1580
+ object: opts,
1581
+ check_unknown: true,
1582
+ scheme: {
1583
+ path: "string",
1584
+ endpoint: { type: "string", default: null },
1585
+ cache: { type: ["boolean", "number"], default: true },
1586
+ endpoints_cache: { type: "object", default: {} },
1587
+ exclude: { type: "array", default: [] },
1588
+ }
1589
+ });
1590
+ // Vars.
1591
+ const exclude = [/.*\.DS_Store/, /.*\.cache/, /.*\.old/, /.*\.ignore/, ...opts.exclude || []];
1592
+ const paths = []; // vlib.Path[]
1593
+ const source = new _vinc_1.vlib.Path(opts.path).abs();
1594
+ const source_len = source.str().length;
1595
+ const is_dir = source.is_dir();
1596
+ // Is excluded.
1597
+ const is_excluded = (path) => {
1598
+ return exclude.some(pattern => {
1599
+ if (path instanceof RegExp) {
1600
+ if (pattern instanceof RegExp) {
1601
+ return pattern.source === path.source;
1602
+ }
1603
+ else {
1604
+ return path.test(String(pattern));
1605
+ }
1606
+ }
1607
+ else {
1608
+ if (pattern instanceof RegExp) {
1609
+ return pattern.test(String(path));
1610
+ }
1611
+ else {
1612
+ return path === pattern;
1613
+ }
1614
+ }
1615
+ });
1616
+ };
1617
+ // Initialize endpoint.
1618
+ opts.endpoint = opts.endpoint || `/${source.name()}`;
1619
+ if (opts.endpoint.charAt(0) != "/") {
1620
+ opts.endpoint = "/" + opts.endpoint;
1621
+ }
1622
+ while (opts.endpoint.charAt(opts.endpoint.length - 1) == "/") {
1623
+ opts.endpoint = opts.endpoint.slice(0, -1);
1624
+ }
1625
+ // Not a directory.
1626
+ if (!is_dir) {
1627
+ return await add_static_file(source, opts.endpoint, opts.cache);
1628
+ }
1629
+ // First extract all paths recursively.
1630
+ // non recursive to ignore .old etc dirs.
1631
+ const read_dir = async (path) => {
1632
+ const dir_paths = await path.paths();
1633
+ const promises = [];
1634
+ for (let i = 0; i < dir_paths.length; i++) {
1635
+ if (!is_excluded(dir_paths[i])) {
1636
+ // @todo excluded does not work `.old` etc is still included and DS_Store.
1637
+ if (dir_paths[i].is_dir()) {
1638
+ promises.push(read_dir(dir_paths[i]));
1639
+ }
1640
+ else {
1641
+ paths.push(dir_paths[i]);
1642
+ }
1643
+ }
1644
+ }
1645
+ ;
1646
+ await Promise.all(promises);
1647
+ };
1648
+ if (is_dir) {
1649
+ await read_dir(source);
1650
+ }
1651
+ // Convert paths into a static object.
1652
+ for (const path of paths) {
1653
+ const endpoint = `${opts.endpoint}${path.str().substr(source_len)}`;
1654
+ await add_static_file(path, endpoint, opts.endpoints_cache === undefined ? opts.cache : opts.endpoints_cache[endpoint] ?? opts.cache);
1655
+ }
1656
+ }
1657
+ else if (typeof opts === "string") {
1658
+ await add_static({ path: opts });
1659
+ }
1660
+ };
1661
+ // Iterate.
1662
+ for (let i = 0; i < this.statics.length; i++) {
1663
+ if (this.statics[i] instanceof _vinc_1.vlib.Path) {
1664
+ this.statics[i] = this.statics[i].str();
1665
+ }
1666
+ await add_static(this.statics[i]);
1667
+ }
1668
+ // Response.
1669
+ return static_paths;
1670
+ }
1671
+ // @todo this one is not TS
1672
+ // Initialize, compile & bundle js & ts.
1673
+ async _init_ts() {
1674
+ // Create dir.
1675
+ // Use ts.output instead of a /tmp/ dir since the node_modules for example is not present here.
1676
+ const tmp_volt_ts = new _vinc_1.vlib.Path(this.ts.output ?? "/tmp/ts-dummy/").abs();
1677
+ if (this.ts.output && !tmp_volt_ts.exists()) {
1678
+ tmp_volt_ts.mkdir_sync();
1679
+ }
1680
+ // Gather all entry paths.
1681
+ const server = this;
1682
+ let has_ts = false;
1683
+ const volt_frontend = new _vinc_1.vlib.Path(`${__dirname}/../../../frontend/dist/`).abs().str();
1684
+ const input_paths = [
1685
+ volt_frontend + "/volt.js",
1686
+ ];
1687
+ const entries = {};
1688
+ for (const endpoint of this.endpoints.values()) {
1689
+ if (endpoint.view?.is_js_ts_view) {
1690
+ input_paths.push(endpoint.view.source_path.str());
1691
+ let output_path = tmp_volt_ts.join(endpoint.view.source_path).abs();
1692
+ const extension = output_path.extension();
1693
+ if (!has_ts && /.tsx?/.test(extension)) {
1694
+ has_ts = true;
1695
+ }
1696
+ const output = output_path.str().slice(0, -extension.length) + ".js";
1697
+ if (entries[output] === undefined) {
1698
+ entries[output] = {
1699
+ endpoints: [endpoint],
1700
+ path: output,
1701
+ extension,
1702
+ async bundle() {
1703
+ server.rate_limit?.reset_all(); // reset api limits.
1704
+ let bundle = undefined;
1705
+ let had_bundle = this.endpoints[0]?.view?.bundle != null;
1706
+ for (let e = 0; e < this.endpoints.length; e++) {
1707
+ if (bundle === undefined) {
1708
+ bundle = await this.endpoints[e]?.view?._bundle_ts(this.path);
1709
+ }
1710
+ else {
1711
+ await this.endpoints[e]?.view?._bundle_ts(this.path, bundle);
1712
+ }
1713
+ if (had_bundle && server.browser_preview) {
1714
+ await server.browser_preview.refresh(this.endpoints[e].endpoint);
1715
+ }
1716
+ }
1717
+ },
1718
+ };
1719
+ }
1720
+ else {
1721
+ entries[output].endpoints.push(endpoint);
1722
+ }
1723
+ }
1724
+ }
1725
+ const entry_values = Object.values(entries);
1726
+ if (has_ts && this.ts.output == null) {
1727
+ throw new Error(`Due to detected typescript endpoint source files the output path attribute "Server.ts.output" must be defined.`);
1728
+ }
1729
+ // For production just compile & bundle.
1730
+ if (this.is_file_watcher || this.production) {
1731
+ await TSCompiler.compile({
1732
+ entry_paths: input_paths,
1733
+ output: tmp_volt_ts.str(),
1734
+ preprocess: TSPreprocessing.volt_auto_imports,
1735
+ compiler_opts: {
1736
+ target: "ES2021",
1737
+ module: "esnext",
1738
+ moduleResolution: "node",
1739
+ lib: ["ES2021", "DOM"],
1740
+ declaration: false,
1741
+ strict: true,
1742
+ esModuleInterop: true,
1743
+ allowJs: true,
1744
+ checkJs: false,
1745
+ noImplicitAny: false,
1746
+ skipLibCheck: true,
1747
+ types: [],
1748
+ ...this.ts.compiler_opts,
1749
+ rootDir: "/", // @warning is required to reliably match input and output file paths.
1750
+ },
1751
+ });
1752
+ let excluded = new Set();
1753
+ for (let i = 0; i < entry_values.length; i++) {
1754
+ await entry_values[i].bundle();
1755
+ // Add file watcher excludes.
1756
+ if (this.is_file_watcher) {
1757
+ for (const endpoint of entry_values[i].endpoints) {
1758
+ // console.log(entry_values[i].path, endpoint.view.bundle.inputs)
1759
+ for (let path of endpoint.view?.bundle?.inputs ?? []) {
1760
+ const vpath = new _vinc_1.vlib.Path(path).abs();
1761
+ const ext = vpath.extension();
1762
+ path = vpath.str();
1763
+ if (path.startsWith(tmp_volt_ts.str())) {
1764
+ const js_path = path.slice(tmp_volt_ts.str().length);
1765
+ const ts_path = path.slice(tmp_volt_ts.str().length, -ext.length) + ".ts";
1766
+ if (new _vinc_1.vlib.Path(js_path).exists() && !excluded.has(js_path)) {
1767
+ // console.log("Exclude", js_path)
1768
+ excluded.add(js_path);
1769
+ this.file_watcher.add_exclude(js_path);
1770
+ }
1771
+ if (new _vinc_1.vlib.Path(ts_path).exists() && !excluded.has(ts_path)) {
1772
+ // console.log("Exclude", ts_path)
1773
+ excluded.add(ts_path);
1774
+ this.file_watcher.add_exclude(ts_path);
1775
+ }
1776
+ }
1777
+ }
1778
+ // process.exit(1)
1779
+ }
1780
+ }
1781
+ }
1782
+ }
1783
+ // Development.
1784
+ else {
1785
+ // Compile with watcher enabled.
1786
+ const res = await TSCompiler.compile({
1787
+ entry_paths: input_paths,
1788
+ output: tmp_volt_ts.str(),
1789
+ preprocess: TSPreprocessing.volt_auto_imports,
1790
+ error_limit: 10,
1791
+ compiler_opts: {
1792
+ target: "ES2021",
1793
+ module: "esnext",
1794
+ moduleResolution: "node",
1795
+ lib: ["ES2021", "DOM"],
1796
+ declaration: false,
1797
+ strict: true,
1798
+ esModuleInterop: true,
1799
+ allowJs: true,
1800
+ checkJs: false,
1801
+ noImplicitAny: false,
1802
+ skipLibCheck: true,
1803
+ types: [],
1804
+ ...this.ts.compiler_opts,
1805
+ rootDir: "/", // @warning is required to reliably match input and output file paths.
1806
+ },
1807
+ watch: {
1808
+ enabled: true,
1809
+ log_level: 0,
1810
+ on_change: async (file_name) => {
1811
+ // console.log("File", file_name, "has changed.")
1812
+ logger_js_1.default.log(2, log_source, `Typescript output file ${file_name} has changed.`);
1813
+ // Check if the file path is part of an endpoint.
1814
+ const entry = entries[file_name];
1815
+ if (entry != null) {
1816
+ await entry.bundle();
1817
+ }
1818
+ // Check every other entries inputs' to see if that entry should be rebundled as well.
1819
+ for (let i = 0; i < entry_values.length; i++) {
1820
+ const entry = entry_values[i];
1821
+ if (entry.path !== file_name && entry.endpoints.iterate(e => e.view?.bundle?.inputs?.includes(file_name) ? true : undefined)) {
1822
+ await entry.bundle();
1823
+ }
1824
+ }
1825
+ },
1826
+ },
1827
+ });
1828
+ if (res.errors.length > 0) {
1829
+ res.debug();
1830
+ }
1831
+ for (let i = 0; i < entry_values.length; i++) {
1832
+ await entry_values[i].bundle();
1833
+ }
1834
+ this._stop_tscompiler_watcher = res.stop;
1835
+ }
1836
+ }
1837
+ // ---------------------------------------------------------
1838
+ // Server (private).
1839
+ // Initialize.
1840
+ // Initialize.
1841
+ async initialize() {
1842
+ /* @performance */ this.performance.start();
1843
+ // File watcher.
1844
+ if (this.is_file_watcher) {
1845
+ // Disable primary for when users access is_primary.
1846
+ this.is_primary = false;
1847
+ // Create default endpoints for excluded static file watch paths.
1848
+ this._create_default_endpoints();
1849
+ // Exclude the typescript output directory from the file watcher.
1850
+ if (this.ts.output) {
1851
+ this.file_watcher.add_exclude(this.ts.output);
1852
+ }
1853
+ // Add the volt backend source files to the additional files.
1854
+ this.file_watcher.add_path(__dirname);
1855
+ this.file_watcher.add_exclude(`${__dirname}/frontend_globals.js`);
1856
+ // Excluded.
1857
+ this.file_watcher.add_exclude(this.source.join(".db"));
1858
+ this.file_watcher.add_exclude(this.source.join(".rate_limit"));
1859
+ this.file_watcher.add_exclude(this.source.join(".logs"));
1860
+ // Exclude static files.
1861
+ this.statics.forEach(item => {
1862
+ if (typeof item === "string" || item instanceof _vinc_1.vlib.Path) {
1863
+ this.file_watcher.add_exclude(item);
1864
+ }
1865
+ else {
1866
+ this.file_watcher.add_exclude(item.path);
1867
+ }
1868
+ });
1869
+ // Initialize all endpoints since this is required for _init_ts()
1870
+ for (const endpoint of this.endpoints.values()) {
1871
+ await endpoint._initialize(this);
1872
+ }
1873
+ for (const endpoint of this.err_endpoints.values()) {
1874
+ await endpoint._initialize(this);
1875
+ }
1876
+ // Add typescript/js view endpoint excludes.
1877
+ await this._init_ts();
1878
+ if (!this.production) {
1879
+ [
1880
+ `${process.env.PERSISTANCE}/private/dev/vinc/vlib/js/vlib.js`,
1881
+ `${process.env.PERSISTANCE}/private/dev/vinc/vhighlight/vhighlight.js`,
1882
+ ].forEach(path => {
1883
+ const vpath = new _vinc_1.vlib.Path(path);
1884
+ if (vpath.exists()) {
1885
+ this.file_watcher.add_exclude(vpath.str());
1886
+ }
1887
+ });
1888
+ }
1889
+ // Stop.
1890
+ return;
1891
+ }
1892
+ // No file watcher.
1893
+ else {
1894
+ // Create HTTPS server.
1895
+ if (this.tls) {
1896
+ this.https = http2.createSecureServer({
1897
+ key: new _vinc_1.vlib.Path(this.tls.key).load_sync({ encoding: 'utf8' }),
1898
+ cert: new _vinc_1.vlib.Path(this.tls.cert).load_sync({ encoding: 'utf8' }),
1899
+ ca: this.tls.ca == null ? null : new _vinc_1.vlib.Path(this.tls.ca).load_sync({ encoding: 'utf8' }),
1900
+ passphrase: this.tls.passphrase,
1901
+ allowHTTP1: true,
1902
+ },
1903
+ // Support for http1.
1904
+ // Does not work, requests get triggered on the stream and on this callback.
1905
+ (req, res) => {
1906
+ if (req.httpVersion.charAt(0) !== "2") {
1907
+ this._serve(undefined, undefined, req, res);
1908
+ }
1909
+ });
1910
+ this.https.on('stream', (stream, headers) => {
1911
+ this._serve(stream, headers, undefined, undefined);
1912
+ });
1913
+ }
1914
+ // Payments require HTTPS in production.
1915
+ else if (this.production && this.payments) {
1916
+ throw Error("Accepting payments in production mode requires HTTPS.");
1917
+ }
1918
+ // Redirect HTTP requests to HTTPS.
1919
+ if (this.tls) {
1920
+ this.http = http.createServer((request, response) => {
1921
+ response.writeHead(301, { Location: `https://${request.headers.host}${request.url}` });
1922
+ response.end();
1923
+ });
1924
+ }
1925
+ else {
1926
+ this.http = http.createServer((req, res) => {
1927
+ this._serve(undefined, undefined, req, res);
1928
+ });
1929
+ }
1930
+ /* @performance */ this.performance.end("create-http-server");
1931
+ }
1932
+ // No file watcher.
1933
+ const file_watcher_restart = process.argv.includes("--file-watcher-restart");
1934
+ // Start the database.
1935
+ if (this.db) {
1936
+ await this.db.initialize();
1937
+ // Database collections.
1938
+ this._sys_db = this.db.create_collection("_sys"); // the volt sys collection.
1939
+ // Accessible collections.
1940
+ this.storage = this.db.create_collection("_storage"); // the sys backend storage collection for the user's server.
1941
+ /* @performance */ this.performance.end("init-db");
1942
+ // Load keys.
1943
+ const keys_document = await this._sys_db.load("keys");
1944
+ const gen_user_crypto_key = (doc, key) => {
1945
+ if (typeof key === "string") {
1946
+ doc[key] = this.generate_crypto_key(32);
1947
+ }
1948
+ else {
1949
+ if (key.length == null) {
1950
+ throw Error(`Crypto key object "${JSON.stringify(key)}" does not contain a "length" attribute.`);
1951
+ }
1952
+ if (typeof key.length !== "number") {
1953
+ throw Error(`Crypto key object "${JSON.stringify(key)}" has an invalid type fo attribute "length", the valid type is "number".`);
1954
+ }
1955
+ if (key.name == null) {
1956
+ throw Error(`Crypto key object "${JSON.stringify(key)}" does not contain a "name" attribute.`);
1957
+ }
1958
+ if (typeof key.name !== "string") {
1959
+ throw Error(`Crypto key object "${JSON.stringify(key)}" has an invalid type fo attribute "name", the valid type is "string".`);
1960
+ }
1961
+ doc[key.name] = this.generate_crypto_key(key.length);
1962
+ this.keys[key.name] = doc[key.name];
1963
+ }
1964
+ };
1965
+ if (keys_document == null) {
1966
+ this._hash_key = this.generate_crypto_key(32);
1967
+ const doc = {
1968
+ _master_sha256: this._hash_key,
1969
+ };
1970
+ this._keys.forEach((key) => {
1971
+ gen_user_crypto_key(doc, key);
1972
+ });
1973
+ await this._sys_db.save("keys", doc);
1974
+ }
1975
+ else {
1976
+ // Check hash key.
1977
+ this._hash_key = keys_document._master_sha256;
1978
+ let perform_save = false;
1979
+ if (this._hash_key === undefined) {
1980
+ this._hash_key = this.generate_crypto_key(32);
1981
+ keys_document._master_sha256 = this._hash_key;
1982
+ perform_save = true;
1983
+ }
1984
+ // Check crypto keys.
1985
+ this._keys.forEach((key) => {
1986
+ let name = typeof key === "string" ? key : key.name;
1987
+ if (keys_document[name] == null) {
1988
+ gen_user_crypto_key(keys_document, key);
1989
+ perform_save = true;
1990
+ }
1991
+ this.keys[name] = keys_document[name];
1992
+ });
1993
+ // Save.
1994
+ if (perform_save) {
1995
+ await this._sys_db.save("keys", keys_document);
1996
+ }
1997
+ }
1998
+ /* @performance */ this.performance.end("load-keys");
1999
+ }
2000
+ // Initialize default headers.
2001
+ this._init_default_headers();
2002
+ /* @performance */ this.performance.end("init-default-headers");
2003
+ // Create default endpoints.
2004
+ this._create_default_endpoints();
2005
+ /* @performance */ this.performance.end("create-default-endpoints");
2006
+ // Create admin endpoints.
2007
+ this._create_admin_endpoint();
2008
+ /* @performance */ this.performance.end("create-admin-endpoints");
2009
+ // Create static endpoints.
2010
+ await this._initialize_statics();
2011
+ /* @performance */ this.performance.end("create-static-endpoints");
2012
+ // Initialize all endpoints.
2013
+ // Must be done after initializing static and default endpoints to support html embeddings.
2014
+ for (const endpoint of this.endpoints.values()) {
2015
+ await endpoint._initialize(this);
2016
+ }
2017
+ for (const endpoint of this.err_endpoints.values()) {
2018
+ await endpoint._initialize(this);
2019
+ }
2020
+ /* @performance */ this.performance.end("init-endpoints");
2021
+ // Initialize users.
2022
+ if (this.db) {
2023
+ this.users._initialize();
2024
+ /* @performance */ this.performance.end("init-users");
2025
+ }
2026
+ // Database preview endpoints (only when production mode is disabled).
2027
+ if (this.db) {
2028
+ this.db._initialize_db_preview();
2029
+ /* @performance */ this.performance.end("init-db-preview");
2030
+ }
2031
+ // Payments.
2032
+ if (this.payments !== undefined) {
2033
+ await this.payments._initialize();
2034
+ }
2035
+ /* @performance */ this.performance.end("init-payments");
2036
+ // Get the icon and stroke icon file paths when defined.
2037
+ if (this.company.stroke_icon || this.company.icon) {
2038
+ for (const endpoint of this.endpoints.values()) {
2039
+ if (this.company.stroke_icon_path == null && endpoint.endpoint === this.company.stroke_icon) {
2040
+ this.company.stroke_icon_path = endpoint._path ?? undefined;
2041
+ }
2042
+ if (this.company.icon_path == null && endpoint.endpoint === this.company.icon) {
2043
+ this.company.icon_path = endpoint._path ?? undefined;
2044
+ }
2045
+ }
2046
+ if (this.company.stroke_icon != null && this.company.stroke_icon_path == null) {
2047
+ throw Error(`Unable to find the company's stroke icon endpoint "${this.company.stroke_icon}".`);
2048
+ }
2049
+ if (this.company.icon != null && this.company.icon_path == null) {
2050
+ throw Error(`Unable to find the company's icon endpoint "${this.company.icon}".`);
2051
+ }
2052
+ }
2053
+ /* @performance */ this.performance.end("init-icons");
2054
+ // Create sitemap when it does not exist.
2055
+ // Must be done at the end of initialization func since some funcs might still create endpoints.
2056
+ if (this._find_endpoint("sitemap.xml") == null) {
2057
+ this._create_sitemap();
2058
+ }
2059
+ /* @performance */ this.performance.end("create-sitemap");
2060
+ // Create robots.txt when it does not exist.
2061
+ // Must be done at the end of initialization func since some funcs might still create endpoints.
2062
+ if (this._find_endpoint("robots.txt") == null) {
2063
+ this._create_robots_txt();
2064
+ }
2065
+ /* @performance */ this.performance.end("create-robots.txt");
2066
+ }
2067
+ // Serve a client.
2068
+ // @todo implement rate limiting.
2069
+ // @todo save internal server errors.
2070
+ async _serve(http2_stream, headers, req, res) {
2071
+ try {
2072
+ // Convert stream.
2073
+ const stream = new stream_js_1.Stream(http2_stream, headers, req, res);
2074
+ // Vars.
2075
+ let endpoint;
2076
+ let method;
2077
+ let endpoint_url;
2078
+ // Log endpoint result.
2079
+ const log_endpoint_result = (message = null, status = null) => {
2080
+ let log_level = 0;
2081
+ // if (endpoint) {
2082
+ // log_level = endpoint.is_static ? 1 : 0;
2083
+ // }
2084
+ if (status == null) {
2085
+ status = stream.status_code;
2086
+ }
2087
+ logger_js_1.default.log(log_level, log_source, `${method}:${endpoint_url}: ${message ? message : status_js_1.Status.get_description(status)} [${status}] (${stream.ip}).`);
2088
+ };
2089
+ // Serve error endpoint.
2090
+ const serve_error_endpoint = async (status_code) => {
2091
+ // Get default response.
2092
+ const is_api_endpoint = endpoint && endpoint.callback != null;
2093
+ let default_response;
2094
+ switch (status_code) {
2095
+ case 400:
2096
+ default_response = {
2097
+ status: 400,
2098
+ headers: { "Content-Type": is_api_endpoint ? "application/json" : "text/plain" },
2099
+ data: is_api_endpoint ? { error: "Bad Request" } : "Bad Request",
2100
+ };
2101
+ break;
2102
+ case 403:
2103
+ default_response = {
2104
+ status: 403,
2105
+ headers: { "Content-Type": is_api_endpoint ? "application/json" : "text/plain" },
2106
+ data: is_api_endpoint ? { error: "Access Denied" } : "Access Denied",
2107
+ };
2108
+ break;
2109
+ case 404:
2110
+ default_response = {
2111
+ status: 404,
2112
+ headers: { "Content-Type": is_api_endpoint ? "application/json" : "text/plain" },
2113
+ data: is_api_endpoint ? { error: "Not Found" } : "Not Found",
2114
+ };
2115
+ break;
2116
+ case 500:
2117
+ default:
2118
+ default_response = {
2119
+ status: 500,
2120
+ headers: { "Content-Type": is_api_endpoint ? "application/json" : "text/plain" },
2121
+ data: is_api_endpoint ? { error: "Internal Server Error" } : "Internal Server Error",
2122
+ };
2123
+ break;
2124
+ }
2125
+ // Serve error endpoint or default response.
2126
+ if (!this.err_endpoints.has(status_code)) {
2127
+ stream.send(default_response);
2128
+ }
2129
+ else {
2130
+ const err_endpoint = this.err_endpoints.get(status_code);
2131
+ if (err_endpoint) {
2132
+ try {
2133
+ await err_endpoint._serve(stream, status_code);
2134
+ }
2135
+ catch (err) {
2136
+ logger_js_1.default.error(log_source, `Error endpoint ${status_code}: `, err);
2137
+ stream.send(default_response);
2138
+ }
2139
+ }
2140
+ // @todo also serve something here.
2141
+ }
2142
+ };
2143
+ // Check ip against blacklist.
2144
+ if (this.online && this.blacklist !== undefined && !this.blacklist.verify(stream.ip)) {
2145
+ await serve_error_endpoint(403);
2146
+ log_endpoint_result();
2147
+ return;
2148
+ }
2149
+ // Check if the request matches any of the defined endpoints.
2150
+ method = stream.method;
2151
+ endpoint_url = stream.endpoint;
2152
+ endpoint = this._find_endpoint(endpoint_url, method);
2153
+ // console.log(method, endpoint_url, endpoint)
2154
+ // No endpoint found.
2155
+ if (!endpoint) {
2156
+ // Check OPTIONS request.
2157
+ if (method === "OPTIONS") {
2158
+ const original_method = stream.headers['access-control-request-method'];
2159
+ const original_endpoint = this._find_endpoint(endpoint_url, original_method);
2160
+ if (original_endpoint) {
2161
+ // Set headers.
2162
+ this._set_header_defaults(stream);
2163
+ original_endpoint._set_headers(stream);
2164
+ // When any cors origin is allowed and origin is present then respond with that origin.
2165
+ if (stream.headers.origin && this.default_headers["Access-Control-Allow-Origin"] === "*") {
2166
+ stream.remove_header("Access-Control-Allow-Origin", "access-control-allow-origin");
2167
+ stream.set_header("Access-Control-Allow-Origin", stream.headers.origin);
2168
+ }
2169
+ // Send.
2170
+ stream.send({ status: status_js_1.Status.no_content });
2171
+ log_endpoint_result();
2172
+ return;
2173
+ }
2174
+ }
2175
+ // Respond with 404.
2176
+ await serve_error_endpoint(404);
2177
+ log_endpoint_result();
2178
+ return;
2179
+ }
2180
+ // Check rate limit.
2181
+ if (this.online && this.production && this.rate_limit !== undefined && endpoint.rate_limits.length > 0) {
2182
+ const result = await this.rate_limit.limit(stream.ip, endpoint.rate_limits);
2183
+ if (result != null) {
2184
+ stream.send({
2185
+ status: 429,
2186
+ headers: {
2187
+ "Content-Type": "text/plain",
2188
+ "X-RateLimit-Reset": result,
2189
+ },
2190
+ data: `Rate limit exceeded, please try again in ${Math.floor((result - Date.now()) / 1000)} seconds.`,
2191
+ });
2192
+ log_endpoint_result();
2193
+ return;
2194
+ }
2195
+ }
2196
+ // Parse the request parameters.
2197
+ try {
2198
+ await stream.join();
2199
+ }
2200
+ catch (err) {
2201
+ logger_js_1.default.error(log_source, `${method}:${endpoint_url}: `, err);
2202
+ await serve_error_endpoint(500);
2203
+ log_endpoint_result();
2204
+ return;
2205
+ }
2206
+ try {
2207
+ stream._parse_params();
2208
+ }
2209
+ catch (err) {
2210
+ logger_js_1.default.error(log_source, `${method}:${endpoint_url}: `, err);
2211
+ await serve_error_endpoint(400);
2212
+ log_endpoint_result();
2213
+ return;
2214
+ }
2215
+ // Set default headers.
2216
+ this._set_header_defaults(stream);
2217
+ // When any cors origin is allowed and origin is present then respond with that origin.
2218
+ if (stream.headers.origin && this.default_headers["Access-Control-Allow-Origin"] === "*") {
2219
+ stream.remove_header("Access-Control-Allow-Origin", "access-control-allow-origin");
2220
+ stream.set_header("Access-Control-Allow-Origin", stream.headers.origin);
2221
+ }
2222
+ // Do not authenticate on static endpoints, unless "authenticated" flag is somehow enabled.
2223
+ if (!endpoint.is_static || endpoint.authenticated) {
2224
+ // Always perform authentication so the stream.uid will also be assigned even when the endpoint is not authenticated.
2225
+ const auth_result = await this.users._authenticate(stream);
2226
+ // Reset cookies when authentication has failed.
2227
+ if (auth_result != null && !endpoint.is_static) {
2228
+ this.users._reset_cookies(stream);
2229
+ }
2230
+ // When the endpoint is authenticated and the authentication has failed then send the error response.
2231
+ if (auth_result != null && endpoint.authenticated) {
2232
+ stream.send(auth_result);
2233
+ log_endpoint_result();
2234
+ return;
2235
+ }
2236
+ }
2237
+ // Serve endpoint.
2238
+ try {
2239
+ await endpoint._serve(stream);
2240
+ }
2241
+ catch (err) {
2242
+ logger_js_1.default.error(log_source, `${method}:${endpoint_url}: `, err);
2243
+ if (!stream.destroyed && !stream.closed) {
2244
+ await serve_error_endpoint(500);
2245
+ log_endpoint_result();
2246
+ }
2247
+ return;
2248
+ }
2249
+ // Check if the response has been sent.
2250
+ if (!stream.finished) {
2251
+ logger_js_1.default.error(log_source, `${method}:${endpoint_url}: `, "Unfinished response.");
2252
+ await serve_error_endpoint(500);
2253
+ log_endpoint_result();
2254
+ return;
2255
+ }
2256
+ // Log.
2257
+ log_endpoint_result();
2258
+ }
2259
+ catch (err) {
2260
+ logger_js_1.default.error(log_source, "Fatal error:", err);
2261
+ }
2262
+ }
2263
+ // ---------------------------------------------------------
2264
+ // Server.
2265
+ // Start the server.
2266
+ /* @docs:
2267
+ * @title: Start
2268
+ * @description:
2269
+ * Start the server.
2270
+ * @usage:
2271
+ * ...
2272
+ * server.start();
2273
+ */
2274
+ async start() {
2275
+ // Always initialize, even when forking.
2276
+ await this.initialize();
2277
+ // Inside file watcher process.
2278
+ if (this.is_file_watcher) {
2279
+ this.file_watcher.start();
2280
+ await this.file_watcher.promise;
2281
+ return;
2282
+ }
2283
+ // Start static file watcher.
2284
+ if (!this.production) {
2285
+ this.static_file_watcher.start();
2286
+ }
2287
+ // Start the rate limiting client/server, also when forking.
2288
+ if (this.db) {
2289
+ /* @performance */ this.performance.start();
2290
+ if (this.rate_limit) {
2291
+ await this.rate_limit.start();
2292
+ }
2293
+ /* @performance */ this.performance.end("init-rate-limit");
2294
+ }
2295
+ // Initialize, compile and bundle ts and js.
2296
+ await this._init_ts();
2297
+ // Production & Master.
2298
+ let forked = false;
2299
+ if (this.production && this.multiprocessing && cluster_1.default.isPrimary) {
2300
+ // Vars.
2301
+ let active_threads = 0;
2302
+ const thread_ids = {};
2303
+ const restart_limiters = {};
2304
+ // Start thread.
2305
+ const start_thread = (thread_id, restart = false) => {
2306
+ // Fork.
2307
+ const worker = cluster_1.default.fork();
2308
+ // Log.
2309
+ logger_js_1.default.log(restart ? 0 : 1, log_source, `Starting thread ${worker.process.pid}.`);
2310
+ // Cache thread id.
2311
+ thread_ids[worker.process.pid] = thread_id;
2312
+ // Increment active threads.
2313
+ ++active_threads;
2314
+ };
2315
+ // Fork workers.
2316
+ for (let i = 0; i < this.processes; i++) {
2317
+ // Generate thread id.
2318
+ let thread_id;
2319
+ while ((thread_id = String.random(8)) && Object.values(thread_ids).includes(thread_id)) { }
2320
+ // Create limiter.
2321
+ restart_limiters[thread_id] = new _vinc_1.vlib.TimeLimiter({ limit: 3, duration: 60 * 1000 });
2322
+ // Start thread.
2323
+ start_thread(thread_id);
2324
+ }
2325
+ // Save status.
2326
+ await this._sys_db.save("status", {
2327
+ running_since: Date.now(),
2328
+ total_threads: active_threads,
2329
+ running_threads: active_threads,
2330
+ });
2331
+ // On exit.
2332
+ cluster_1.default.addListener('exit', async (worker, code, signal) => {
2333
+ // Fetch thread id.
2334
+ const thread_id = thread_ids[worker.process.pid];
2335
+ delete thread_ids[worker.process.pid];
2336
+ // Logs.
2337
+ logger_js_1.default.error(log_source, `Thread ${worker.process.pid} crashed.`);
2338
+ // Restart with limit.
2339
+ const limiter = restart_limiters[thread_id];
2340
+ if (limiter != null && limiter.limit()) {
2341
+ --active_threads;
2342
+ start_thread(thread_id, true);
2343
+ }
2344
+ // Reached limit, shutdown thread.
2345
+ else {
2346
+ logger_js_1.default.error(log_source, `Thread ${worker.process.pid} is being shut down due too its periodic restart limit.`);
2347
+ --active_threads;
2348
+ await this._sys_db.save("status", { running_threads: active_threads });
2349
+ if (active_threads === 0) {
2350
+ logger_js_1.default.error(log_source, `All threads died, stopping server.`);
2351
+ process.exit(0);
2352
+ }
2353
+ }
2354
+ });
2355
+ }
2356
+ else {
2357
+ forked = this.production && this.multiprocessing;
2358
+ // Set default port.
2359
+ let http_port, https_port;
2360
+ if (this.port == null) {
2361
+ http_port = 80;
2362
+ https_port = 443;
2363
+ }
2364
+ else {
2365
+ http_port = this.port;
2366
+ https_port = this.port + 1;
2367
+ }
2368
+ // Callbacks.
2369
+ let is_running = false;
2370
+ const on_running = () => {
2371
+ if (!is_running) {
2372
+ is_running = true;
2373
+ if (this.https !== undefined) {
2374
+ logger_js_1.default.log(0, log_source, `Running on http://${this.ip}:${http_port} and https://${this.ip}:${https_port}.`);
2375
+ }
2376
+ else {
2377
+ logger_js_1.default.log(0, log_source, `Running on http://${this.ip}:${http_port}.`);
2378
+ }
2379
+ }
2380
+ };
2381
+ const on_error = (error) => {
2382
+ if (error.syscall !== 'listen') {
2383
+ throw error;
2384
+ }
2385
+ switch (error.code) {
2386
+ case 'EACCES':
2387
+ console.error(`Error: Address ${this.ip}:${this.port} requires elevated privileges.`);
2388
+ process.exit(1);
2389
+ break;
2390
+ case 'EADDRINUSE':
2391
+ console.error(`Error: Address ${this.ip}:${this.port} is already in use.`);
2392
+ process.exit(1);
2393
+ break;
2394
+ default:
2395
+ throw error;
2396
+ }
2397
+ };
2398
+ // Listen.
2399
+ this.http.listen(http_port, this.ip, on_running);
2400
+ this.http.on("error", on_error);
2401
+ if (this.https !== undefined) {
2402
+ this.https.listen(https_port, this.ip, on_running);
2403
+ this.https.on("error", on_error);
2404
+ }
2405
+ // Set signals.
2406
+ process.on('SIGTERM', () => process.exit(0));
2407
+ process.on('SIGINT', () => process.exit(0));
2408
+ // Send running message.
2409
+ if (process.env.VOLT_FILE_WATCHER === "1") {
2410
+ new _vinc_1.vlib.Path(process.env.VOLT_STARTED_FILE).save_sync("1");
2411
+ }
2412
+ // Start browser.
2413
+ // if (this.browser_preview) {
2414
+ // await this.browser_preview.start();
2415
+ // await this.browser_preview.navigate(this.full_domain);
2416
+ // }
2417
+ /* @performance */ this.performance.end("listen");
2418
+ }
2419
+ // On start callbacks.
2420
+ for (const callback of this._on_start) {
2421
+ const res = callback({ forked });
2422
+ if (res instanceof Promise) {
2423
+ await res;
2424
+ }
2425
+ }
2426
+ // Start browser preview on primary node.
2427
+ if (this.browser_preview && !forked) {
2428
+ await this.browser_preview.start();
2429
+ await this.browser_preview.navigate(this.full_domain);
2430
+ }
2431
+ // /* @performance */ this.performance.dump();
2432
+ }
2433
+ /* @docs:
2434
+ * @title: On start
2435
+ * @description:
2436
+ * Set an (async) callback which will be executed at the end of `server.start()`.
2437
+ * The callback may take arguments `({forked <boolean>})`.
2438
+ * @usage:
2439
+ * ...
2440
+ * server.on_start(({forked}) => console.log("Hello World!"));
2441
+ */
2442
+ on_start(callback) {
2443
+ this._on_start.append(callback);
2444
+ }
2445
+ // Stop the server.
2446
+ /* @docs:
2447
+ * @title: Stop
2448
+ * @description:
2449
+ * Stop the server.
2450
+ * @usage:
2451
+ * ...
2452
+ * server.stop();
2453
+ */
2454
+ async stop() {
2455
+ logger_js_1.default.log(0, log_source, "Stopping the server...");
2456
+ // On stop callbacks.
2457
+ for (const callback of this._on_stop) {
2458
+ const res = callback();
2459
+ if (res instanceof Promise) {
2460
+ await res;
2461
+ }
2462
+ }
2463
+ // Stop rate limit.
2464
+ if (this.rate_limit) {
2465
+ await this.rate_limit.stop();
2466
+ }
2467
+ // Stop view source file watcher.
2468
+ if (this._stop_tscompiler_watcher) {
2469
+ logger_js_1.default.log(0, log_source, "Stopping typescript watcher.");
2470
+ this._stop_tscompiler_watcher();
2471
+ }
2472
+ if (this.static_file_watcher) {
2473
+ this.static_file_watcher.stop();
2474
+ }
2475
+ // Stop sockets.
2476
+ if (this.https) {
2477
+ await this.https.close();
2478
+ }
2479
+ if (this.http) {
2480
+ await this.http.close();
2481
+ }
2482
+ if (this.db) {
2483
+ await this.db.close();
2484
+ }
2485
+ // Stop the logger.
2486
+ logger_js_1.default.stop();
2487
+ // setTimeout(() => {
2488
+ // thread_monitor.dump_active_resources({
2489
+ // // min_age: 5000,
2490
+ // // exclude_types: ['TIMERWRAP'],
2491
+ // include_internal: false
2492
+ // });
2493
+ // }, 6000);
2494
+ }
2495
+ /* @docs:
2496
+ * @title: On stop
2497
+ * @description:
2498
+ * Set an (async) callback which will be executed at the start of `server.stop()`.
2499
+ * @usage:
2500
+ * ...
2501
+ * server.on_stop(() => console.log("Hello World!"));
2502
+ */
2503
+ on_stop(callback) {
2504
+ this._on_stop.append(callback);
2505
+ }
2506
+ // Fetch status.
2507
+ /* @docs:
2508
+ @title: Fetch status.
2509
+ @desc: This function is meant to be used when the server is in production mode, it will make an API request to your server through the defined `Server.domain` parameter.
2510
+ @note: This function can be called without initializing the server.
2511
+ @param:
2512
+ @name: type
2513
+ @desc: The wanted output type. Either an `object` or a `string` type for CLI purposes.
2514
+ */
2515
+ async fetch_status(type = "object") {
2516
+ // Load key.
2517
+ const key_path = this.source.join(".status/key");
2518
+ if (!key_path.exists()) {
2519
+ throw new Error("No status key has been generated yet. Start your server first.");
2520
+ }
2521
+ const key = key_path.load_sync();
2522
+ // Make request.
2523
+ const { body: status } = await _vinc_1.vlib.request({
2524
+ host: this.domain,
2525
+ endpoint: "/.status",
2526
+ method: "GET",
2527
+ params: { key },
2528
+ query: true,
2529
+ json: true,
2530
+ });
2531
+ // String type.
2532
+ if (type === "string") {
2533
+ if (status.running_since != null) {
2534
+ status.running_since = new _vinc_1.vlib.Date(status.running_since).format("%d-%m-%y %H:%M:%S");
2535
+ }
2536
+ let str = `${this.domain}:\n`;
2537
+ Object.keys(status).forEach((key) => {
2538
+ str += ` * ${key}: ${status[key]}\n`;
2539
+ });
2540
+ str = str.substr(0, str.length - 1);
2541
+ return str;
2542
+ }
2543
+ // Response.
2544
+ return status;
2545
+ }
2546
+ // ---------------------------------------------------------
2547
+ // Content Security Policy.
2548
+ // Add a csp.
2549
+ /* @docs:
2550
+ * @title: Add CSP
2551
+ * @description: Add an url to the Content-Security-Policy. This function does not overwrite the existing key's value.
2552
+ * @warning: This function no longer has any effect when `Server.start()` has been called.
2553
+ * @parameter:
2554
+ * @name: key
2555
+ * @description: The Content-Security-Policy key, e.g. `script-src`.
2556
+ * @type: string
2557
+ * @parameter:
2558
+ * @name: value
2559
+ * @description: The value to add to the Content-Security-Policy key.
2560
+ * @type: null, string, string[]
2561
+ * @usage:
2562
+ * ...
2563
+ * server.add_csp("script-src", "somewebsite.com");
2564
+ * server.add_csp("upgrade-insecure-requests");
2565
+ */
2566
+ add_csp(key, value = null) {
2567
+ if (this.csp[key] === undefined) {
2568
+ this.csp[key] = "";
2569
+ }
2570
+ if (Array.isArray(value)) {
2571
+ value.forEach((v) => {
2572
+ if (typeof v === "string" && v.length > 0) {
2573
+ this.csp[key] += " " + v.trim();
2574
+ }
2575
+ });
2576
+ }
2577
+ else if (typeof value === "string" && value.length > 0) {
2578
+ this.csp[key] += " " + value.trim();
2579
+ }
2580
+ }
2581
+ // Remove a csp.
2582
+ /* @docs:
2583
+ * @title: Remove CSP
2584
+ * @description: Remove an url from the Content-Security-Policy. This function does not overwrite the existing key's value.
2585
+ * @warning: This function no longer has any effect when `Server.start()` has been called.
2586
+ * @parameter:
2587
+ * @name: key
2588
+ * @description: The Content-Security-Policy key, e.g. `script-src`.
2589
+ * @type: string
2590
+ * @parameter:
2591
+ * @name: value
2592
+ * @description: The value to remove from the Content-Security-Policy key.
2593
+ * @type: null, string
2594
+ * @usage:
2595
+ * ...
2596
+ * server.remove_csp("script-src", "somewebsite.com");
2597
+ * server.remove_csp("upgrade-insecure-requests");
2598
+ */
2599
+ remove_csp(key, value = null) {
2600
+ if (this.csp[key] === undefined) {
2601
+ return;
2602
+ }
2603
+ if (typeof value === "string" && value.length > 0) {
2604
+ this.csp[key] = this.csp[key].replaceAll(value, "");
2605
+ }
2606
+ else {
2607
+ delete this.csp[key];
2608
+ }
2609
+ }
2610
+ // Delete a csp key.
2611
+ /* @docs:
2612
+ * @title: Delete CSP
2613
+ * @description: Delete an key from the Content-Security-Policy.
2614
+ * @warning: This function no longer has any effect when `Server.start()` has been called.
2615
+ * @parameter:
2616
+ * @name: key
2617
+ * @description: The Content-Security-Policy key, e.g. `script-src`.
2618
+ * @type: string
2619
+ * @usage:
2620
+ * ...
2621
+ * server.del_csp("script-src");
2622
+ * server.del_csp("upgrade-insecure-requests");
2623
+ */
2624
+ del_csp(key) {
2625
+ delete this.csp[key];
2626
+ }
2627
+ // ---------------------------------------------------------
2628
+ // TLS.
2629
+ // Generate a key and csr for tls.
2630
+ async generate_tls_key({ path, organization_unit = "IT", ec = true }) {
2631
+ // Args.
2632
+ if (path == null) {
2633
+ throw Error("Define parameter \"path\".");
2634
+ }
2635
+ if (organization_unit == null) {
2636
+ throw Error("Define parameter \"organization_unit\".");
2637
+ }
2638
+ // Paths.
2639
+ const vpath = new _vinc_1.vlib.Path(path);
2640
+ const key = vpath.join("key.key");
2641
+ const csr = vpath.join("csr.csr");
2642
+ if (!vpath.exists()) {
2643
+ vpath.mkdir_sync();
2644
+ }
2645
+ if (key.exists()) {
2646
+ throw Error(`Key path "${key.str()}" already exists, remove the file manually to continue.`);
2647
+ }
2648
+ if (csr.exists()) {
2649
+ throw Error(`CSR path "${csr.str()}" already exists, remove the file manually to continue.`);
2650
+ }
2651
+ // Generate the private key using the EC parameters file
2652
+ const proc = new _vinc_1.vlib.Proc();
2653
+ await proc.start({
2654
+ command: "openssl",
2655
+ args: ec
2656
+ ? ["ecparam", "-genkey", "-name", "secp384r1", "-out", key.str()]
2657
+ : ["genpkey", "-algorithm", "RSA", "-pkeyopt", "rsa_keygen_bits:2048", "-out", key.str()],
2658
+ opts: { stdio: "inherit" },
2659
+ });
2660
+ if (proc.exit_status != 0) {
2661
+ throw Error(`Encountered an error while generating the private key [${proc.exit_status}]: ${proc.err}`);
2662
+ }
2663
+ // Generate the CSR using the generated private key
2664
+ await proc.start({
2665
+ command: "openssl",
2666
+ args: [
2667
+ "req", "-new", "-key", key.str(), "-out", csr.str(),
2668
+ "-subj",
2669
+ "\"" +
2670
+ "/C=" + this.company.country_code +
2671
+ "/ST=" + this.company.province +
2672
+ "/L=" + this.company.city +
2673
+ "/O=" + this.company.name +
2674
+ "/OU=" + organization_unit +
2675
+ "/CN=" + this.domain +
2676
+ "\""
2677
+ ],
2678
+ opts: { stdio: "inherit" },
2679
+ });
2680
+ if (proc.exit_status != 0) {
2681
+ throw Error(`Encountered an error while generating the CSR [${proc.exit_status}]: ${proc.err}`);
2682
+ }
2683
+ logger_js_1.default.log(0, log_source, `Generated the tls key with CSR for domain "${this.domain}".`);
2684
+ }
2685
+ // ---------------------------------------------------------
2686
+ // Endpoints.
2687
+ // Add one or multiple endpoints.
2688
+ /* @docs:
2689
+ @title: Add endpoint(s)
2690
+ @description: Add one or multiple endpoints.
2691
+ @parameter:
2692
+ @name: ...endpoints
2693
+ @description:
2694
+ The endpoint parameters.
2695
+
2696
+ An endpoint parameter can either be a `Endpoint` class or an `object` with the `Endpoint` arguments.
2697
+ @type: Endpoint, object
2698
+ */
2699
+ endpoint(...endpoints) {
2700
+ for (let i = 0; i < endpoints.length; i++) {
2701
+ let init_endpoint = endpoints[i];
2702
+ // Skip.
2703
+ if (init_endpoint == null) {
2704
+ continue;
2705
+ }
2706
+ // Is array of endpoints.
2707
+ if (Array.isArray(init_endpoint)) {
2708
+ this.endpoint(...init_endpoint);
2709
+ continue;
2710
+ }
2711
+ // Initialize endpoint.
2712
+ if (!(init_endpoint instanceof endpoint_js_1.Endpoint)) {
2713
+ init_endpoint = new endpoint_js_1.Endpoint(init_endpoint);
2714
+ }
2715
+ const endpoint = init_endpoint;
2716
+ // Build view.
2717
+ if (endpoint.view != null) {
2718
+ if (endpoint.view.meta == null) {
2719
+ endpoint.view.meta = this.meta.copy();
2720
+ }
2721
+ else if (typeof endpoint.view.meta === "object" && !(endpoint.view.meta instanceof meta_js_1.Meta)) {
2722
+ endpoint.view.meta = new meta_js_1.Meta(endpoint.view.meta);
2723
+ }
2724
+ }
2725
+ // Add endpoint.
2726
+ this.endpoints.set(`${endpoint.endpoint}:${endpoint.method}`, endpoint);
2727
+ if (!this.production) {
2728
+ if (endpoint._path && this.file_watcher?.add_exclude) {
2729
+ this.file_watcher.add_exclude(endpoint._path);
2730
+ }
2731
+ this.static_file_watcher.add(endpoint);
2732
+ }
2733
+ }
2734
+ return this;
2735
+ }
2736
+ // Add an error endpoint.
2737
+ /* @docs:
2738
+ @title: Add error endpoint
2739
+ @description:
2740
+ Add an endpoint per error status code.
2741
+ @parameter:
2742
+ @name: status_code
2743
+ @type: number
2744
+ @description:
2745
+ The status code of the error.
2746
+
2747
+ The supported status codes are:
2748
+ * `404`
2749
+ * `400` (Will not be used when the endpoint uses an API callback).
2750
+ * `403`
2751
+ * `404`
2752
+ * `500`
2753
+ @parameter:
2754
+ @name: endpoint
2755
+ @description:
2756
+ The endpoint parameters.
2757
+
2758
+ An endpoint parameter can either be a `Endpoint` class or an `object` with the `Endpoint` arguments.
2759
+ @type: Endpoint, object
2760
+ */
2761
+ error_endpoint(status_code, endpoint) {
2762
+ this.err_endpoints.set(status_code, endpoint instanceof endpoint_js_1.Endpoint ? endpoint : new endpoint_js_1.Endpoint(endpoint));
2763
+ return this;
2764
+ }
2765
+ // ---------------------------------------------------------
2766
+ // Functions.
2767
+ // Send a mail.
2768
+ /* @docs:
2769
+ * @title: Send Mail
2770
+ * @description: Send one or multiple mails.
2771
+ * @note: Make sure the domain's DNS records SPF and DKIM are properly configured when sending attachments.
2772
+ * @return:
2773
+ * Returns a promise that will be resolved or rejected when the mail has been sent.
2774
+ * @parameter:
2775
+ * @name: sender
2776
+ * @description:
2777
+ * The sender address.
2778
+ * A sender address may either be a string with the email address, e.g. `your@email.com`.
2779
+ * Or an array with the sender name and email address, e.g. `["Sender", "your@email.com"]`.
2780
+ * @type: string, array
2781
+ * @parameter:
2782
+ * @name: recipients
2783
+ * @description:
2784
+ * The recipient addresses.
2785
+ * A reciepient address may either be a string with the email address, e.g. `your@email.com`.
2786
+ * Or an array with the sender name and email address, e.g. `["Sender", "your@email.com"]`.
2787
+ * @type: array[string, array]
2788
+ * @parameter:
2789
+ * @name: subject
2790
+ * @description: The subject text.
2791
+ * @type: string
2792
+ * @parameter:
2793
+ * @name: body
2794
+ * @description: The body text.
2795
+ * @type: string
2796
+ * @parameter:
2797
+ * @name: attachments
2798
+ * @description: An array with absolute file paths for attachments, or an array with nodemailer attachment objects.
2799
+ * @type: array[string], array[object]
2800
+ * @usage:
2801
+ * ...
2802
+ * await server.send_mail({
2803
+ * sender: ["Sender Name", "sender\@email.com"],
2804
+ * recipients: [
2805
+ * ["Recipient Name", "recipient1\@email.com"],
2806
+ * "recipient2\@email.com",
2807
+ * },
2808
+ * subject: "Example Mail",
2809
+ * body: "Hello World!",
2810
+ * attachments: ["/path/to/image.png"]
2811
+ * });
2812
+ */
2813
+ async send_mail({ sender = undefined, recipients = [], subject = undefined, body = "", attachments = [], }) {
2814
+ // Not enabled.
2815
+ if (this.smtp === undefined) {
2816
+ throw new Error("SMTP is not enabled, define the required server argument on initialization to enable smtp.");
2817
+ }
2818
+ // Convert MailElement to html.
2819
+ if (body instanceof Mail.MailElement) {
2820
+ body = body.html();
2821
+ }
2822
+ // Check args.
2823
+ if (sender == null && this.smtp_sender != null) {
2824
+ sender = this.smtp_sender;
2825
+ }
2826
+ if (recipients.length === 0) {
2827
+ throw new Error(`The mail has no recipients.`);
2828
+ }
2829
+ if (sender == null) {
2830
+ throw new Error(`Parameter "sender" should be a defined value of type "string" or "array".`);
2831
+ }
2832
+ // Format address wrapper.
2833
+ const format_address = (address) => {
2834
+ if (Array.isArray(address)) {
2835
+ return `${address[0]} <${address[1]}>`;
2836
+ }
2837
+ return address;
2838
+ };
2839
+ // Create to array.
2840
+ const to = [];
2841
+ recipients.forEach((address) => to.push(format_address(address)));
2842
+ // Create attachments array.
2843
+ let attached_files = [];
2844
+ if (attachments != null) {
2845
+ attachments.forEach((path) => {
2846
+ if (path instanceof _vinc_1.vlib.Path) {
2847
+ attached_files.push({
2848
+ filename: path.name(),
2849
+ path: path.str(),
2850
+ content: path.load_sync(),
2851
+ });
2852
+ }
2853
+ else if (typeof path === "string") {
2854
+ const p = new _vinc_1.vlib.Path(path);
2855
+ attached_files.push({
2856
+ filename: p.name(),
2857
+ path: path,
2858
+ content: p.load_sync(),
2859
+ });
2860
+ }
2861
+ else {
2862
+ attached_files.push(path);
2863
+ }
2864
+ });
2865
+ }
2866
+ // Send mail.
2867
+ try {
2868
+ await this.smtp.sendMail({
2869
+ from: format_address(sender),
2870
+ to: to,
2871
+ subject: subject,
2872
+ html: body,
2873
+ attachments: attached_files,
2874
+ });
2875
+ }
2876
+ catch (error) {
2877
+ throw new Error(error.message); // to keep readable stacktrace.
2878
+ }
2879
+ }
2880
+ // ---------------------------------------------------------
2881
+ // Default callbacks.
2882
+ // These can all be overwritten by the user.
2883
+ // @todo add scheme for payment params.
2884
+ // On delete user.
2885
+ /* @docs:
2886
+ * @title: On delete user
2887
+ * @description: This function can be overridden with a callback for when a user is deleted.
2888
+ * @parameter:
2889
+ * @name: uid
2890
+ * @description: The uid of the deleted user.
2891
+ * @type: string, array
2892
+ * @usage:
2893
+ * ...
2894
+ * server.on_delete_user = ({uid}) => {}
2895
+ */
2896
+ async on_delete_user({ uid }) { }
2897
+ // On successfull one-time payment.
2898
+ // This gets called for every product in the payment.
2899
+ async on_payment({ product, payment }) { }
2900
+ // On successfull subscription.
2901
+ // This gets called for every product in the payment.
2902
+ async on_subscription({ product, payment }) { }
2903
+ // On failed one-time or recurring payment.
2904
+ // async on_failed_payment({ payment }: { payment: any }): Promise<void> {}
2905
+ // On successfull cancellation.
2906
+ async on_cancellation({ payment, line_items }) { }
2907
+ // On failed cancellation.
2908
+ // async on_failed_cancellation({ payment, line_items }: { payment: any; line_items: any[] }): Promise<void> {}
2909
+ // On successfull refund.
2910
+ // The line items array are the items were refunded.
2911
+ async on_refund({ payment, line_items }) { }
2912
+ // On failed refund.
2913
+ // The line items array are the items were the refund failed.
2914
+ async on_failed_refund({ payment, line_items }) { }
2915
+ // On chargeback.
2916
+ // The line items array are the items were charged back.
2917
+ async on_chargeback({ payment, line_items }) { }
2918
+ // On failed chargeback.
2919
+ // The line items array are the items were the chargeback failed.
2920
+ async on_failed_chargeback({ payment, line_items }) { }
2921
+ // Mail template.
2922
+ _mail_template({ max_width = 400, children = [], }) {
2923
+ const style = this.mail_style;
2924
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2925
+ // Create header.
2926
+ let header;
2927
+ if (this.company.stroke_icon != null) {
2928
+ header = [
2929
+ Image(`${this.full_domain}/${this.company.stroke_icon}`).height(16),
2930
+ ];
2931
+ }
2932
+ else if (this.company.icon != null) {
2933
+ header = [
2934
+ Image(`${this.full_domain}/${this.company.icon}`).frame(20, 40),
2935
+ ];
2936
+ }
2937
+ if (header) {
2938
+ header = Table(TableRow(...header)
2939
+ .wrap(true)
2940
+ .center()
2941
+ .center_vertical()).margin_bottom(15);
2942
+ }
2943
+ // Create mail.
2944
+ return Mail.Mail(Table(TableData(Table(
2945
+ // Header.
2946
+ header,
2947
+ // Widget.
2948
+ Table(...children)
2949
+ .background_color(style.widget_bg ?? "")
2950
+ .border(`1px solid ${style.widget_border ?? ""}`)
2951
+ .border_radius("10px")
2952
+ .padding(40, 25, 25, 25)
2953
+ .margin(0),
2954
+ // Copyright.
2955
+ Table(TableRow(Text(`Copyright © ${new Date().getFullYear()} ${this.company.name}, ${this.company.legal_name} All Rights Included.\n` +
2956
+ `${this.company.street} ${this.company.house_number}, ${this.company.postal_code}, ${this.company.city}, ${this.company.province}, ${this.company.country}.\n` +
2957
+ (this.company.tax_id == null ? "" : `VAT ID ${this.company.tax_id}`))
2958
+ .white_space("pre")
2959
+ .display("inline-block")
2960
+ .font_size(11)
2961
+ .color(style.footer_fg)
2962
+ .margin(0)).center().center_vertical()).margin(0, 0, 10, 0)).max_width(max_width)).center()).padding(25, 20, 25, 20)).font_family(style.font).background(style.bg);
2963
+ }
2964
+ // Render payment line items.
2965
+ _render_mail_payment_line_items({ payment, line_items, show_total_due = false }) {
2966
+ // Shortcuts.
2967
+ const style = this.mail_style;
2968
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2969
+ // Render payment line item for a mail.
2970
+ const _render_mail_payment_line_item = ({ name, desc, unit_cost, quantity, total_cost, font_weight = "normal", divider = true, color = style.text_fg, }) => {
2971
+ return [
2972
+ Table(TableRow(TableData(Text(name)
2973
+ .color(color)
2974
+ .font_size(14)
2975
+ .text_wrap("wrap")
2976
+ .overflow_wrap("break-word")
2977
+ .word_wrap("break-word")
2978
+ .font_weight(font_weight)).width("25%").margin_right(10), TableData(Text(desc)
2979
+ .color(color)
2980
+ .font_size(14)
2981
+ .text_wrap("wrap")
2982
+ .overflow_wrap("break-word")
2983
+ .word_wrap("break-word")
2984
+ .font_weight(font_weight)).width("35%").margin_right(10), TableData(Text(unit_cost)
2985
+ .color(color)
2986
+ .font_size(14)
2987
+ .text_wrap("wrap")
2988
+ .overflow_wrap("break-word")
2989
+ .word_wrap("break-word")
2990
+ .font_weight(font_weight)).fixed_width("13.32%").margin_right(10), TableData(Text(quantity)
2991
+ .color(color)
2992
+ .font_size(14)
2993
+ .text_wrap("wrap")
2994
+ .overflow_wrap("break-word")
2995
+ .word_wrap("break-word")
2996
+ .font_weight(font_weight)).fixed_width("13.32%").margin_right(10), TableData(Text(total_cost)
2997
+ .color(color)
2998
+ .font_size(14)
2999
+ .text_wrap("wrap")
3000
+ .overflow_wrap("break-word")
3001
+ .word_wrap("break-word")
3002
+ .font_weight(font_weight)).fixed_width("13.32%")).width("100%").styles({ "vertical-align": "baseline" })).width("100%"),
3003
+ !divider
3004
+ ? null
3005
+ : TableRow(TableData(VStack()
3006
+ .background_color(style.text_fg)
3007
+ .frame("100%", 1)
3008
+ .margin(5, 0, 10, 0)).frame("100%", 1)).width("100%"),
3009
+ ];
3010
+ };
3011
+ // Render a divider.
3012
+ const render_divider = () => {
3013
+ return TableRow(TableData(VStack()
3014
+ .background_color(style.divider_bg)
3015
+ .frame("100%", 1)
3016
+ .margin(5, 0, 10, 0)).frame("100%", 1)).width("100%");
3017
+ };
3018
+ // Vars.
3019
+ let currency;
3020
+ let subtotal = 0;
3021
+ let subtotal_tax = 0;
3022
+ let total = 0;
3023
+ payment.line_items.iterate((item) => {
3024
+ if (typeof item.product === "string") {
3025
+ item.product = this.payments.get_product_sync(item.product);
3026
+ }
3027
+ if (currency == null) {
3028
+ const c = utils_js_1.Utils.get_currency_symbol(item.product.currency);
3029
+ if (c == null) {
3030
+ logger_js_1.default.error(log_source, `Failed to create a payment mail: `, new Error(`Unable to determine the currency of payment "${payment.id}".`));
3031
+ }
3032
+ currency = c ?? "?";
3033
+ }
3034
+ subtotal += item.subtotal;
3035
+ subtotal_tax += item.tax;
3036
+ total += item.total;
3037
+ });
3038
+ let total_due = payment.status === "open" ? total : 0;
3039
+ return [
3040
+ render_divider(),
3041
+ line_items.iterate_append((item, index) => {
3042
+ return Table(TableRow(TableData(Image(item.product.icon)
3043
+ .frame(35, 35)
3044
+ .margin_right(15)).width("auto"), TableData(Table(Text(item.product.name)
3045
+ .color(style.title_fg)
3046
+ .font_size(14)
3047
+ .font_weight("bold")
3048
+ .margin(0)
3049
+ .ellipsis_overflow(true), Text(item.product.description)
3050
+ .color(style.text_fg)
3051
+ .font_size(14)
3052
+ .margin(0)
3053
+ .ellipsis_overflow(true))).width("100%"), TableData(Text(`${currency} ${item.subtotal.toFixed(2)}`)
3054
+ .color(style.title_fg)
3055
+ .font_size(14)
3056
+ .font_weight("bold")
3057
+ .margin(0)
3058
+ .white_space("nowrap")).width("100%")).wrap(true).leading_vertical().width("100%")).width("100%");
3059
+ }),
3060
+ render_divider(),
3061
+ Table([
3062
+ ["Subtotal:", `${currency} ${subtotal.toFixed(2)}`],
3063
+ ["Tax:", `${currency} ${subtotal_tax.toFixed(2)}`],
3064
+ ["Total:", `${currency} ${total.toFixed(2)}`],
3065
+ ].iterate_append((item) => {
3066
+ return TableRow(TableData().width("100%"), TableData(Text(item[0])
3067
+ .color(style.title_fg)
3068
+ .font_size(14)
3069
+ .ellipsis_overflow(true)
3070
+ .font_weight("bold")).min_width(75), TableData(Text(item[1])
3071
+ .color(style.title_fg)
3072
+ .font_size(14)
3073
+ .white_space("nowrap")
3074
+ .font_weight("bold"))
3075
+ // .min_width(50)
3076
+ ).wrap(true);
3077
+ // .text_align("right")
3078
+ })),
3079
+ ];
3080
+ }
3081
+ // On 2fa mail.
3082
+ on_2fa_mail({ code, username, email, date, ip, device }) {
3083
+ const style = this.mail_style;
3084
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
3085
+ return this._mail_template({
3086
+ max_width: 400,
3087
+ children: [
3088
+ // Title.
3089
+ TableRow(Title("Verification Required")
3090
+ .color(style.title_fg)
3091
+ .width("fit-content")
3092
+ .font_size(26)).center(),
3093
+ // Text.
3094
+ TableRow(Text("Please confirm your request with this 2FA code.")
3095
+ .center()
3096
+ .margin(10, 0, 20, 0)
3097
+ .color(style.text_fg)
3098
+ .font_size(18)),
3099
+ // Auth info.
3100
+ [
3101
+ ["Username", username],
3102
+ ["Email", email],
3103
+ ["Date", date],
3104
+ ["Ip Address", ip],
3105
+ ["Device", device],
3106
+ ].iterate_append((item) => {
3107
+ return [
3108
+ TableRow(VStack()
3109
+ .margin_right(7.5)
3110
+ // .background("linear-gradient(135deg, #4830C4, #6E399E, #421959)")
3111
+ .background_color(style.text_fg)
3112
+ .border_radius("50%")
3113
+ .frame(5, 5), Text(`<span style='font-weight: 600'>${item[0]}:</span> ${item[1]}`)
3114
+ .color(style.text_fg)
3115
+ .font_size(16)
3116
+ .text_wrap("wrap")
3117
+ .overflow_wrap("break-word")
3118
+ .word_wrap("break-word")).wrap(true).center_vertical(),
3119
+ TableRow().fixed_frame(5, 5),
3120
+ ];
3121
+ }),
3122
+ // 2FA code.
3123
+ TableRow(Text(code)
3124
+ .background(style.button_bg)
3125
+ .border_radius("10px")
3126
+ .padding(10, 15)
3127
+ .center()
3128
+ .color(style.button_fg)
3129
+ .width("100%")
3130
+ .margin(20, 0, 0, 0)),
3131
+ // Text.
3132
+ TableRow(Text("This 2FA code will be valid for 5 minutes.")
3133
+ .color(style.text_fg)
3134
+ .font_style("italic")
3135
+ .font_size(12)
3136
+ .margin_top(20)
3137
+ .center()),
3138
+ ],
3139
+ });
3140
+ }
3141
+ // On successfull payment mail.
3142
+ on_payment_mail({ payment }) {
3143
+ // Shortcuts.
3144
+ const style = this.mail_style;
3145
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
3146
+ // Create mail.
3147
+ return this._mail_template({
3148
+ max_width: 600,
3149
+ children: [
3150
+ // Title.
3151
+ TableRow(Title("Successful Payment")
3152
+ .color(style.title_fg)
3153
+ .width("fit-content")
3154
+ .font_size(26)).center(),
3155
+ // Text.
3156
+ TableRow(Text("We're delighted to inform you that your payment has been successfully processed. Thank you for your purchase.")
3157
+ .margin(10, 0, 20, 0)
3158
+ .color(style.text_fg)
3159
+ .font_size(16)
3160
+ .center()),
3161
+ // Image.
3162
+ TableRow(Image(`${this.full_domain}/volt_static/payments/party.png`)
3163
+ .frame(60, 60)
3164
+ .margin(0, 0, 30, 0)).center(),
3165
+ // Title.
3166
+ TableRow(Title("Order Summary")
3167
+ .color(style.subtitle_fg)
3168
+ .font_size(18)
3169
+ .margin(0)),
3170
+ TableRow(Text("A summary of your order can be found below or in the attachmed invoice pdf.")
3171
+ .margin(5, 0, 20, 0)
3172
+ .color(style.text_fg)
3173
+ .font_size(16)),
3174
+ // Line items.
3175
+ this._render_mail_payment_line_items({ payment, line_items: payment.line_items, show_total_due: true }),
3176
+ // Bottom spacing.
3177
+ VStack()
3178
+ .margin_bottom(15)
3179
+ ],
3180
+ });
3181
+ }
3182
+ // On failed payment mail.
3183
+ on_failed_payment_mail({ payment }) {
3184
+ // Shortcuts.
3185
+ const style = this.mail_style;
3186
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
3187
+ // Create mail.
3188
+ return this._mail_template({
3189
+ max_width: 800,
3190
+ children: [
3191
+ // Title.
3192
+ TableRow(Title("Payment Failed")
3193
+ .color(style.title_fg)
3194
+ .width("fit-content")
3195
+ .font_size(26)).center(),
3196
+ // Text.
3197
+ TableRow(Text("We regret to inform you that your payment has encountered an issue and could not be processed successfully. We understand the inconvenience this may cause. Please try again, please contact customer support if the problem persists.")
3198
+ .margin(10, 0, 20, 0)
3199
+ .color(style.text_fg)
3200
+ .font_size(16)
3201
+ .center()),
3202
+ // Image.
3203
+ TableRow(ImageMask(`${this.full_domain}/volt_static/payments/error.png`)
3204
+ .frame(40, 40)
3205
+ .mask_color("#E8454E")
3206
+ .margin(0, 0, 30, 0)).center(),
3207
+ // Title.
3208
+ TableRow(Title("Order Summary")
3209
+ .color(style.subtitle_fg)
3210
+ .font_size(18)
3211
+ .margin(0)),
3212
+ TableRow(Text("A summary of your failed order can be found below.")
3213
+ .margin(5, 0, 20, 0)
3214
+ .color(style.text_fg)
3215
+ .font_size(16)),
3216
+ // Line items.
3217
+ this._render_mail_payment_line_items({ payment, line_items: payment.line_items }),
3218
+ // Bottom spacing.
3219
+ VStack()
3220
+ .margin_bottom(15)
3221
+ ],
3222
+ });
3223
+ }
3224
+ // On cancellation mail.
3225
+ on_cancellation_mail({ payment, line_items }) {
3226
+ // Shortcuts.
3227
+ const style = this.mail_style;
3228
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
3229
+ // Create mail.
3230
+ return this._mail_template({
3231
+ max_width: 800,
3232
+ children: [
3233
+ // Title.
3234
+ TableRow(Title("Successfull Cancellation")
3235
+ .color(style.title_fg)
3236
+ .width("fit-content")
3237
+ .font_size(26)).center(),
3238
+ // Text.
3239
+ TableRow(Text("Your recent cancellation request has been successfully processed.")
3240
+ .margin(10, 0, 20, 0)
3241
+ .color(style.text_fg)
3242
+ .font_size(16)
3243
+ .center()),
3244
+ // Image.
3245
+ TableRow(Image(`${this.full_domain}/volt_static/payments/check.png`)
3246
+ .frame(40, 40)
3247
+ .margin(0, 0, 30, 0)).center(),
3248
+ // Title.
3249
+ TableRow(Title("Cancelled Summary")
3250
+ .color(style.subtitle_fg)
3251
+ .font_size(18)
3252
+ .margin(0)),
3253
+ TableRow(Text("A summary of your cancelled products.")
3254
+ .margin(5, 0, 20, 0)
3255
+ .color(style.text_fg)
3256
+ .font_size(16)),
3257
+ // Line items.
3258
+ this._render_mail_payment_line_items({ payment, line_items }),
3259
+ // Bottom spacing.
3260
+ VStack()
3261
+ .margin_bottom(15)
3262
+ ],
3263
+ });
3264
+ }
3265
+ // On refund mail.
3266
+ on_failed_cancellation_mail({ payment }) {
3267
+ // Shortcuts.
3268
+ const style = this.mail_style;
3269
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
3270
+ // Create mail.
3271
+ return this._mail_template({
3272
+ max_width: 800,
3273
+ children: [
3274
+ // Title.
3275
+ TableRow(Title("Cancellation Failed")
3276
+ .color(style.title_fg)
3277
+ .width("fit-content")
3278
+ .font_size(26)).center(),
3279
+ // Text.
3280
+ TableRow(Text("We regret to inform you that your recent cancellation request has encountered an issue and could not be processed successfully. We understand the inconvenience this may cause. If you believe you are eligible for a cancellation, please try again or contact customer support.")
3281
+ .margin(10, 0, 20, 0)
3282
+ .color(style.text_fg)
3283
+ .font_size(16)
3284
+ .center()).center(),
3285
+ // Image.
3286
+ TableRow(ImageMask(`${this.full_domain}/volt_static/payments/error.png`)
3287
+ .frame(40, 40)
3288
+ .mask_color("#E8454E")
3289
+ .margin(0, 0, 30, 0)).center(),
3290
+ // Title.
3291
+ TableRow(Title("Cancellation Summary")
3292
+ .color(style.subtitle_fg)
3293
+ .font_size(18)
3294
+ .margin(0)),
3295
+ TableRow(Text("A summary of your cancellation request.")
3296
+ .margin(5, 0, 20, 0)
3297
+ .color(style.text_fg)
3298
+ .font_size(16)),
3299
+ // Line items.
3300
+ this._render_mail_payment_line_items({ payment, line_items: payment.line_items }),
3301
+ // Bottom spacing.
3302
+ VStack()
3303
+ .margin_bottom(15)
3304
+ ],
3305
+ });
3306
+ }
3307
+ // On refund mail.
3308
+ on_refund_mail({ payment, line_items }) {
3309
+ // Shortcuts.
3310
+ const style = this.mail_style;
3311
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
3312
+ // Create mail.
3313
+ return this._mail_template({
3314
+ max_width: 800,
3315
+ children: [
3316
+ // Title.
3317
+ TableRow(Title("Successful Refund")
3318
+ .color(style.title_fg)
3319
+ .width("fit-content")
3320
+ .font_size(26)).center(),
3321
+ // Text.
3322
+ TableRow(Text("We're delighted to inform you that your recent refund request has been successfully processed. The charged amount will soon be credited back to your account.")
3323
+ .margin(10, 0, 20, 0)
3324
+ .color(style.text_fg)
3325
+ .font_size(16)
3326
+ .center()),
3327
+ // Image.
3328
+ TableRow(Image(`${this.full_domain}/volt_static/payments/party.png`)
3329
+ .frame(60, 60)
3330
+ .margin(0, 0, 30, 0)).center(),
3331
+ // Title.
3332
+ TableRow(Title("Refund Summary")
3333
+ .color(style.subtitle_fg)
3334
+ .font_size(18)
3335
+ .margin(0)),
3336
+ TableRow(Text("A summary of your refunded products.")
3337
+ .margin(5, 0, 20, 0)
3338
+ .color(style.text_fg)
3339
+ .font_size(16)),
3340
+ // Line items.
3341
+ this._render_mail_payment_line_items({ payment, line_items }),
3342
+ // Bottom spacing.
3343
+ VStack()
3344
+ .margin_bottom(15)
3345
+ ],
3346
+ });
3347
+ }
3348
+ // On refund mail.
3349
+ on_failed_refund_mail({ payment, line_items }) {
3350
+ // Shortcuts.
3351
+ const style = this.mail_style;
3352
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
3353
+ // Create mail.
3354
+ return this._mail_template({
3355
+ max_width: 800,
3356
+ children: [
3357
+ // Title.
3358
+ TableRow(Title("Refund Failed")
3359
+ .color(style.title_fg)
3360
+ .width("fit-content")
3361
+ .font_size(26)).center(),
3362
+ // Text.
3363
+ TableRow(Text("We regret to inform you that your recent refund request has encountered an issue and could not be processed successfully. We understand the inconvenience this may cause. If you believe you are eligible for a refund, please try again or contact customer support.")
3364
+ .margin(10, 0, 20, 0)
3365
+ .color(style.text_fg)
3366
+ .font_size(16)
3367
+ .center()).center(),
3368
+ // Image.
3369
+ TableRow(ImageMask(`${this.full_domain}/volt_static/payments/error.png`)
3370
+ .frame(40, 40)
3371
+ .mask_color("#E8454E")
3372
+ .margin(0, 0, 30, 0)).center(),
3373
+ // Title.
3374
+ TableRow(Title("Refund Summary")
3375
+ .color(style.subtitle_fg)
3376
+ .font_size(18)
3377
+ .margin(0)),
3378
+ TableRow(Text("A summary of your refund request.")
3379
+ .margin(5, 0, 20, 0)
3380
+ .color(style.text_fg)
3381
+ .font_size(16)),
3382
+ // Line items.
3383
+ this._render_mail_payment_line_items({ payment, line_items }),
3384
+ // Bottom spacing.
3385
+ VStack()
3386
+ .margin_bottom(15)
3387
+ ],
3388
+ });
3389
+ }
3390
+ // On refund mail.
3391
+ on_chargeback_mail({ payment, line_items }) {
3392
+ // Shortcuts.
3393
+ const style = this.mail_style;
3394
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
3395
+ // Create mail.
3396
+ return this._mail_template({
3397
+ max_width: 800,
3398
+ children: [
3399
+ // Title.
3400
+ TableRow(Title("Successful Refund")
3401
+ .color(style.title_fg)
3402
+ .width("fit-content")
3403
+ .font_size(26)).center(),
3404
+ // Text.
3405
+ TableRow(Text("We're delighted to inform you that your recent chargeback request has been successfully processed. The charged amount will soon be credited back to your account.")
3406
+ .margin(10, 0, 20, 0)
3407
+ .color(style.text_fg)
3408
+ .font_size(16)
3409
+ .center()),
3410
+ // Image.
3411
+ TableRow(Image(`${this.full_domain}/volt_static/payments/party.png`)
3412
+ .frame(60, 60)
3413
+ .margin(0, 0, 30, 0)).center(),
3414
+ // Title.
3415
+ TableRow(Title("Chargeback Summary")
3416
+ .color(style.subtitle_fg)
3417
+ .font_size(18)
3418
+ .margin(0)),
3419
+ TableRow(Text("A summary of your refundend products.")
3420
+ .margin(5, 0, 20, 0)
3421
+ .color(style.text_fg)
3422
+ .font_size(16)),
3423
+ // Line items.
3424
+ this._render_mail_payment_line_items({ payment, line_items }),
3425
+ // Bottom spacing.
3426
+ VStack()
3427
+ .margin_bottom(15)
3428
+ ],
3429
+ });
3430
+ }
3431
+ // On refund mail.
3432
+ on_failed_chargeback_mail({ payment, line_items }) {
3433
+ // Shortcuts.
3434
+ const style = this.mail_style;
3435
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
3436
+ // Create mail.
3437
+ return this._mail_template({
3438
+ max_width: 800,
3439
+ children: [
3440
+ // Title.
3441
+ TableRow(Title("Chargeback Failed")
3442
+ .color(style.title_fg)
3443
+ .width("fit-content")
3444
+ .font_size(26)).center(),
3445
+ // Text.
3446
+ TableRow(Text("We regret to inform you that your recent chargeback request has been declined.")
3447
+ .margin(10, 0, 20, 0)
3448
+ .color(style.text_fg)
3449
+ .font_size(16)
3450
+ .center()).center(),
3451
+ // Image.
3452
+ TableRow(ImageMask(`${this.full_domain}/volt_static/payments/error.png`)
3453
+ .frame(40, 40)
3454
+ .mask_color("#E8454E")
3455
+ .margin(0, 0, 30, 0)).center(),
3456
+ // Title.
3457
+ TableRow(Title("Chargeback Summary")
3458
+ .color(style.subtitle_fg)
3459
+ .font_size(18)
3460
+ .margin(0)),
3461
+ TableRow(Text("A summary of your chargeback request.")
3462
+ .margin(5, 0, 20, 0)
3463
+ .color(style.text_fg)
3464
+ .font_size(16)),
3465
+ // Line items.
3466
+ this._render_mail_payment_line_items({ payment, line_items }),
3467
+ // Bottom spacing.
3468
+ VStack()
3469
+ .margin_bottom(15)
3470
+ ],
3471
+ });
3472
+ }
3473
+ }
3474
+ exports.Server = Server;
3475
+ exports.default = Server;