@vandenberghinc/volt 1.1.26 → 1.1.28

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 (502) hide show
  1. package/backend/dist/cjs/{blacklist.d.ts → backend/src/blacklist.d.ts} +5 -3
  2. package/backend/dist/cjs/{blacklist.js → backend/src/blacklist.js} +8 -5
  3. package/backend/dist/cjs/{cli.js → backend/src/cli.js} +29 -47
  4. package/backend/dist/cjs/backend/src/database/collection.d.ts +1543 -0
  5. package/backend/dist/cjs/backend/src/database/collection.js +3042 -0
  6. package/backend/dist/cjs/backend/src/database/database.d.ts +66 -0
  7. package/backend/dist/cjs/{database → backend/src/database}/database.js +48 -43
  8. package/backend/dist/cjs/backend/src/database/filters/filters.d.ts +6 -0
  9. package/backend/dist/cjs/backend/src/database/filters/filters.js +15 -0
  10. package/backend/dist/cjs/backend/src/database/filters/strict_filter.d.ts +223 -0
  11. package/backend/dist/cjs/backend/src/database/filters/strict_filter.js +15 -0
  12. package/backend/dist/cjs/backend/src/database/filters/strict_filter_test.js +443 -0
  13. package/backend/dist/cjs/backend/src/database/filters/strict_filter_test_v0.js +15 -0
  14. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v0.d.ts +50 -0
  15. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v0.js +15 -0
  16. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v1.d.ts +76 -0
  17. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v1.js +15 -0
  18. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v2.d.ts +75 -0
  19. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v2.js +15 -0
  20. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v3.d.ts +219 -0
  21. package/backend/dist/cjs/backend/src/database/filters/strict_filter_v3.js +15 -0
  22. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter.d.ts +165 -0
  23. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter.js +15 -0
  24. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter_test.d.ts +5 -0
  25. package/backend/dist/cjs/backend/src/database/filters/strict_update_filter_test.js +355 -0
  26. package/backend/dist/cjs/backend/src/database/flatten.d.ts +75 -0
  27. package/backend/dist/cjs/{logger.js → backend/src/database/flatten.js} +18 -7
  28. package/backend/dist/cjs/backend/src/database/flatten_test.js +175 -0
  29. package/backend/dist/cjs/backend/src/database/quota/quota.d.ts +461 -0
  30. package/backend/dist/cjs/backend/src/database/quota/quota.js +1014 -0
  31. package/backend/dist/cjs/backend/src/database/quota/quota_v1.d.ts +534 -0
  32. package/backend/dist/cjs/backend/src/database/quota/quota_v1.js +1087 -0
  33. package/backend/dist/cjs/backend/src/database/quota/safe_int.d.ts +293 -0
  34. package/backend/dist/cjs/backend/src/database/quota/safe_int.js +573 -0
  35. package/backend/dist/{esm → cjs/backend/src}/endpoint.d.ts +69 -46
  36. package/backend/dist/cjs/{endpoint.js → backend/src/endpoint.js} +87 -101
  37. package/backend/dist/cjs/backend/src/errors/index.d.ts +7 -0
  38. package/backend/dist/cjs/backend/src/errors/index.js +25 -0
  39. package/backend/dist/{esm/utils.d.ts → cjs/backend/src/errors/internal_external.d.ts} +14 -22
  40. package/backend/dist/cjs/backend/src/errors/internal_external.js +85 -0
  41. package/backend/dist/cjs/backend/src/errors/invalid_usage_error.d.ts +38 -0
  42. package/backend/dist/cjs/{mutex.js → backend/src/errors/invalid_usage_error.js} +20 -37
  43. package/backend/dist/cjs/backend/src/errors/system_error.d.ts +230 -0
  44. package/backend/dist/cjs/backend/src/errors/system_error.js +393 -0
  45. package/backend/dist/cjs/backend/src/events.d.ts +54 -0
  46. package/backend/dist/cjs/backend/src/events.js +15 -0
  47. package/backend/dist/cjs/{frontend.js → backend/src/frontend.js} +1 -1
  48. package/backend/dist/cjs/{image_endpoint.d.ts → backend/src/image_endpoint.d.ts} +16 -1
  49. package/backend/dist/cjs/{image_endpoint.js → backend/src/image_endpoint.js} +3 -5
  50. package/backend/dist/cjs/backend/src/logger.d.ts +5 -0
  51. package/backend/dist/cjs/backend/src/logger.js +15 -0
  52. package/backend/dist/cjs/backend/src/meta.d.ts +64 -0
  53. package/backend/dist/cjs/{meta.js → backend/src/meta.js} +9 -12
  54. package/backend/dist/cjs/backend/src/payments/paddle.d.ts +326 -0
  55. package/backend/dist/cjs/{payments → backend/src/payments}/paddle.js +377 -327
  56. package/backend/dist/cjs/backend/src/plugins/browser.d.ts +1 -0
  57. package/backend/dist/cjs/backend/src/plugins/browser.js +15 -0
  58. package/backend/dist/cjs/backend/src/plugins/mail/mail.d.ts +248 -0
  59. package/backend/dist/cjs/backend/src/plugins/mail/mail.js +379 -0
  60. package/backend/dist/{esm → cjs/backend/src}/plugins/mail/ui.d.ts +23 -0
  61. package/backend/dist/cjs/backend/src/plugins/pdf.d.ts +1 -0
  62. package/backend/dist/cjs/backend/src/rate_limit.d.ts +145 -0
  63. package/backend/dist/cjs/backend/src/rate_limit.js +549 -0
  64. package/backend/dist/cjs/{route.d.ts → backend/src/route.d.ts} +3 -10
  65. package/backend/dist/cjs/{route.js → backend/src/route.js} +23 -21
  66. package/backend/dist/cjs/backend/src/server.d.ts +485 -0
  67. package/backend/dist/cjs/{server.js → backend/src/server.js} +688 -873
  68. package/backend/dist/cjs/backend/src/splash_screen.d.ts +80 -0
  69. package/backend/dist/cjs/{splash_screen.js → backend/src/splash_screen.js} +24 -3
  70. package/backend/dist/cjs/backend/src/status.d.ts +74 -0
  71. package/backend/dist/cjs/{status.js → backend/src/status.js} +64 -64
  72. package/backend/dist/cjs/backend/src/stream.d.ts +376 -0
  73. package/backend/dist/cjs/{stream.js → backend/src/stream.js} +299 -276
  74. package/backend/dist/cjs/backend/src/users.d.ts +807 -0
  75. package/backend/dist/cjs/backend/src/users.js +1971 -0
  76. package/backend/dist/cjs/backend/src/utils.d.ts +16 -0
  77. package/backend/dist/cjs/{utils.js → backend/src/utils.js} +14 -77
  78. package/backend/dist/{esm → cjs/backend/src}/view.d.ts +33 -11
  79. package/backend/dist/cjs/backend/src/view.js +508 -0
  80. package/backend/dist/{esm → cjs/backend/src}/volt.d.ts +10 -1
  81. package/backend/dist/cjs/{volt.js → backend/src/volt.js} +8 -5
  82. package/backend/dist/cjs/frontend/src/modules/request.d.ts +70 -0
  83. package/backend/dist/cjs/frontend/src/modules/request.js +99 -0
  84. package/backend/dist/esm/{blacklist.d.ts → backend/src/blacklist.d.ts} +5 -3
  85. package/backend/dist/esm/{blacklist.js → backend/src/blacklist.js} +9 -6
  86. package/backend/dist/esm/{cli.js → backend/src/cli.js} +43 -60
  87. package/backend/dist/esm/backend/src/database/collection.d.ts +1543 -0
  88. package/backend/dist/esm/backend/src/database/collection.js +3510 -0
  89. package/backend/dist/esm/backend/src/database/database.d.ts +66 -0
  90. package/backend/dist/esm/{database → backend/src/database}/database.js +62 -103
  91. package/backend/dist/esm/backend/src/database/document.d.ts +1 -0
  92. package/backend/dist/esm/backend/src/database/document.js +558 -0
  93. package/backend/dist/esm/backend/src/database/filters/filters.d.ts +6 -0
  94. package/backend/dist/esm/backend/src/database/filters/filters.js +1 -0
  95. package/backend/dist/esm/backend/src/database/filters/strict_filter.d.ts +223 -0
  96. package/backend/dist/esm/backend/src/database/filters/strict_filter.js +3 -0
  97. package/backend/dist/esm/backend/src/database/filters/strict_filter_test.d.ts +1 -0
  98. package/backend/dist/esm/backend/src/database/filters/strict_filter_test.js +505 -0
  99. package/backend/dist/esm/backend/src/database/filters/strict_filter_test_v0.d.ts +1 -0
  100. package/backend/dist/esm/backend/src/database/filters/strict_filter_test_v0.js +712 -0
  101. package/backend/dist/esm/backend/src/database/filters/strict_filter_v0.d.ts +50 -0
  102. package/backend/dist/esm/backend/src/database/filters/strict_filter_v0.js +5 -0
  103. package/backend/dist/esm/backend/src/database/filters/strict_filter_v1.d.ts +76 -0
  104. package/backend/dist/esm/backend/src/database/filters/strict_filter_v1.js +44 -0
  105. package/backend/dist/esm/backend/src/database/filters/strict_filter_v2.d.ts +75 -0
  106. package/backend/dist/esm/backend/src/database/filters/strict_filter_v2.js +5 -0
  107. package/backend/dist/esm/backend/src/database/filters/strict_filter_v3.d.ts +219 -0
  108. package/backend/dist/esm/backend/src/database/filters/strict_filter_v3.js +1 -0
  109. package/backend/dist/esm/backend/src/database/filters/strict_update_filter.d.ts +165 -0
  110. package/backend/dist/esm/backend/src/database/filters/strict_update_filter.js +5 -0
  111. package/backend/dist/esm/backend/src/database/filters/strict_update_filter_test.d.ts +5 -0
  112. package/backend/dist/esm/backend/src/database/filters/strict_update_filter_test.js +405 -0
  113. package/backend/dist/esm/backend/src/database/flatten.d.ts +75 -0
  114. package/backend/dist/esm/backend/src/database/flatten.js +22 -0
  115. package/backend/dist/esm/backend/src/database/flatten_test.d.ts +1 -0
  116. package/backend/dist/esm/backend/src/database/flatten_test.js +174 -0
  117. package/backend/dist/esm/backend/src/database/quota/quota.d.ts +461 -0
  118. package/backend/dist/esm/backend/src/database/quota/quota.js +1118 -0
  119. package/backend/dist/esm/backend/src/database/quota/quota_v1.d.ts +534 -0
  120. package/backend/dist/esm/backend/src/database/quota/quota_v1.js +1242 -0
  121. package/backend/dist/esm/backend/src/database/quota/safe_int.d.ts +293 -0
  122. package/backend/dist/esm/backend/src/database/quota/safe_int.js +602 -0
  123. package/backend/dist/{cjs → esm/backend/src}/endpoint.d.ts +69 -46
  124. package/backend/dist/esm/{endpoint.js → backend/src/endpoint.js} +136 -127
  125. package/backend/dist/esm/backend/src/errors/index.d.ts +7 -0
  126. package/backend/dist/esm/backend/src/errors/index.js +7 -0
  127. package/backend/dist/{cjs/utils.d.ts → esm/backend/src/errors/internal_external.d.ts} +14 -22
  128. package/backend/dist/esm/backend/src/errors/internal_external.js +70 -0
  129. package/backend/dist/esm/backend/src/errors/invalid_usage_error.d.ts +38 -0
  130. package/backend/dist/esm/backend/src/errors/invalid_usage_error.js +30 -0
  131. package/backend/dist/esm/backend/src/errors/system_error.d.ts +230 -0
  132. package/backend/dist/esm/backend/src/errors/system_error.js +402 -0
  133. package/backend/dist/esm/backend/src/events.d.ts +54 -0
  134. package/backend/dist/esm/backend/src/events.js +5 -0
  135. package/backend/dist/esm/{frontend.js → backend/src/frontend.js} +1 -1
  136. package/backend/dist/esm/{image_endpoint.d.ts → backend/src/image_endpoint.d.ts} +16 -1
  137. package/backend/dist/esm/{image_endpoint.js → backend/src/image_endpoint.js} +16 -20
  138. package/backend/dist/esm/backend/src/logger.d.ts +5 -0
  139. package/backend/dist/esm/backend/src/logger.js +8 -0
  140. package/backend/dist/esm/backend/src/meta.d.ts +64 -0
  141. package/backend/dist/esm/{meta.js → backend/src/meta.js} +15 -54
  142. package/backend/dist/esm/backend/src/payments/paddle.d.ts +326 -0
  143. package/backend/dist/esm/{payments → backend/src/payments}/paddle.js +417 -452
  144. package/backend/dist/esm/backend/src/plugins/browser.d.ts +1 -0
  145. package/backend/dist/esm/backend/src/plugins/browser.js +170 -0
  146. package/backend/dist/esm/backend/src/plugins/mail/mail.d.ts +248 -0
  147. package/backend/dist/esm/backend/src/plugins/mail/mail.js +389 -0
  148. package/backend/dist/{cjs → esm/backend/src}/plugins/mail/ui.d.ts +23 -0
  149. package/backend/dist/esm/{plugins → backend/src/plugins}/mail/ui.js +3 -6
  150. package/backend/dist/esm/backend/src/plugins/pdf.d.ts +1 -0
  151. package/backend/dist/esm/{plugins → backend/src/plugins}/pdf.js +3 -3
  152. package/backend/dist/esm/backend/src/rate_limit.d.ts +145 -0
  153. package/backend/dist/esm/backend/src/rate_limit.js +667 -0
  154. package/backend/dist/esm/{route.d.ts → backend/src/route.d.ts} +3 -10
  155. package/backend/dist/esm/{route.js → backend/src/route.js} +26 -21
  156. package/backend/dist/esm/backend/src/server.d.ts +485 -0
  157. package/backend/dist/esm/{server.js → backend/src/server.js} +891 -1441
  158. package/backend/dist/esm/backend/src/splash_screen.d.ts +80 -0
  159. package/backend/dist/esm/{splash_screen.js → backend/src/splash_screen.js} +42 -55
  160. package/backend/dist/esm/backend/src/status.d.ts +74 -0
  161. package/backend/dist/esm/backend/src/status.js +199 -0
  162. package/backend/dist/esm/backend/src/stream.d.ts +376 -0
  163. package/backend/dist/esm/{stream.js → backend/src/stream.js} +327 -292
  164. package/backend/dist/esm/backend/src/users.d.ts +809 -0
  165. package/backend/dist/esm/backend/src/users.js +2140 -0
  166. package/backend/dist/esm/backend/src/utils.d.ts +16 -0
  167. package/backend/dist/esm/{utils.js → backend/src/utils.js} +20 -81
  168. package/backend/dist/{cjs → esm/backend/src}/view.d.ts +33 -11
  169. package/backend/dist/esm/{view.js → backend/src/view.js} +266 -86
  170. package/backend/dist/{cjs → esm/backend/src}/volt.d.ts +10 -1
  171. package/backend/dist/esm/{volt.js → backend/src/volt.js} +7 -4
  172. package/backend/dist/esm/frontend/src/modules/request.d.ts +70 -0
  173. package/backend/dist/esm/frontend/src/modules/request.js +117 -0
  174. package/frontend/dist/backend/src/database/collection.d.ts +1543 -0
  175. package/frontend/dist/backend/src/database/collection.js +3510 -0
  176. package/frontend/dist/backend/src/database/database.d.ts +66 -0
  177. package/frontend/dist/backend/src/database/database.js +196 -0
  178. package/frontend/dist/backend/src/database/filters/filters.d.ts +6 -0
  179. package/frontend/dist/backend/src/database/filters/filters.js +1 -0
  180. package/frontend/dist/backend/src/database/filters/strict_filter.d.ts +223 -0
  181. package/frontend/dist/backend/src/database/filters/strict_filter.js +3 -0
  182. package/frontend/dist/backend/src/database/filters/strict_update_filter.d.ts +165 -0
  183. package/frontend/dist/backend/src/database/filters/strict_update_filter.js +5 -0
  184. package/frontend/dist/backend/src/database/flatten.d.ts +75 -0
  185. package/frontend/dist/backend/src/database/flatten.js +22 -0
  186. package/frontend/dist/backend/src/endpoint.d.ts +204 -0
  187. package/frontend/dist/backend/src/endpoint.js +570 -0
  188. package/frontend/dist/backend/src/errors/index.d.ts +7 -0
  189. package/frontend/dist/backend/src/errors/index.js +7 -0
  190. package/frontend/dist/backend/src/errors/internal_external.d.ts +38 -0
  191. package/frontend/dist/backend/src/errors/internal_external.js +70 -0
  192. package/frontend/dist/backend/src/errors/invalid_usage_error.d.ts +38 -0
  193. package/frontend/dist/backend/src/errors/invalid_usage_error.js +30 -0
  194. package/frontend/dist/backend/src/errors/system_error.d.ts +230 -0
  195. package/frontend/dist/backend/src/errors/system_error.js +402 -0
  196. package/frontend/dist/backend/src/events.d.ts +54 -0
  197. package/frontend/dist/backend/src/events.js +5 -0
  198. package/frontend/dist/backend/src/frontend.d.ts +11 -0
  199. package/frontend/dist/backend/src/frontend.js +12 -0
  200. package/frontend/dist/backend/src/image_endpoint.d.ts +39 -0
  201. package/frontend/dist/backend/src/image_endpoint.js +202 -0
  202. package/frontend/dist/backend/src/meta.d.ts +64 -0
  203. package/frontend/dist/backend/src/meta.js +110 -0
  204. package/frontend/dist/backend/src/payments/paddle.d.ts +326 -0
  205. package/frontend/dist/backend/src/payments/paddle.js +2256 -0
  206. package/frontend/dist/backend/src/plugins/mail/mail.d.ts +248 -0
  207. package/frontend/dist/backend/src/plugins/mail/mail.js +389 -0
  208. package/{backend/dist/esm/plugins/mail.d.ts → frontend/dist/backend/src/plugins/mail/ui.d.ts} +23 -0
  209. package/{backend/dist/esm/plugins/mail.js → frontend/dist/backend/src/plugins/mail/ui.js} +3 -6
  210. package/frontend/dist/backend/src/rate_limit.d.ts +145 -0
  211. package/frontend/dist/backend/src/rate_limit.js +673 -0
  212. package/frontend/dist/backend/src/route.d.ts +35 -0
  213. package/frontend/dist/backend/src/route.js +212 -0
  214. package/frontend/dist/backend/src/server.d.ts +485 -0
  215. package/frontend/dist/backend/src/server.js +2670 -0
  216. package/frontend/dist/backend/src/splash_screen.d.ts +80 -0
  217. package/frontend/dist/backend/src/splash_screen.js +135 -0
  218. package/frontend/dist/backend/src/status.d.ts +74 -0
  219. package/frontend/dist/backend/src/status.js +199 -0
  220. package/frontend/dist/backend/src/stream.d.ts +376 -0
  221. package/frontend/dist/backend/src/stream.js +1007 -0
  222. package/frontend/dist/backend/src/users.d.ts +807 -0
  223. package/frontend/dist/backend/src/users.js +2118 -0
  224. package/frontend/dist/backend/src/utils.d.ts +16 -0
  225. package/frontend/dist/backend/src/utils.js +241 -0
  226. package/frontend/dist/backend/src/view.d.ts +162 -0
  227. package/frontend/dist/backend/src/view.js +720 -0
  228. package/frontend/dist/frontend/src/elements/base.d.ts +4414 -0
  229. package/frontend/dist/{elements → frontend/src/elements}/base.js +3624 -260
  230. package/frontend/dist/frontend/src/elements/module.d.ts +95 -0
  231. package/frontend/dist/{elements → frontend/src/elements}/module.js +53 -52
  232. package/frontend/dist/frontend/src/elements/types.d.ts +52 -0
  233. package/frontend/dist/frontend/src/elements/types.js +5 -0
  234. package/frontend/dist/frontend/src/modules/attachment.d.ts +126 -0
  235. package/frontend/dist/frontend/src/modules/attachment.js +306 -0
  236. package/frontend/dist/frontend/src/modules/auth.d.ts +44 -0
  237. package/frontend/dist/frontend/src/modules/auth.js +80 -0
  238. package/frontend/dist/{modules → frontend/src/modules}/color.js +2 -2
  239. package/frontend/dist/frontend/src/modules/compression.d.ts +39 -0
  240. package/frontend/dist/frontend/src/modules/compression.js +102 -0
  241. package/frontend/dist/frontend/src/modules/cookies.d.ts +44 -0
  242. package/frontend/dist/frontend/src/modules/cookies.js +143 -0
  243. package/frontend/dist/frontend/src/modules/events.d.ts +31 -0
  244. package/frontend/dist/frontend/src/modules/events.js +74 -0
  245. package/frontend/dist/frontend/src/modules/google.d.ts +23 -0
  246. package/frontend/dist/frontend/src/modules/google.js +52 -0
  247. package/frontend/dist/frontend/src/modules/meta.d.ts +14 -0
  248. package/frontend/dist/{modules → frontend/src/modules}/meta.js +9 -7
  249. package/frontend/dist/{modules → frontend/src/modules}/paddle.d.ts +37 -134
  250. package/frontend/dist/{modules → frontend/src/modules}/paddle.js +620 -568
  251. package/frontend/dist/frontend/src/modules/request.d.ts +70 -0
  252. package/frontend/dist/frontend/src/modules/request.js +117 -0
  253. package/frontend/dist/frontend/src/modules/settings.d.ts +3 -0
  254. package/frontend/dist/frontend/src/modules/settings.js +5 -0
  255. package/frontend/dist/frontend/src/modules/statics.d.ts +21 -0
  256. package/frontend/dist/{modules → frontend/src/modules}/statics.js +15 -18
  257. package/frontend/dist/frontend/src/modules/support.d.ts +30 -0
  258. package/frontend/dist/frontend/src/modules/support.js +53 -0
  259. package/frontend/dist/{modules → frontend/src/modules}/theme.d.ts +67 -0
  260. package/frontend/dist/{modules → frontend/src/modules}/theme.js +68 -38
  261. package/frontend/dist/frontend/src/modules/themes.d.ts +12 -0
  262. package/frontend/dist/frontend/src/modules/themes.js +22 -0
  263. package/frontend/dist/frontend/src/modules/user.d.ts +164 -0
  264. package/frontend/dist/frontend/src/modules/user.js +268 -0
  265. package/frontend/dist/frontend/src/modules/utils.d.ts +176 -0
  266. package/frontend/dist/frontend/src/modules/utils.js +569 -0
  267. package/frontend/dist/frontend/src/types/gradient.d.ts +29 -0
  268. package/frontend/dist/{types → frontend/src/types}/gradient.js +14 -18
  269. package/frontend/dist/frontend/src/ui/border_button.d.ts +94 -0
  270. package/frontend/dist/{ui → frontend/src/ui}/border_button.js +7 -13
  271. package/frontend/dist/frontend/src/ui/button.d.ts +28 -0
  272. package/frontend/dist/{ui → frontend/src/ui}/button.js +21 -12
  273. package/frontend/dist/frontend/src/ui/canvas.d.ts +138 -0
  274. package/frontend/dist/{ui → frontend/src/ui}/canvas.js +88 -55
  275. package/frontend/dist/frontend/src/ui/checkbox.d.ts +74 -0
  276. package/frontend/dist/{ui → frontend/src/ui}/checkbox.js +80 -41
  277. package/frontend/dist/{ui → frontend/src/ui}/code.d.ts +73 -6
  278. package/frontend/dist/{ui → frontend/src/ui}/code.js +55 -52
  279. package/frontend/dist/{ui → frontend/src/ui}/context_menu.d.ts +4 -0
  280. package/frontend/dist/{ui → frontend/src/ui}/context_menu.js +12 -17
  281. package/frontend/dist/{ui → frontend/src/ui}/css.d.ts +4 -0
  282. package/frontend/dist/{ui → frontend/src/ui}/css.js +3 -3
  283. package/frontend/dist/{ui → frontend/src/ui}/divider.d.ts +4 -0
  284. package/frontend/dist/{ui → frontend/src/ui}/divider.js +3 -3
  285. package/frontend/dist/{ui → frontend/src/ui}/dropdown.d.ts +57 -2
  286. package/frontend/dist/{ui → frontend/src/ui}/dropdown.js +87 -94
  287. package/frontend/dist/{ui → frontend/src/ui}/for_each.d.ts +4 -0
  288. package/frontend/dist/{ui → frontend/src/ui}/for_each.js +3 -3
  289. package/frontend/dist/{ui → frontend/src/ui}/form.d.ts +6 -2
  290. package/frontend/dist/{ui → frontend/src/ui}/form.js +10 -7
  291. package/frontend/dist/frontend/src/ui/frame_modes.d.ts +37 -0
  292. package/frontend/dist/{ui → frontend/src/ui}/frame_modes.js +16 -22
  293. package/frontend/dist/{ui → frontend/src/ui}/google_map.d.ts +4 -0
  294. package/frontend/dist/{ui → frontend/src/ui}/google_map.js +4 -4
  295. package/frontend/dist/{ui → frontend/src/ui}/gradient.d.ts +4 -0
  296. package/frontend/dist/{ui → frontend/src/ui}/gradient.js +3 -3
  297. package/frontend/dist/{ui → frontend/src/ui}/image.d.ts +4 -0
  298. package/frontend/dist/{ui → frontend/src/ui}/image.js +5 -5
  299. package/frontend/dist/frontend/src/ui/input.d.ts +392 -0
  300. package/frontend/dist/{ui → frontend/src/ui}/input.js +346 -360
  301. package/frontend/dist/{ui → frontend/src/ui}/link.d.ts +4 -0
  302. package/frontend/dist/{ui → frontend/src/ui}/link.js +3 -3
  303. package/frontend/dist/{ui → frontend/src/ui}/list.d.ts +4 -0
  304. package/frontend/dist/{ui → frontend/src/ui}/list.js +12 -6
  305. package/frontend/dist/frontend/src/ui/loader_button.d.ts +80 -0
  306. package/frontend/dist/{ui → frontend/src/ui}/loader_button.js +35 -47
  307. package/frontend/dist/frontend/src/ui/loaders.d.ts +57 -0
  308. package/frontend/dist/{ui → frontend/src/ui}/loaders.js +11 -11
  309. package/frontend/dist/{ui → frontend/src/ui}/popup.d.ts +11 -6
  310. package/frontend/dist/{ui → frontend/src/ui}/popup.js +32 -18
  311. package/frontend/dist/frontend/src/ui/pseudo.d.ts +44 -0
  312. package/frontend/dist/{ui → frontend/src/ui}/pseudo.js +84 -8
  313. package/frontend/dist/{ui → frontend/src/ui}/scroller.d.ts +14 -2
  314. package/frontend/dist/{ui → frontend/src/ui}/scroller.js +37 -43
  315. package/frontend/dist/{ui → frontend/src/ui}/slider.d.ts +5 -1
  316. package/frontend/dist/{ui → frontend/src/ui}/slider.js +4 -4
  317. package/frontend/dist/{ui → frontend/src/ui}/spacer.d.ts +4 -0
  318. package/frontend/dist/{ui → frontend/src/ui}/spacer.js +3 -3
  319. package/frontend/dist/{ui → frontend/src/ui}/span.d.ts +4 -0
  320. package/frontend/dist/{ui → frontend/src/ui}/span.js +3 -3
  321. package/frontend/dist/{ui → frontend/src/ui}/stack.d.ts +4 -0
  322. package/frontend/dist/{ui → frontend/src/ui}/stack.js +3 -9
  323. package/frontend/dist/frontend/src/ui/steps.d.ts +131 -0
  324. package/frontend/dist/{ui → frontend/src/ui}/steps.js +30 -45
  325. package/frontend/dist/{ui → frontend/src/ui}/style.d.ts +4 -0
  326. package/frontend/dist/{ui → frontend/src/ui}/style.js +3 -3
  327. package/frontend/dist/{ui → frontend/src/ui}/switch.d.ts +5 -1
  328. package/frontend/dist/{ui → frontend/src/ui}/switch.js +4 -4
  329. package/frontend/dist/{ui → frontend/src/ui}/table.d.ts +4 -0
  330. package/frontend/dist/{ui → frontend/src/ui}/table.js +6 -6
  331. package/frontend/dist/{ui → frontend/src/ui}/tabs.d.ts +45 -3
  332. package/frontend/dist/{ui → frontend/src/ui}/tabs.js +65 -40
  333. package/frontend/dist/{ui → frontend/src/ui}/text.d.ts +4 -0
  334. package/frontend/dist/{ui → frontend/src/ui}/text.js +3 -3
  335. package/frontend/dist/frontend/src/ui/title.d.ts +91 -0
  336. package/frontend/dist/frontend/src/ui/title.js +272 -0
  337. package/frontend/dist/{ui → frontend/src/ui}/view.d.ts +4 -0
  338. package/frontend/dist/{ui → frontend/src/ui}/view.js +3 -3
  339. package/frontend/dist/{volt.d.ts → frontend/src/volt.d.ts} +3 -0
  340. package/frontend/dist/{volt.js → frontend/src/volt.js} +4 -0
  341. package/frontend/tools/bundle_d_ts.js +71 -0
  342. package/frontend/tools/convert_to_jsdoc_input.txt +9452 -0
  343. package/frontend/tools/convert_to_jsdoc_output.txt +7626 -0
  344. package/frontend/tools/convert_to_jsdoc_tmp.js +345 -0
  345. package/package.json +11 -12
  346. package/backend/dist/cjs/database/collection.d.ts +0 -160
  347. package/backend/dist/cjs/database/collection.js +0 -842
  348. package/backend/dist/cjs/database/database.d.ts +0 -121
  349. package/backend/dist/cjs/database/document.d.ts +0 -131
  350. package/backend/dist/cjs/database/document.js +0 -224
  351. package/backend/dist/cjs/database.d.ts +0 -502
  352. package/backend/dist/cjs/database.js +0 -2248
  353. package/backend/dist/cjs/logger.d.ts +0 -3
  354. package/backend/dist/cjs/meta.d.ts +0 -50
  355. package/backend/dist/cjs/mutex.d.ts +0 -24
  356. package/backend/dist/cjs/payments/paddle.d.ts +0 -160
  357. package/backend/dist/cjs/plugins/browser.d.ts +0 -36
  358. package/backend/dist/cjs/plugins/browser.js +0 -198
  359. package/backend/dist/cjs/plugins/css.d.ts +0 -11
  360. package/backend/dist/cjs/plugins/css.js +0 -80
  361. package/backend/dist/cjs/plugins/mail.d.ts +0 -277
  362. package/backend/dist/cjs/plugins/mail.js +0 -1370
  363. package/backend/dist/cjs/plugins/ts/compiler.d.ts +0 -139
  364. package/backend/dist/cjs/plugins/ts/compiler.js +0 -750
  365. package/backend/dist/cjs/plugins/ts/preprocessing.d.ts +0 -14
  366. package/backend/dist/cjs/plugins/ts/preprocessing.js +0 -440
  367. package/backend/dist/cjs/rate_limit.d.ts +0 -63
  368. package/backend/dist/cjs/rate_limit.js +0 -348
  369. package/backend/dist/cjs/request.deprc.d.ts +0 -48
  370. package/backend/dist/cjs/request.deprc.js +0 -572
  371. package/backend/dist/cjs/response.deprc.d.ts +0 -55
  372. package/backend/dist/cjs/response.deprc.js +0 -275
  373. package/backend/dist/cjs/server.d.ts +0 -342
  374. package/backend/dist/cjs/splash_screen.d.ts +0 -35
  375. package/backend/dist/cjs/status.d.ts +0 -61
  376. package/backend/dist/cjs/stream.d.ts +0 -79
  377. package/backend/dist/cjs/users.d.ts +0 -111
  378. package/backend/dist/cjs/users.js +0 -1817
  379. package/backend/dist/cjs/view.js +0 -352
  380. package/backend/dist/cjs/vinc.dev.d.ts +0 -3
  381. package/backend/dist/cjs/vinc.dev.js +0 -7
  382. package/backend/dist/css/adyen.css +0 -92
  383. package/backend/dist/css/volt.css +0 -70
  384. package/backend/dist/esm/database/collection.d.ts +0 -160
  385. package/backend/dist/esm/database/collection.js +0 -1328
  386. package/backend/dist/esm/database/database.d.ts +0 -121
  387. package/backend/dist/esm/database/document.d.ts +0 -131
  388. package/backend/dist/esm/database/document.js +0 -247
  389. package/backend/dist/esm/database.d.ts +0 -502
  390. package/backend/dist/esm/database.js +0 -2423
  391. package/backend/dist/esm/file_watcher.js +0 -329
  392. package/backend/dist/esm/logger.d.ts +0 -3
  393. package/backend/dist/esm/logger.js +0 -11
  394. package/backend/dist/esm/meta.d.ts +0 -50
  395. package/backend/dist/esm/mutex.d.ts +0 -24
  396. package/backend/dist/esm/mutex.js +0 -48
  397. package/backend/dist/esm/payments/paddle.d.ts +0 -160
  398. package/backend/dist/esm/plugins/browser.d.ts +0 -36
  399. package/backend/dist/esm/plugins/browser.js +0 -176
  400. package/backend/dist/esm/plugins/css.d.ts +0 -11
  401. package/backend/dist/esm/plugins/css.js +0 -90
  402. package/backend/dist/esm/plugins/ts/compiler.d.ts +0 -139
  403. package/backend/dist/esm/plugins/ts/compiler.js +0 -1194
  404. package/backend/dist/esm/plugins/ts/preprocessing.d.ts +0 -14
  405. package/backend/dist/esm/plugins/ts/preprocessing.js +0 -726
  406. package/backend/dist/esm/rate_limit.d.ts +0 -63
  407. package/backend/dist/esm/rate_limit.js +0 -417
  408. package/backend/dist/esm/request.deprc.d.ts +0 -48
  409. package/backend/dist/esm/request.deprc.js +0 -572
  410. package/backend/dist/esm/response.deprc.d.ts +0 -55
  411. package/backend/dist/esm/response.deprc.js +0 -275
  412. package/backend/dist/esm/server.d.ts +0 -342
  413. package/backend/dist/esm/splash_screen.d.ts +0 -35
  414. package/backend/dist/esm/status.d.ts +0 -61
  415. package/backend/dist/esm/status.js +0 -197
  416. package/backend/dist/esm/stream.d.ts +0 -79
  417. package/backend/dist/esm/users.d.ts +0 -111
  418. package/backend/dist/esm/users.js +0 -1935
  419. package/backend/dist/esm/vinc.dev.d.ts +0 -3
  420. package/backend/dist/esm/vinc.dev.js +0 -7
  421. package/frontend/dist/elements/base.d.ts +0 -9889
  422. package/frontend/dist/elements/module.d.ts +0 -30
  423. package/frontend/dist/modules/array.d.ts +0 -94
  424. package/frontend/dist/modules/array.js +0 -634
  425. package/frontend/dist/modules/auth.d.ts +0 -46
  426. package/frontend/dist/modules/auth.js +0 -139
  427. package/frontend/dist/modules/colors.d.ts +0 -1
  428. package/frontend/dist/modules/colors.js +0 -417
  429. package/frontend/dist/modules/compression.d.ts +0 -6
  430. package/frontend/dist/modules/compression.js +0 -999
  431. package/frontend/dist/modules/cookies.d.ts +0 -18
  432. package/frontend/dist/modules/cookies.js +0 -167
  433. package/frontend/dist/modules/date.d.ts +0 -142
  434. package/frontend/dist/modules/date.js +0 -493
  435. package/frontend/dist/modules/events.d.ts +0 -8
  436. package/frontend/dist/modules/events.js +0 -91
  437. package/frontend/dist/modules/google.d.ts +0 -11
  438. package/frontend/dist/modules/google.js +0 -54
  439. package/frontend/dist/modules/meta.d.ts +0 -10
  440. package/frontend/dist/modules/mutex.d.ts +0 -7
  441. package/frontend/dist/modules/mutex.js +0 -51
  442. package/frontend/dist/modules/number.d.ts +0 -16
  443. package/frontend/dist/modules/number.js +0 -23
  444. package/frontend/dist/modules/object.d.ts +0 -52
  445. package/frontend/dist/modules/object.js +0 -383
  446. package/frontend/dist/modules/scheme.d.ts +0 -227
  447. package/frontend/dist/modules/scheme.js +0 -531
  448. package/frontend/dist/modules/settings.d.ts +0 -3
  449. package/frontend/dist/modules/settings.js +0 -4
  450. package/frontend/dist/modules/statics.d.ts +0 -5
  451. package/frontend/dist/modules/string.d.ts +0 -124
  452. package/frontend/dist/modules/string.js +0 -745
  453. package/frontend/dist/modules/support.d.ts +0 -19
  454. package/frontend/dist/modules/support.js +0 -103
  455. package/frontend/dist/modules/themes.d.ts +0 -8
  456. package/frontend/dist/modules/themes.js +0 -18
  457. package/frontend/dist/modules/user.d.ts +0 -59
  458. package/frontend/dist/modules/user.js +0 -280
  459. package/frontend/dist/modules/utils.d.ts +0 -87
  460. package/frontend/dist/modules/utils.js +0 -923
  461. package/frontend/dist/types/gradient.d.ts +0 -12
  462. package/frontend/dist/ui/border_button.d.ts +0 -152
  463. package/frontend/dist/ui/button.d.ts +0 -21
  464. package/frontend/dist/ui/canvas.d.ts +0 -56
  465. package/frontend/dist/ui/checkbox.d.ts +0 -52
  466. package/frontend/dist/ui/frame_modes.d.ts +0 -25
  467. package/frontend/dist/ui/input.d.ts +0 -241
  468. package/frontend/dist/ui/loader_button.d.ts +0 -93
  469. package/frontend/dist/ui/loaders.d.ts +0 -57
  470. package/frontend/dist/ui/pseudo.d.ts +0 -16
  471. package/frontend/dist/ui/steps.d.ts +0 -59
  472. package/frontend/dist/ui/title.d.ts +0 -21
  473. package/frontend/dist/ui/title.js +0 -121
  474. package/frontend/examples/dashboard/dashboard.ts +0 -776
  475. /package/backend/dist/cjs/{cli.d.ts → backend/src/cli.d.ts} +0 -0
  476. /package/backend/dist/cjs/{file_watcher.d.ts → backend/src/database/document.d.ts} +0 -0
  477. /package/backend/dist/cjs/{file_watcher.js → backend/src/database/document.js} +0 -0
  478. /package/backend/dist/cjs/{plugins/pdf.d.ts → backend/src/database/filters/strict_filter_test.d.ts} +0 -0
  479. /package/backend/dist/{esm/file_watcher.d.ts → cjs/backend/src/database/filters/strict_filter_test_v0.d.ts} +0 -0
  480. /package/backend/dist/{esm/plugins/pdf.d.ts → cjs/backend/src/database/flatten_test.d.ts} +0 -0
  481. /package/backend/dist/cjs/{frontend.d.ts → backend/src/frontend.d.ts} +0 -0
  482. /package/backend/dist/cjs/{plugins → backend/src/plugins}/communication.d.ts +0 -0
  483. /package/backend/dist/cjs/{plugins → backend/src/plugins}/communication.js +0 -0
  484. /package/backend/dist/cjs/{plugins → backend/src/plugins}/mail/ui.js +0 -0
  485. /package/backend/dist/cjs/{plugins → backend/src/plugins}/pdf.js +0 -0
  486. /package/backend/dist/cjs/{plugins → backend/src/plugins}/thread_monitor.d.ts +0 -0
  487. /package/backend/dist/cjs/{plugins → backend/src/plugins}/thread_monitor.js +0 -0
  488. /package/backend/dist/cjs/{vinc.d.ts → backend/src/vinc.d.ts} +0 -0
  489. /package/backend/dist/cjs/{vinc.js → backend/src/vinc.js} +0 -0
  490. /package/backend/dist/esm/{cli.d.ts → backend/src/cli.d.ts} +0 -0
  491. /package/backend/dist/esm/{frontend.d.ts → backend/src/frontend.d.ts} +0 -0
  492. /package/backend/dist/esm/{plugins → backend/src/plugins}/communication.d.ts +0 -0
  493. /package/backend/dist/esm/{plugins → backend/src/plugins}/communication.js +0 -0
  494. /package/backend/dist/esm/{plugins → backend/src/plugins}/thread_monitor.d.ts +0 -0
  495. /package/backend/dist/esm/{plugins → backend/src/plugins}/thread_monitor.js +0 -0
  496. /package/backend/dist/esm/{vinc.d.ts → backend/src/vinc.d.ts} +0 -0
  497. /package/backend/dist/esm/{vinc.js → backend/src/vinc.js} +0 -0
  498. /package/frontend/dist/{elements → frontend/src/elements}/register_element.d.ts +0 -0
  499. /package/frontend/dist/{elements → frontend/src/elements}/register_element.js +0 -0
  500. /package/frontend/dist/{modules → frontend/src/modules}/color.d.ts +0 -0
  501. /package/frontend/dist/{ui → frontend/src/ui}/ui.d.ts +0 -0
  502. /package/frontend/dist/{ui → frontend/src/ui}/ui.js +0 -0
@@ -1,19 +1,26 @@
1
- var __dirname = typeof __dirname !== 'undefined' ? __dirname : import.meta.dirname;
1
+ /**
2
+ * @author Daan van den Bergh
3
+ * @copyright © 2022 - 2025 Daan van den Bergh.
4
+ */
5
+ import { fileURLToPath } from 'url';
6
+ import * as path from 'path';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
2
9
  // ---------------------------------------------------------
3
10
  // Libraries.
4
11
  import * as http from "http";
5
12
  import * as http2 from "http2";
6
13
  import * as crypto from "crypto";
7
- import * as nodemailer from 'nodemailer';
8
- // import * as libcluster from 'cluster';
9
14
  import libcluster from 'cluster';
10
15
  import * as os from 'os';
16
+ import * as vlib from "@vandenberghinc/vlib";
17
+ const { debug } = vlib;
11
18
  // ---------------------------------------------------------
12
19
  // Imports.
13
- import * as vlib from "@vandenberghinc/vlib";
14
20
  import { Utils } from "./utils.js";
15
21
  import { Meta } from './meta.js';
16
- import * as Mail from './plugins/mail/ui.js';
22
+ import * as MailUI from './plugins/mail/ui.js';
23
+ import { Mail } from "./plugins/mail/mail.js";
17
24
  import { Status } from "./status.js";
18
25
  import { Endpoint } from "./endpoint.js";
19
26
  import { ImageEndpoint } from "./image_endpoint.js";
@@ -22,475 +29,24 @@ import { Database } from "./database/database.js";
22
29
  import { Users } from "./users.js";
23
30
  import { Paddle } from "./payments/paddle.js";
24
31
  import { RateLimits, RateLimitServer, RateLimitClient } from "./rate_limit.js";
25
- import { logger } from "./logger.js";
26
- // import { BrowserPreview } from "./plugins/browser.js";
27
- const { log, error, warn } = logger;
28
- const { debug } = vlib;
29
- // import { ThreadMonitor } from "./plugins/thread_monitor.js";
30
- // const thread_monitor = new ThreadMonitor()
31
- // thread_monitor.start();
32
32
  import { Route } from "./route.js";
33
- // ---------------------------------------------------------
34
- // The server object.
35
- // @todo redirect to https on http also important for seo.
36
- // @todo convert throw new Error to frontend errors.
37
- // @todo figure out with what settings nodejs should be started for heavy servers, for example larger memory size `--max-old-space-size`
38
- // @todo implement usage of multiple cpu's using lib `cluster`.
39
- // @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).
40
- /* @docs:
41
- @nav: Backend
42
- @chapter: Server
43
- @title: Server
44
- @description:
45
- The backend server class.
46
- When the https parameters `certificate` and `private_key` are defined, the server will run automatically on http and https.
47
- @parameter:
48
- @name: production
49
- @description: Whether the server is in production more, or in development mode.
50
- @type: boolean
51
- @required: true
52
- @parameter:
53
- @name: localhost
54
- @description: Wether to run on localhost. This overrides the given `ip` parameter, and makes the `ip` parameter optional.
55
- @type: boolean
56
- @parameter:
57
- @name: ip
58
- @description: The ip where the server will run on.
59
- @type: string
60
- @required: true
61
- @parameter:
62
- @name: port
63
- @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.
64
- @type: number
65
- @parameter:
66
- @name: tls
67
- @description: The tls settings for HTTPS.
68
- @type: object
69
- @attribute:
70
- @name: cert
71
- @description: The path to the certificate.
72
- @type: string
73
- @attribute:
74
- @name: key
75
- @description: The path to the private key file.
76
- @type: string
77
- @attribute:
78
- @name: ca
79
- @description: The path to the ca bundle file.
80
- @type: null, string
81
- @attribute:
82
- @name: passphrase
83
- @description: The passphrase of the private key.
84
- @type: string
85
- @parameter:
86
- @name: domain
87
- @description: The full domain url without `http://` or `https://`.
88
- @type: string
89
- @required: true
90
- @parameter:
91
- @name: source
92
- @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.
93
- @type: string
94
- @required: true
95
- @parameter:
96
- @name: is_primary
97
- @description: Used to indicate if the current server is the primary node.
98
- @type: string
99
- @required: true
100
- @parameter:
101
- @name: statics
102
- @description: Array with paths to static directories or static directory objects.
103
- @type: string[], vlib.Path[], StaticDirectory
104
- @required: true
105
- @attributes_type: StaticDirectory
106
- @attr:
107
- @name: path
108
- @descr: The path to the static directory or file.
109
- @required: true
110
- @attr:
111
- @name: endpoint
112
- @descr: The base endpoint of the static directory, by default the path's name will be used.
113
- @required: false
114
- @attr:
115
- @name: cache
116
- @descr: Enable caching for the static endpoints, this value will be used for parameter `Endpoint.cache`.
117
- @type: boolean | number
118
- @default: true
119
- @required: false
120
- @attr:
121
- @name: endpoints_cache
122
- @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`.
123
- @default: {}
124
- @required: false
125
- @attr:
126
- @name: exclude
127
- @descr: An array of paths to exlude. The array may contain regexes.
128
- @default: {}
129
- @required: false
130
- @parameter:
131
- @name: database
132
- @description:
133
- The database settings.
134
- @type: string, object, boolean
135
- @required: true
136
- @parameter:
137
- @name: default_headers
138
- @description: Used to override the default headers generated by volt. Leave parameter `default_headers` as `null` to let volt automatically generate the default headers.
139
- @type: object
140
- @parameter:
141
- @name: favicon
142
- @description: The path to the favicon.
143
- @type: string
144
- @parameter:
145
- @name: token_expiration
146
- @description: The token a sign in token will be valid in seconds.
147
- @type: number
148
- @parameter:
149
- @name: enable_2fa
150
- @description: Enable 2fa for user authentication.
151
- @type: boolean
152
- @required: true
153
- @parameter:
154
- @name: enable_account_activation
155
- @description: Enable account activation by email after a user signs up.
156
- @type: boolean
157
- @required: true
158
- @parameter:
159
- @name: meta
160
- @description: The default meta object.
161
- @type: object, volt.Meta
162
- @parameter:
163
- @name: company
164
- @type: object
165
- @description: Your company information.
166
- @attribute:
167
- @name: name
168
- @type: string
169
- @required: true
170
- @description: The name of your company.
171
- @attribute:
172
- @name: legal_name
173
- @type: string
174
- @required: true
175
- @description: The legal name of your company.
176
- @attribute:
177
- @name: street
178
- @type: string
179
- @required: true
180
- @description: The street name of your company's address.
181
- @attribute:
182
- @name: house_number
183
- @type: string
184
- @required: true
185
- @description: The house number or house name of your company's address.
186
- @attribute:
187
- @name: postal_code
188
- @type: string
189
- @required: true
190
- @description: The postal code or zip code of your company's address.
191
- @attribute:
192
- @name: city
193
- @type: string
194
- @required: true
195
- @description: The city of your company's address.
196
- @attribute:
197
- @name: province
198
- @type: string
199
- @required: true
200
- @description: The province or state of your company's address.
201
- @attribute:
202
- @name: country
203
- @type: string
204
- @required: true
205
- @description: The country name of your company's address.
206
- @attribute:
207
- @name: country_code
208
- @type: string
209
- @required: true
210
- @description: The two-letter ISO country code of your company's location.
211
- @attribute:
212
- @name: tax_id
213
- @type: string
214
- @required: false
215
- @description: The tax id of your company.
216
- @attribute:
217
- @name: type
218
- @type: string
219
- @description: The type of company.
220
- @attribute:
221
- @name: icon
222
- @type: string
223
- @required: true
224
- @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.
225
- @attribute:
226
- @name: stroke_icon
227
- @type: string
228
- @required: true
229
- @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.
230
- @parameter:
231
- @name: smtp
232
- @description:
233
- The smpt arguments object.
234
- More information about the arguments can be found at the nodemailer <Link https://nodemailer.com/smtp/>documentation</Link>.
235
- @type: object
236
- @attribute:
237
- @name: sender
238
- @description:
239
- The smtp sender address may either be a string with the email address, e.g. `your@email.com`.
240
- Or an array with the sender name and email address, e.g. `["Sender", "your@email.com"]`.
241
- @type: string, array
242
- @attribute:
243
- @name: host
244
- @description: The mail server's host address.
245
- @type: string
246
- @attribute:
247
- @name: port
248
- @description: The mail server's port.
249
- @type: number
250
- @attribute:
251
- @name: secure
252
- @description: Enable secure options.
253
- @type: boolean
254
- @attr:
255
- @name: auth
256
- @description: The authentication settings.
257
- @type: object
258
- @attribute:
259
- @name: user
260
- @description: The email used for authentication.
261
- @type: string
262
- @attribute:
263
- @name: pass
264
- @description: The password used for authentication.
265
- @type: string
266
- @parameter:
267
- @name: payments
268
- @type: object
269
- @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>.
270
- @attribute:
271
- @name: type
272
- @type: string
273
- @description: The payment provider name.
274
- @required: true
275
- @enum:
276
- @value: paddle
277
- @desc: Payment provider Paddle.
278
- @parameter:
279
- @name: google_tag
280
- @description: The google tag id.
281
- @type: string
282
- @parameter:
283
- @name: mail_style
284
- @description: The mail settings to customize automatically generated mails.
285
- @type: object
286
- @attribute:
287
- @name: font
288
- @description: The font family.
289
- @type: string
290
- @attribute:
291
- @name: button_bg
292
- @description: The background color of the button's in your mails.
293
- @type: string
294
- @parameter:
295
- @name: offline
296
- @description: Boolean indicating if the development server is being run offline.
297
- @type: boolean
298
- @parameter:
299
- @name: multiprocessing
300
- @description: Enable multiprocessing when in production mode.
301
- @type: boolean
302
- @def: true
303
- @parameter:
304
- @name: processes
305
- @description: The number of processes when multiprocessing is enabled. By default the number of CPU's will be used for the amount of processes.
306
- @type: null, number
307
- @def: null
308
- @parameter:
309
- @name: rate_limit
310
- @description:
311
- The rate limit server and client settings. Rate limiting works with a centralizer websocket server and secondary clients.
312
- @type: object
313
- @required: false
314
- @attribute:
315
- @name: server
316
- @type: object
317
- @description:
318
- The server configuration.
319
-
320
- By default the primary server instance will start the rate limit service.
321
-
322
- 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.
323
- @attribute:
324
- @name: ip
325
- @description:
326
- The ip to which the rate limiting server will bind. By default the rate limit server will run on localhost only.
327
- @type: null, string
328
- @attribute:
329
- @name: port
330
- @description:
331
- The port to which the rate limiting server will bind. The default port is `51234`.
332
- @type: number
333
- @def: 51234
334
- @attribute:
335
- @name: https
336
- @description:
337
- To enable https on the server you must define a `https.createServer` configuration. Otherwise, the rate limit server will run on http.
338
- @type: null, object
339
- @attribute:
340
- @name: client
341
- @description:
342
- The client configuration.
343
- @attribute:
344
- @name: ip
345
- @description:
346
- The ip address of the primary node with the rate limiting server. The primary node is indicated by the `Server.is_primary` parameter.
347
-
348
- When `Server.is_primary` is true, the rate limiting server will listen on the private ip address of your current machine.
349
- @type: null, string
350
- @attribute:
351
- @name: port
352
- @description:
353
- The port of the primary node with the rate limiting server. The default port is `51234`.
354
- @type: number
355
- @def: 51234
356
- @attribute:
357
- @name: url
358
- @description:
359
- The full websocket url of the server. If defined this takes precedence over parameters `ip` and `port`.
360
-
361
- This can be useful when `rate_limit.server` is `false`.
362
- @type: null, string
363
- @parameter:
364
- @name: keys
365
- @description:
366
- 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`.
367
-
368
- 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.
369
- @type: array[string], array[object]
370
- @required: false
371
- @attribute:
372
- @name: name
373
- @description:
374
- The name of the key.
375
- @type: string
376
- @attribute:
377
- @name: length
378
- @description:
379
- The length of the key.
380
- @type: number
381
- @parameter:
382
- @name: additional_sitemap_endpoints
383
- @description:
384
- 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.
385
- @type: array[string]
386
- @parameter:
387
- @name: daemon
388
- @description:
389
- The optional settings for the service daemons. The service daemons can be disabled by passing value `false` to parameter `daemon`.
390
-
391
- By default this settings will also partially be used for the database service daemon.
392
- @type: object
393
- @attr:
394
- @name: user
395
- @desc: The executing user of the service daemon.
396
- @type: string
397
- @attr:
398
- @name: group
399
- @desc: The executing group of the service daemon.
400
- @type: string
401
- @attr:
402
- @name: args
403
- @desc: The arguments for the start command.
404
- @type: array[string]
405
- @attr:
406
- @name: env
407
- @desc: The environment variables for the service daemon.
408
- @type: object
409
- @attr:
410
- @name: description
411
- @desc: The description of the service daemon.
412
- @type: string
413
- @attr:
414
- @name: logs
415
- @desc: The path to the log file.
416
- @type: string
417
- @attr:
418
- @name: errors
419
- @desc: The path to the error log file.
420
- @type: string
421
- @parameter:
422
- @name: admin
423
- @description:
424
- Administrator settings used for protected administrator endpoints.
425
- @type: object
426
- @attr:
427
- @name: password
428
- @desc: The password used for administrator endpoints.
429
- @type: string
430
- @attr:
431
- @name: ips
432
- @desc: IP addresses used by the website administrator. These ip's will be used to create a whitelist for administrator endpoints.
433
- @type: string[]
434
- @parameter:
435
- @name: ts
436
- @description:
437
- Specify typescript options.
438
- @type: object
439
- @attr:
440
- @name: output
441
- @desc: The output directory for typescript endpoint source files.
442
- @type: string
443
- @attr:
444
- @name: compiler_options
445
- @desc: The compiler options for the typescript files.
446
- @type: object
447
- @required: false
448
-
449
- @attribute:
450
- @name: users
451
- @type: object
452
- @attribute:
453
- @name: public
454
- @type: UIDCollection
455
- @desc:
456
- The database collection for public data of users.
457
-
458
- More information about the collection's functions can be found at <Type>UIDCollection</Type>
459
- @warning:
460
- 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.
461
- @attribute:
462
- @name: protected
463
- @type: UIDCollection
464
- @desc:
465
- The database collection for public data of users.
466
-
467
- More information about the collection's functions can be found at <Type>UIDCollection</Type>
468
- @warning:
469
- 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.
470
- @attribute:
471
- @name: private
472
- @type: UIDCollection
473
- @desc:
474
- The database collection for public data of users.
475
-
476
- More information about the collection's functions can be found at <Type>UIDCollection</Type>
477
- @note:
478
- The user has no read or write access to the private directory.
479
- @attribute:
480
- @name: storage
481
- @type: Collection
482
- @desc:
483
- The database storage collection for the website's system backend data.
484
-
485
- More information about the collection's functions can be found at <Type>Collection</Type>
486
-
33
+ import { ExternalError } from '@vandenberghinc/volt';
34
+ /**
35
+ * The backend server class.
36
+ *
37
+ * When the HTTPS parameters `certificate` and `private_key` are defined, the server will run automatically on HTTP and HTTPS.
38
+ *
39
+ * @property users The initialized {@link Users} instance.
487
40
  */
488
41
  // @tdo implement 3D secure "requires_action" status for a refund and payment intent.
489
42
  // https://stripe.com/docs/payments/3d-secure
490
43
  // @ts-ignore
491
44
  export class Server {
45
+ // ---------------------------------------------------------
492
46
  // Static attributes.
493
- static content_type_mimes = [
47
+ // ---------------------------------------------------------
48
+ /** Content type per mime. */
49
+ static content_type_mimes = new Map([
494
50
  [".html", "text/html"],
495
51
  [".htm", "text/html"],
496
52
  [".shtml", "text/html"],
@@ -500,7 +56,7 @@ export class Server {
500
56
  [".jpeg", "image/jpeg"],
501
57
  [".jpg", "image/jpeg"],
502
58
  [".js", "application/javascript"],
503
- [".ts", "application/javascript"],
59
+ [".ts", "application/typescript"],
504
60
  [".atom", "application/atom+xml"],
505
61
  [".rss", "application/rss+xml"],
506
62
  [".mml", "text/mathml"],
@@ -596,11 +152,9 @@ export class Server {
596
152
  [".asf", "video/x-ms-asf"],
597
153
  [".wmv", "video/x-ms-wmv"],
598
154
  [".avi", "video/x-msvideo"],
599
- ];
600
- log;
601
- warn;
602
- error;
603
- static compressed_extensions = [
155
+ ]);
156
+ /** All file path extensions that are already compressed. */
157
+ static compressed_extensions = new Set([
604
158
  ".png",
605
159
  ".jpg",
606
160
  ".jpeg",
@@ -609,7 +163,7 @@ export class Server {
609
163
  ".bmp",
610
164
  ".tiff",
611
165
  ".ico",
612
- ".svg",
166
+ // ".svg",
613
167
  ".svgz",
614
168
  ".mng",
615
169
  ".apng",
@@ -638,91 +192,95 @@ export class Server {
638
192
  ".mpg",
639
193
  ".mpeg",
640
194
  ".flv",
641
- ];
642
- // Instance properties
195
+ ]);
196
+ // ---------------------------------------------------------
197
+ // Attributes.
198
+ // ---------------------------------------------------------
199
+ /** The binded ip address. */
643
200
  ip;
201
+ /** The binded http port. */
644
202
  port;
203
+ /** The binded https port. */
645
204
  https_port;
205
+ /** The raw domain. */
646
206
  domain;
207
+ /** The full domain name with http/https depending if tls is enabled. */
647
208
  full_domain;
648
- source; // vlib.Path type
209
+ /** The persistent storage source directory. */
210
+ source;
211
+ /** Is the primary thread. */
649
212
  is_primary;
650
- statics;
651
- statics_aspect_ratios;
652
- favicon;
653
- enable_2fa;
654
- enable_account_activation;
655
- token_expiration;
656
- google_tag;
213
+ /** Is in production mode. */
657
214
  production;
658
- // public localhost: boolean;
659
- multiprocessing;
660
- processes;
215
+ /** The company information. */
661
216
  company;
217
+ /** The default meta information. */
662
218
  meta;
663
- mail_style;
664
- online;
219
+ /** Is running in offline mode. */
665
220
  offline;
666
- // private honey_pot_key: string | null;
667
- _keys;
668
- additional_sitemap_endpoints;
669
- log_level;
670
- tls;
671
- // public admin: AdminConfig;
672
- // public ts: TypeScriptConfig;
673
- lightweight;
674
- performance;
675
- csp;
676
- default_headers;
677
- http;
678
- https;
679
- endpoints;
680
- err_endpoints;
221
+ /** The database instance. */
681
222
  db;
682
- _sys_db; // needs to be public for the RateLimit classes.
683
- storage;
684
- smtp;
685
- smtp_sender;
223
+ /** The rate limit instance. */
686
224
  rate_limit;
687
- blacklist;
688
- _hash_key = null;
225
+ /** The added endpoints. */
226
+ endpoints = new Map();
227
+ /** The added error endpoints. */
228
+ err_endpoints = new Map();
229
+ /** A record of keys used for hashing. */
689
230
  keys = {};
690
- _on_start = [];
691
- _on_initialize = [];
692
- _on_stop = [];
693
- // public browser_preview?: BrowserPreview;
694
- daemon;
695
- _stop_tscompiler_watcher;
696
- users;
697
- payments;
231
+ /** Alias for the `Status` module. */
698
232
  status;
233
+ /** Alias for the `RateLimits` module. */
699
234
  rate_limits;
700
- logger;
235
+ /** The file logger. */
236
+ log;
237
+ /** The users instance. */
238
+ users;
239
+ /** The payments instance. */
240
+ payments;
241
+ /** Daemon instance to manage a live daemon. */
242
+ daemon;
243
+ /** The mail instance. */
244
+ mail;
245
+ // Public for internal use:
246
+ csp;
247
+ statics_aspect_ratios;
248
+ google_tag;
249
+ rate_limit_api_key;
250
+ performance;
251
+ /** The events map @internal */
252
+ events = new vlib.Events();
253
+ // Private.
254
+ favicon;
255
+ statics;
256
+ _user_keys_opts;
257
+ additional_sitemap_endpoints;
258
+ tls;
259
+ default_headers;
260
+ http;
261
+ https;
262
+ threading;
263
+ // Private ollections.
264
+ _keys_db;
265
+ _sys_keys_db;
266
+ _website_status_db;
267
+ /** Construct a new server instance. */
701
268
  constructor({ ip = "127.0.0.1", port, // leave undefined for blank detection.
702
- domain, is_primary = true, source, database, statics = [], favicon, company, meta = new Meta(), tls, smtp, mail_style = {
703
- font: '"Helvetica", sans-serif',
704
- title_fg: "#121B23",
705
- subtitle_fg: "#121B23",
706
- text_fg: "#1F2F3D",
707
- button_fg: "#FFFFFF",
708
- footer_fg: "#686B80",
709
- bg: "#EEEEEE",
710
- widget_bg: "#FFFFFF",
711
- widget_border: "#E6E6E6",
712
- button_bg: "#1F2F3D",
713
- divider_bg: "#706780",
714
- }, rate_limit = {
269
+ domain, is_primary = true, source, database, statics = [], favicon, company, meta = new Meta(), tls, mail, rate_limit = {
715
270
  server: {
716
- ip: null,
271
+ ip: undefined,
717
272
  port: RateLimitServer.default_port,
718
- https: null,
273
+ https: undefined,
719
274
  },
720
275
  client: {
721
- ip: null,
276
+ ip: undefined,
722
277
  port: RateLimitServer.default_port,
723
- url: null,
278
+ url: undefined,
724
279
  },
725
- }, keys = [], payments, default_headers, google_tag = undefined, token_expiration = 86400, enable_2fa = false, enable_account_activation = true, production = false, multiprocessing = true, processes, offline = false, additional_sitemap_endpoints = [], log_level = 0, daemon = {},
280
+ }, keys = [], payments, default_headers, google_tag = undefined, users, production = false, threading = {
281
+ enabled: false,
282
+ threads: undefined,
283
+ }, offline = false, additional_sitemap_endpoints = [], log_level = 0, daemon = false,
726
284
  // admin = {
727
285
  // password: null,
728
286
  // ips: [],
@@ -732,151 +290,125 @@ export class Server {
732
290
  // output: undefined,
733
291
  // },
734
292
  // browser_preview = undefined,
735
- lightweight = false, }) {
736
- // @debug
737
- // Async hook for tracking active processes during stop().
738
- // const async_resource_map = new Map();
739
- // this.async_hook = require('async_hooks').createHook({
740
- // init(async_id, type, trigger_async_id, resource) {
741
- // const ignoredTypes = ['TickObject', 'PROMISE'];
742
- // if (ignoredTypes.includes(type)) {
743
- // return; // Skip logging for these async types
744
- // }
745
- // // Capture the stack trace of the function that initiated the async operation
746
- // const stack = new Error("SKIPAFTER").stack.split("SKIPAFTER")[1].trim();
747
- // // Log async_id and initiating function call stack
748
- // // console.log(`Init async_id: ${async_id}`)
749
- // // console.log(`Init async_id: ${async_id}, type: ${type}, trigger: ${trigger_async_id}\nStack: ${stack}`);
750
- // // Store the async resource and stack trace
751
- // async_resource_map.set(async_id, { type, stack });
293
+ }) {
294
+ // // Verify args.
295
+ // vlib.schema.validate(arguments[0], {
296
+ // throw: true,
297
+ // error_prefix: "Server: ", unknown: false,
298
+ // schema: {
299
+ // ip: { type: "string", required: false },
300
+ // port: { type: "number", required: false },
301
+ // domain: "string",
302
+ // statics: { type: "array", default: [] },
303
+ // is_primary: { type: "boolean", default: true },
304
+ // source: "string",
305
+ // database: {
306
+ // type: ["string", "object"],
307
+ // required: true,
308
+ // scheme: { ...(Database.constructor_scheme as any), _server: undefined },
309
+ // },
310
+ // favicon: { type: "string", required: false },
311
+ // company: {
312
+ // type: "object",
313
+ // default: {},
314
+ // scheme: {
315
+ // name: "string",
316
+ // legal_name: "string",
317
+ // street: "string",
318
+ // house_number: "string",
319
+ // postal_code: "string",
320
+ // city: "string",
321
+ // province: "string",
322
+ // country: "string",
323
+ // country_code: "string",
324
+ // tax_id: { type: "string", default: null },
325
+ // icon: { type: "string", default: null },
326
+ // icon_path: { type: "string", default: null },
327
+ // stroke_icon: { type: "string", default: null },
328
+ // stroke_icon_path: { type: "string", default: null },
329
+ // }
330
+ // },
331
+ // meta: { type: "object", required: false },
332
+ // tls: {
333
+ // type: ["object"],
334
+ // required: false,
335
+ // scheme: {
336
+ // cert: "string",
337
+ // key: "string",
338
+ // ca: { type: "string", default: null },
339
+ // passphrase: { type: "string", default: null },
340
+ // }
341
+ // },
342
+ // rate_limit: {
343
+ // type: ["boolean", "object"],
344
+ // default: false,
345
+ // scheme: {
346
+ // server: {
347
+ // type: "object", default: {}, scheme: {
348
+ // ip: { type: "string", default: null },
349
+ // port: { type: "number", default: RateLimitServer.default_port },
350
+ // https: { type: "object", default: null },
351
+ // }
352
+ // },
353
+ // client: {
354
+ // type: "object", default: {}, scheme: {
355
+ // ip: { type: "string", default: null },
356
+ // port: { type: "number", default: RateLimitServer.default_port },
357
+ // url: { type: "string", default: null },
358
+ // }
359
+ // },
360
+ // },
361
+ // },
362
+ // keys: { type: "array", default: [] },
363
+ // smtp: { type: ["null", "object"], required: false },
364
+ // mail_style: {
365
+ // type: "object",
366
+ // required: false,
367
+ // scheme: {
368
+ // font: { type: "string", default: '"Helvetica", sans-serif' },
369
+ // title_fg: { type: "string", default: "#121B23" },
370
+ // subtitle_fg: { type: "string", default: "#121B23" },
371
+ // text_fg: { type: "string", default: "#1F2F3D" },
372
+ // button_fg: { type: "string", default: "#FFFFFF" },
373
+ // footer_fg: { type: "string", default: "#686B80" },
374
+ // bg: { type: "string", default: "#EEEEEE" },
375
+ // widget_bg: { type: "string", default: "#FFFFFF" },
376
+ // button_bg: { type: "string", default: "#421959" },
377
+ // widget_border: { type: "string", default: "#E6E6E6" },
378
+ // divider_bg: { type: "string", default: "#E6E6E6" },
379
+ // }
380
+ // },
381
+ // payments: { type: ["null", "object"], required: false },
382
+ // default_headers: { type: ["null", "object"], required: false },
383
+ // google_tag: { type: "string", required: false },
384
+ // token_expiration: { type: "number", required: false },
385
+ // enable_2fa: { type: "boolean", required: false },
386
+ // enable_account_activation: { type: "boolean", required: false },
387
+ // production: { type: "boolean", required: false },
388
+ // multiprocessing: { type: "boolean", required: false, default: true },
389
+ // processes: { type: "number", required: false, default: null },
390
+ // offline: { type: "boolean", default: false },
391
+ // additional_sitemap_endpoints: { type: "array", default: [] },
392
+ // log_level: { type: "number", default: 0 },
393
+ // daemon: { type: ["object", "boolean"], default: {} },
394
+ // // admin: {type: "object", default: {}, attributes: {
395
+ // // ips: {type: "array", default: []},
396
+ // // password: {
397
+ // // type: "string",
398
+ // // verify: (param: string, attrs) => (param.length < 10 ? `Parameter "Server.admin.password" must have a length of at least 10 characters.` : undefined),
399
+ // // },
400
+ // // }},
401
+ // // ts: {
402
+ // // type: "object",
403
+ // // required: false,
404
+ // // scheme: {
405
+ // // compiler_opts: {type: "object", default: {}},
406
+ // // output: "string",
407
+ // // },
408
+ // // },
409
+ // // browser_preview: {type: ["string", "undefined"], required: false, default: undefined},
752
410
  // },
753
- // destroy(async_id) {
754
- // // When an async resource is destroyed, remove it from the map
755
- // // console.log(`Destroy async_id: ${async_id}`);
756
- // async_resource_map.delete(async_id);
757
- // },
758
- // // before(async_id) {
759
- // // console.log(`Before async_id: ${async_id}`);
760
- // // },
761
- // // after(async_id) {
762
- // // console.log(`After async_id: ${async_id}`);
763
- // // },
764
- // })
765
- // this.async_hook.resource_map = async_resource_map;
766
- // this.async_hook.enable();
767
- // Verify args.
768
- vlib.Scheme.validate(arguments[0], { err_prefix: "Server: ", strict: true, scheme: {
769
- ip: { type: "string", required: false },
770
- port: { type: "number", required: false },
771
- domain: "string",
772
- statics: { type: "array", default: [] },
773
- is_primary: { type: "boolean", default: true },
774
- source: "string",
775
- database: {
776
- type: ["string", "object"],
777
- required: false,
778
- scheme: { ...Database.constructor_scheme, _server: undefined },
779
- },
780
- favicon: { type: "string", required: false },
781
- // honey_pot_key: {type: "string", default: null},
782
- company: {
783
- type: "object",
784
- default: {},
785
- scheme: {
786
- name: "string",
787
- legal_name: "string",
788
- street: "string",
789
- house_number: "string",
790
- postal_code: "string",
791
- city: "string",
792
- province: "string",
793
- country: "string",
794
- country_code: "string",
795
- tax_id: { type: "string", default: null },
796
- icon: { type: "string", default: null },
797
- icon_path: { type: "string", default: null },
798
- stroke_icon: { type: "string", default: null },
799
- stroke_icon_path: { type: "string", default: null },
800
- }
801
- },
802
- meta: { type: "object", required: false },
803
- tls: {
804
- type: ["object"],
805
- required: false,
806
- scheme: {
807
- cert: "string",
808
- key: "string",
809
- ca: { type: "string", default: null },
810
- passphrase: { type: "string", default: null },
811
- }
812
- },
813
- rate_limit: {
814
- type: ["boolean", "object"],
815
- default: false,
816
- scheme: {
817
- server: { type: "object", default: {}, scheme: {
818
- ip: { type: "string", default: null },
819
- port: { type: "number", default: RateLimitServer.default_port },
820
- https: { type: "object", default: null },
821
- } },
822
- client: { type: "object", default: {}, scheme: {
823
- ip: { type: "string", default: null },
824
- port: { type: "number", default: RateLimitServer.default_port },
825
- url: { type: "string", default: null },
826
- } },
827
- },
828
- },
829
- keys: { type: "array", default: [] },
830
- smtp: { type: ["null", "object"], required: false },
831
- mail_style: {
832
- type: "object",
833
- required: false,
834
- scheme: {
835
- font: { type: "string", default: '"Helvetica", sans-serif' },
836
- title_fg: { type: "string", default: "#121B23" },
837
- subtitle_fg: { type: "string", default: "#121B23" },
838
- text_fg: { type: "string", default: "#1F2F3D" },
839
- button_fg: { type: "string", default: "#FFFFFF" },
840
- footer_fg: { type: "string", default: "#686B80" },
841
- bg: { type: "string", default: "#EEEEEE" },
842
- widget_bg: { type: "string", default: "#FFFFFF" },
843
- button_bg: { type: "string", default: "#421959" },
844
- widget_border: { type: "string", default: "#E6E6E6" },
845
- divider_bg: { type: "string", default: "#E6E6E6" },
846
- }
847
- },
848
- payments: { type: ["null", "object"], required: false },
849
- default_headers: { type: ["null", "object"], required: false },
850
- google_tag: { type: "string", required: false },
851
- token_expiration: { type: "number", required: false },
852
- enable_2fa: { type: "boolean", required: false },
853
- enable_account_activation: { type: "boolean", required: false },
854
- production: { type: "boolean", required: false },
855
- // localhost: { type: "boolean", required: false },
856
- multiprocessing: { type: "boolean", required: false, default: true },
857
- processes: { type: "number", required: false, default: null },
858
- offline: { type: "boolean", default: false },
859
- additional_sitemap_endpoints: { type: "array", default: [] },
860
- log_level: { type: "number", default: 0 },
861
- daemon: { type: ["object", "boolean"], default: {} },
862
- // admin: {type: "object", default: {}, attributes: {
863
- // ips: {type: "array", default: []},
864
- // password: {
865
- // type: "string",
866
- // verify: (param: string, attrs) => (param.length < 10 ? `Parameter "Server.admin.password" must have a length of at least 10 characters.` : undefined),
867
- // },
868
- // }},
869
- // ts: {
870
- // type: "object",
871
- // required: false,
872
- // scheme: {
873
- // compiler_opts: {type: "object", default: {}},
874
- // output: "string",
875
- // },
876
- // },
877
- // browser_preview: {type: ["string", "undefined"], required: false, default: undefined},
878
- lightweight: { type: "boolean", required: false },
879
- } });
411
+ // });
880
412
  // Assign attributes directly.
881
413
  if (production || port == null) {
882
414
  this.port = 80;
@@ -890,35 +422,41 @@ export class Server {
890
422
  this.is_primary = is_primary && libcluster.isPrimary;
891
423
  this.source = new vlib.Path(source);
892
424
  this.favicon = favicon;
893
- this.enable_2fa = enable_2fa;
894
- this.enable_account_activation = enable_account_activation;
895
- this.token_expiration = token_expiration;
896
425
  this.google_tag = google_tag;
897
426
  this.production = production;
898
- // this.localhost = localhost;
899
- this.lightweight = lightweight;
900
- this.multiprocessing = multiprocessing;
901
- this.processes = processes == null ? os.cpus().length : processes;
902
427
  this.company = company;
903
- this.mail_style = mail_style;
904
428
  this.offline = offline;
905
- this.online = !offline;
906
- // this.honey_pot_key = honey_pot_key;
907
- this._keys = keys;
429
+ this._user_keys_opts = keys;
908
430
  this.additional_sitemap_endpoints = additional_sitemap_endpoints;
909
- this.log_level = log_level;
910
431
  this.tls = tls;
911
432
  // this.admin = admin as AdminConfig;
912
- // this.ts = ts as TypeScriptConfig;
913
- this.endpoints = new Map();
914
- this.err_endpoints = new Map();
915
- /* @performance */ this.performance = new vlib.Performance("Server performance");
916
- // Assign objects to server so it is easy to access.
433
+ // Set threading.
434
+ if (typeof threading === "boolean") {
435
+ this.threading = {
436
+ enabled: threading,
437
+ threads: os.cpus().length,
438
+ };
439
+ }
440
+ else {
441
+ this.threading = {
442
+ enabled: threading.enabled ?? true,
443
+ threads: threading.threads ?? os.cpus().length,
444
+ };
445
+ }
446
+ // Module aliases.
917
447
  this.status = Status;
918
- this.logger = logger;
919
448
  this.rate_limits = RateLimits;
920
- // Add global rate limit.
921
- this.rate_limits.add({ group: "global", interval: 60, limit: 1000 });
449
+ /* @performance */ this.performance = new vlib.Performance("Server performance");
450
+ // Create logs directory.
451
+ const log_source = this.source.join("logs");
452
+ if (!log_source.exists()) {
453
+ log_source.mkdir_sync({ recursive: true });
454
+ }
455
+ this.log = new vlib.logging.FileLogger({
456
+ level: log_level,
457
+ log_path: log_source.join("logs").str(),
458
+ error_path: log_source.join("errors").str(),
459
+ });
922
460
  // Check source.
923
461
  if (!this.source.exists()) {
924
462
  throw Error(`Source directory "${this.source.str()}" does not exist.`);
@@ -930,9 +468,9 @@ export class Server {
930
468
  this.domain = this.domain.substr(0, this.domain.length - 1);
931
469
  }
932
470
  // Set full domain.
933
- this.full_domain = `http${tls == null || tls.key == null ? "" : "s"}://${domain}`; // also required for Stripe.
934
- while (this.full_domain.charAt(this.full_domain.length - 1) === "/") {
935
- this.full_domain = this.full_domain.substr(0, this.full_domain.length - 1);
471
+ this.full_domain = `http${this.tls ? "s" : ""}://${this.domain}`;
472
+ while (this.full_domain.endsWith("/")) {
473
+ this.full_domain = this.full_domain.slice(0, -1);
936
474
  }
937
475
  // Set statics.
938
476
  this.statics = statics;
@@ -958,23 +496,43 @@ export class Server {
958
496
  this.meta = meta;
959
497
  // Default headers.
960
498
  const base_default_headers = {
961
- "Vary": "Origin",
962
- "Referrer-Policy": "same-origin",
499
+ // Cache correctness for CORS/preflight:
500
+ "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
501
+ // Safer default than same-origin, still keeps useful referrers:
502
+ "Referrer-Policy": "strict-origin-when-cross-origin",
963
503
  "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
964
- "Access-Control-Allow-Origin": "*",
965
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
966
- 'Access-Control-Allow-Credentials': 'true',
967
- "X-XSS-Protection": "1; mode=block",
504
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
505
+ // Let browsers read our rate-limit hint:
506
+ "Access-Control-Expose-Headers": "X-RateLimit-Reset",
968
507
  "X-Content-Type-Options": "nosniff",
969
- "frame-ancestors": 'none',
970
508
  "X-Frame-Options": "DENY",
971
- "Strict-Transport-Security": "max-age=31536000",
509
+ // Helpful isolation defaults (safe for most apps):
510
+ "Cross-Origin-Opener-Policy": "same-origin",
511
+ "Cross-Origin-Resource-Policy": "same-site",
512
+ // If you need SharedArrayBuffer, add COEP below (can break some embeds):
513
+ // "Cross-Origin-Embedder-Policy": "require-corp",
514
+ "Strict-Transport-Security": "max-age=63072000; includeSubDomains; preload",
515
+ // Lock down powerful APIs by default.
516
+ // If you need one on a third-party origin, add it beside (self).
517
+ "Permissions-Policy": "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), usb=(), hid=(), serial=(), xr-spatial-tracking=(), display-capture=(), screen-wake-lock=(), sync-xhr=(), publickey-credentials-get=(self), encrypted-media=(self), autoplay=(self 'https://www.youtube-nocookie.com') fullscreen=(self 'https://www.youtube-nocookie.com'), browsing-topics=()",
518
+ // Do NOT set Allow-Origin / Credentials statically; set them per-request below.
519
+ // "X-XSS-Protection": "1; mode=block", // deprecated
972
520
  };
973
521
  const default_csp = {
974
- "default-src": "'self' https://*.google-analytics.com",
975
- "img-src": `'self' http://${this.domain} https://${this.domain} https://*.google-analytics.com https://raw.githubusercontent.com/vandenberghinc/ `,
976
- "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/",
977
- "style-src": "'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/@vandenberghinc/",
522
+ "default-src": "'self'",
523
+ "base-uri": "'none'",
524
+ "object-src": "'none'",
525
+ "form-action": "'self'",
526
+ "frame-ancestors": "'none'",
527
+ // Keep GA images; drop explicit http:// to avoid mixed content.
528
+ "img-src": "'self' data: blob: https://*.google-analytics.com",
529
+ "script-src": "'self' https://ajax.googleapis.com https://www.googletagmanager.com https://*.google-analytics.com",
530
+ // Needed for GA/GTAG beacons/fetch:
531
+ "connect-src": "'self' https://*.google-analytics.com",
532
+ "style-src": "'self'",
533
+ "font-src": "'self' data:",
534
+ // Auto-upgrade stray http URLs where possible:
535
+ "upgrade-insecure-requests": "",
978
536
  };
979
537
  if (default_headers == null) {
980
538
  this.csp = default_csp;
@@ -992,6 +550,10 @@ export class Server {
992
550
  });
993
551
  this.default_headers = default_headers;
994
552
  }
553
+ if (!this.tls) {
554
+ // Always drop HSTS if TLS is not active.
555
+ delete this.default_headers["Strict-Transport-Security"];
556
+ }
995
557
  // Initialize payments.
996
558
  if (payments) {
997
559
  if (payments.type === "paddle") {
@@ -1004,60 +566,56 @@ export class Server {
1004
566
  throw Error(`Invalid payment processor type "${payments.type}", valid types are ["paddle"].`);
1005
567
  }
1006
568
  }
1007
- // Define your list of endpoints
1008
- this.endpoints = new Map();
1009
- this.err_endpoints = new Map();
1010
- // Browser preview.
1011
- // if (browser_preview) {
1012
- // this.browser_preview = new BrowserPreview(browser_preview);
1013
- // }
1014
- // Create logs directory.
1015
- const log_source = this.source.join("logs");
1016
- if (!log_source.exists()) {
1017
- log_source.mkdir_sync();
1018
- }
1019
- // Configure the logger.
1020
- logger.log_level.set(this.log_level);
1021
- if (daemon === false) {
1022
- logger.assign_paths(log_source.join("logs").str(), log_source.join("errors").str());
1023
- }
1024
- this.log = logger.log.bind(logger);
1025
- this.warn = logger.warn.bind(logger);
1026
- this.error = logger.error.bind(logger);
1027
569
  // Initialize the service daemon.
1028
570
  // Must be initialized before initializing the database.
1029
571
  if (daemon !== false) {
1030
572
  const log_source = this.source.join("daemon");
1031
573
  if (!log_source.exists()) {
1032
- log_source.mkdir_sync();
574
+ log_source.mkdir_sync({ recursive: true });
1033
575
  }
1034
576
  this.daemon = new vlib.Daemon({
1035
577
  name: this.domain.replaceAll(".", ""),
1036
- user: daemon.user || os.userInfo().username,
1037
- group: daemon.group || null,
1038
- command: "volt --service --start",
1039
- cwd: this.source.str(),
1040
- args: daemon.args || [],
1041
- env: daemon.env || {},
1042
- description: daemon.description || `Service daemon for website ${this.domain}.`,
1043
- auto_restart: true,
1044
578
  logs: daemon.logs || log_source.join("logs").str(),
1045
579
  errors: daemon.errors || log_source.join("errors").str(),
580
+ ...daemon,
581
+ // user: (daemon as Record<string, any>).user || os.userInfo().username,
582
+ // group: (daemon as Record<string, any>).group || null,
583
+ // command: "volt --service --start",
584
+ // cwd: this.source.str(),
585
+ // args: (daemon as Record<string, any>).args || [],
586
+ // env: (daemon as Record<string, any>).env || {},
587
+ // description: (daemon as Record<string, any>).description || `Service daemon for website ${this.domain}.`,
588
+ // auto_restart: true,
1046
589
  });
1047
590
  }
1048
591
  // Initialize the database class.
1049
592
  if (typeof database === "string") {
1050
593
  this.db = new Database({ uri: database, _server: this });
1051
594
  }
1052
- else if (database != null) {
595
+ else {
1053
596
  this.db = new Database({ ...database, _server: this });
1054
597
  }
598
+ // Database collections.
599
+ this._keys_db = this.db.collection({
600
+ name: "Volt.Keys",
601
+ indexes: ["id"],
602
+ });
603
+ this._sys_keys_db = this.db.collection({
604
+ name: "Volt.SystemKeys",
605
+ indexes: ["id"],
606
+ });
607
+ this._website_status_db = this.db.collection({
608
+ name: "Volt.WebsiteStatus",
609
+ indexes: ["id"],
610
+ });
1055
611
  // Initialize the users class.
1056
- this.users = new Users(this);
1057
- // The smtp instance.
1058
- if (smtp) {
1059
- this.smtp_sender = smtp.sender;
1060
- this.smtp = nodemailer.createTransport(smtp);
612
+ this.users = new Users({
613
+ ...users,
614
+ _server: this,
615
+ });
616
+ // The mail instance.
617
+ if (mail) {
618
+ this.mail = new Mail(mail);
1061
619
  }
1062
620
  // The rate limit server/client.
1063
621
  if (rate_limit) {
@@ -1071,51 +629,39 @@ export class Server {
1071
629
  this.rate_limit = new RateLimitClient({ ...(rate_limit.client ?? {}), _server: this });
1072
630
  }
1073
631
  }
1074
- // Blacklist class.
1075
- // if (this.honey_pot_key) {
1076
- // this.blacklist = new Blacklist({api_key: this.honey_pot_key});
1077
- // }
1078
632
  }
1079
633
  // ---------------------------------------------------------
1080
634
  // Utils.
1081
- // Get a content type from an extension.
635
+ /** Get a content type (MIME) from a file extension. */
1082
636
  get_content_type(extension) {
1083
- let content_type = Server.content_type_mimes.find((item) => {
1084
- if (item[0] == extension) {
1085
- return item[1];
1086
- }
1087
- })?.[1];
1088
- if (content_type == null) {
1089
- content_type = "application/octet-stream";
1090
- }
1091
- return content_type;
637
+ return Server.content_type_mimes.get(extension.toLowerCase()) ?? "application/octet-stream";
1092
638
  }
1093
- // Set log level.
639
+ /** Set the logging verbosity level. */
1094
640
  set_log_level(level) {
1095
- this.log_level = level;
1096
- logger.log_level.set(level);
641
+ this.log.level.set(level);
1097
642
  }
1098
643
  // ---------------------------------------------------------
1099
644
  // Crypto (private).
1100
- // Generate a crypto key.
645
+ /** Generate a cryptographically secure random key as a hex string. */
1101
646
  generate_crypto_key(length = 32) {
1102
647
  return crypto.randomBytes(length).toString('hex');
1103
648
  }
1104
- // Create a sha hmac with the master key.
649
+ /** Create an HMAC hash using the provided key and data. */
1105
650
  hmac(key, data, algo = "sha256") {
1106
651
  const hmac = crypto.createHmac(algo, key);
1107
652
  hmac.update(data);
1108
653
  return hmac.digest("hex");
1109
654
  }
1110
- _hmac(data) {
1111
- if (!this._hash_key) {
1112
- throw new Error("Hash key not initialized");
1113
- }
1114
- const hmac = crypto.createHmac("sha256", this._hash_key);
1115
- hmac.update(data);
1116
- return hmac.digest("hex");
1117
- }
1118
- // Hash without a key.
655
+ // /** Create an HMAC hash using the server's master hash key. */
656
+ // hmac_with_master(data: string): string {
657
+ // if (!this._master_hash_key) {
658
+ // throw new Error("Hash key not initialized");
659
+ // }
660
+ // const hmac = crypto.createHmac("sha256", this._master_hash_key);
661
+ // hmac.update(data);
662
+ // return hmac.digest("hex");
663
+ // }
664
+ /** Create a hash (no key) of the given data using the specified algorithm. */
1119
665
  hash(data, algo = "sha256") {
1120
666
  if (typeof data !== "string") {
1121
667
  data = JSON.stringify(data);
@@ -1140,6 +686,26 @@ export class Server {
1140
686
  // Add header defaults.
1141
687
  _set_header_defaults(stream) {
1142
688
  stream.set_headers(this.default_headers);
689
+ const origin = stream.headers.origin;
690
+ if (origin) {
691
+ const same_http = `http://${this.domain}`;
692
+ const same_https = `https://${this.domain}`;
693
+ if (origin === same_http || origin === same_https) {
694
+ stream.set_header("Access-Control-Allow-Origin", origin);
695
+ stream.set_header("Access-Control-Allow-Credentials", "true");
696
+ }
697
+ else {
698
+ stream.set_header("Access-Control-Allow-Origin", "*");
699
+ // Do not send Access-Control-Allow-Credentials with a wildcard origin.
700
+ }
701
+ // Improve preflight reflection for caches and correctness.
702
+ const req_hdrs = stream.headers["access-control-request-headers"];
703
+ if (req_hdrs)
704
+ stream.set_header("Access-Control-Allow-Headers", String(req_hdrs));
705
+ const req_method = stream.headers["access-control-request-method"];
706
+ if (req_method)
707
+ stream.set_header("Access-Control-Allow-Methods", String(req_method));
708
+ }
1143
709
  }
1144
710
  _find_endpoint(endpoint, method) {
1145
711
  let route;
@@ -1175,13 +741,13 @@ export class Server {
1175
741
  data: favicon.load_sync({ type: "buffer" }),
1176
742
  content_type: this.get_content_type(favicon.extension()),
1177
743
  _is_static: true,
1178
- _server: this,
744
+ server: this,
1179
745
  });
1180
746
  }
1181
747
  // Create status endpoint.
1182
748
  const status_dir = this.source.join(".status");
1183
749
  if (!status_dir.exists()) {
1184
- status_dir.mkdir_sync();
750
+ status_dir.mkdir_sync({ recursive: true });
1185
751
  }
1186
752
  const status_key_path = status_dir.join("key");
1187
753
  let status_key;
@@ -1218,9 +784,10 @@ export class Server {
1218
784
  status.https_port = this.https_port;
1219
785
  }
1220
786
  // Load data.
1221
- const data = await this._sys_db.load("status", {
787
+ const data = await this._website_status_db.load({ id: "status" }, {
1222
788
  default: {
1223
- running_since: null,
789
+ id: "status",
790
+ running_since: undefined,
1224
791
  running_threads: 0,
1225
792
  total_threads: 0,
1226
793
  }
@@ -1261,16 +828,18 @@ export class Server {
1261
828
  // Create the sitemap endpoint.
1262
829
  async _create_sitemap() {
1263
830
  // Logs.
1264
- if (this.lightweight) {
1265
- return;
1266
- }
1267
- log(2, "Creating sitemap.");
831
+ this.log(2, "Creating sitemap.");
1268
832
  let sitemap = "";
1269
833
  sitemap += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1270
834
  sitemap += "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n";
1271
835
  for (const endpoint of this.endpoints.values()) {
1272
836
  if (endpoint.allow_sitemap) {
1273
- sitemap += `<url>\n <loc>${this.full_domain}/${endpoint.route.endpoint_str}</loc>\n</url>\n`; // @todo not compatiable with regex endpoints
837
+ if (endpoint.route.is_regex)
838
+ continue; // skip regex routes
839
+ const ep = encodeURI(endpoint.route.endpoint_str.startsWith("/")
840
+ ? endpoint.route.endpoint_str
841
+ : `/${endpoint.route.endpoint_str}`);
842
+ sitemap += `<url>\n <loc>${this.full_domain}${ep}</loc>\n</url>\n`;
1274
843
  }
1275
844
  }
1276
845
  this.additional_sitemap_endpoints.forEach((endpoint) => {
@@ -1291,10 +860,7 @@ export class Server {
1291
860
  // Create the robots.txt endpoint.
1292
861
  async _create_robots_txt() {
1293
862
  // Logs.
1294
- if (this.lightweight) {
1295
- return;
1296
- }
1297
- log(2, "Creating robots.txt.");
863
+ this.log(2, "Creating robots.txt.");
1298
864
  // Proceed.
1299
865
  let robots = "User-agent: *\n";
1300
866
  let disallowed = 0;
@@ -1321,8 +887,7 @@ export class Server {
1321
887
  /* private _create_admin_endpoint(): void {
1322
888
 
1323
889
  // Logs.
1324
- if (this.lightweight) { return; }
1325
- log(2, "Creating admin endpoint.");
890
+ this.log(2, "Creating admin endpoint.");
1326
891
 
1327
892
  // Add admin tokens.
1328
893
  this.admin.tokens = [];
@@ -1358,7 +923,7 @@ export class Server {
1358
923
  password: "string",
1359
924
  },
1360
925
  ip_whitelist: this.admin.ips,
1361
- callback: async (stream: any, params: {password: string}) => {
926
+ callback: async (stream: Stream, params: {password: string}) => {
1362
927
  // Check key.
1363
928
  if (params.password !== this.admin.password) {
1364
929
  return stream.send({
@@ -1394,7 +959,7 @@ export class Server {
1394
959
  token: "string",
1395
960
  },
1396
961
  ip_whitelist: this.admin.ips,
1397
- callback: async (stream: any, params: {token: string}) => {
962
+ callback: async (stream: Stream, params: {token: string}) => {
1398
963
  // Verify token.
1399
964
  if (!verify_token(params.token)) {
1400
965
  return stream.send({
@@ -1473,7 +1038,7 @@ export class Server {
1473
1038
  // Initialize statics.
1474
1039
  async _initialize_statics() {
1475
1040
  // Logs.
1476
- log(2, "Initializing static directories.");
1041
+ this.log(2, "Initializing static directories.");
1477
1042
  // Static paths for the file watcher.
1478
1043
  const static_paths = [];
1479
1044
  // Add static file.
@@ -1507,13 +1072,12 @@ export class Server {
1507
1072
  method: "GET",
1508
1073
  endpoint,
1509
1074
  content_type,
1510
- compress: !Server.compressed_extensions.includes(path.extension()),
1075
+ compress: !Server.compressed_extensions.has(path.extension().toLowerCase()),
1511
1076
  cache,
1512
1077
  rate_limit: "global",
1513
- _static_path: path.str(),
1078
+ file_path: path,
1514
1079
  _is_static: true,
1515
- })
1516
- ._load_data_by_path(this));
1080
+ }));
1517
1081
  }
1518
1082
  };
1519
1083
  // Initialize statics.
@@ -1523,9 +1087,10 @@ export class Server {
1523
1087
  }
1524
1088
  if (typeof opts === "object") {
1525
1089
  // Check object.
1526
- vlib.Scheme.validate(opts, {
1527
- strict: true,
1528
- scheme: {
1090
+ vlib.schema.validate(opts, {
1091
+ unknown: false,
1092
+ throw: true,
1093
+ schema: {
1529
1094
  path: "string",
1530
1095
  endpoint: { type: "string", default: null },
1531
1096
  cache: { type: ["boolean", "number"], default: true },
@@ -1534,31 +1099,19 @@ export class Server {
1534
1099
  }
1535
1100
  });
1536
1101
  // Vars.
1537
- const exclude = [/.*\.DS_Store/, /.*\.cache/, /.*\.old/, /.*\.ignore/, ...opts.exclude || []];
1538
- const paths = []; // vlib.Path[]
1102
+ const paths = [];
1539
1103
  const source = new vlib.Path(opts.path).abs();
1104
+ if (!source.exists()) {
1105
+ this.log(1, `Static path "${source.str()}" does not exist; skipping.`);
1106
+ return;
1107
+ }
1540
1108
  const source_len = source.str().length;
1541
1109
  const is_dir = source.is_dir();
1542
1110
  // Is excluded.
1543
- const is_excluded = (path) => {
1544
- return exclude.some(pattern => {
1545
- if (path instanceof RegExp) {
1546
- if (pattern instanceof RegExp) {
1547
- return pattern.source === path.source;
1548
- }
1549
- else {
1550
- return path.test(String(pattern));
1551
- }
1552
- }
1553
- else {
1554
- if (pattern instanceof RegExp) {
1555
- return pattern.test(String(path));
1556
- }
1557
- else {
1558
- return path === pattern;
1559
- }
1560
- }
1561
- });
1111
+ const exclude = [/\.DS_Store$/, /\.cache(?:\/|$)/, /\.old(?:\/|$)/, /\.ignore$/, ...(opts.exclude || [])];
1112
+ const is_excluded = (p) => {
1113
+ const s = typeof p === "string" ? p : p.str();
1114
+ return exclude.some(pattern => pattern instanceof RegExp ? pattern.test(s) : s === String(pattern));
1562
1115
  };
1563
1116
  // Initialize endpoint.
1564
1117
  opts.endpoint = opts.endpoint || `/${source.full_name()}`;
@@ -1614,199 +1167,86 @@ export class Server {
1614
1167
  // Response.
1615
1168
  return static_paths;
1616
1169
  }
1617
- // ---------------------------------------------------------
1618
- // Server (private).
1619
- // Initialize.
1620
- // Initialize.
1621
- async initialize() {
1622
- // Logs.
1623
- log(1, "Initializing server.");
1624
- /* @performance */ this.performance.start();
1625
- // Create HTTPS server.
1626
- if (this.tls) {
1627
- this.https = http2.createSecureServer({
1628
- key: new vlib.Path(this.tls.key).load_sync({ encoding: 'utf8' }),
1629
- cert: new vlib.Path(this.tls.cert).load_sync({ encoding: 'utf8' }),
1630
- ca: this.tls.ca == null ? undefined : new vlib.Path(this.tls.ca).load_sync({ encoding: 'utf8' }),
1631
- passphrase: this.tls.passphrase,
1632
- allowHTTP1: true,
1633
- },
1634
- // Support for http1.
1635
- // Does not work, requests get triggered on the stream and on this callback.
1636
- (req, res) => {
1637
- if (req.httpVersion.charAt(0) !== "2") {
1638
- this._serve(undefined, undefined, req, res);
1639
- }
1640
- });
1641
- this.https.on('stream', (stream, headers) => {
1642
- this._serve(stream, headers, undefined, undefined);
1643
- });
1644
- }
1645
- // Payments require HTTPS in production.
1646
- else if (this.production && this.payments) {
1647
- throw Error("Accepting payments in production mode requires HTTPS.");
1648
- }
1649
- // Redirect HTTP requests to HTTPS.
1650
- if (this.tls) {
1651
- this.http = http.createServer((request, response) => {
1652
- response.writeHead(301, { Location: `https://${request.headers.host}${request.url}` });
1653
- response.end();
1654
- });
1170
+ /** Initialize the system and user defined keys. */
1171
+ async _initialize_keys() {
1172
+ // Await database initialization.
1173
+ const start = Date.now();
1174
+ await this._db_init_promise;
1175
+ /* @performance */ this.performance.end("_initialize_keys():await-db-init", start);
1176
+ // Load system keys.
1177
+ const sys_keys = await this._sys_keys_db.load({ id: "sys_keys" }, {
1178
+ default: {
1179
+ id: "sys_keys",
1180
+ rate_limit_api_key: undefined,
1181
+ }
1182
+ });
1183
+ let perform_sys_keys_save = false;
1184
+ // Check rate limit api key.
1185
+ if (sys_keys.rate_limit_api_key == null) {
1186
+ this.rate_limit_api_key = this.generate_crypto_key(32);
1187
+ sys_keys.rate_limit_api_key = this.rate_limit_api_key;
1188
+ perform_sys_keys_save = true;
1655
1189
  }
1656
1190
  else {
1657
- this.http = http.createServer((req, res) => {
1658
- this._serve(undefined, undefined, req, res);
1659
- });
1191
+ this.rate_limit_api_key = sys_keys.rate_limit_api_key;
1660
1192
  }
1661
- /* @performance */ this.performance.end("create-http-server");
1662
- // Start the database.
1663
- if (this.db) {
1664
- await this.db.initialize();
1665
- /* @performance */ this.performance.end("init-db");
1666
- // Database collections.
1667
- this._sys_db = await this.db.collection({
1668
- name: "Volt.System",
1669
- indexes: ["_path"],
1670
- });
1671
- /* @performance */ this.performance.end("init-sys-collection");
1672
- // Load keys.
1673
- const keys_document = await this._sys_db.load("keys");
1674
- const gen_user_crypto_key = (doc, key) => {
1193
+ // Save.
1194
+ if (perform_sys_keys_save) {
1195
+ await this._sys_keys_db.set({ id: "sys_keys" }, sys_keys);
1196
+ }
1197
+ // Check user defined crypto keys.
1198
+ const user_keys = await this._keys_db.load({ id: "user_keys" }, {
1199
+ default: {
1200
+ id: "user_keys",
1201
+ keys: {},
1202
+ }
1203
+ });
1204
+ let perform_user_keys_save = false;
1205
+ for (const key of this._user_keys_opts) {
1206
+ const name = typeof key === "string" ? key : key.name;
1207
+ if (user_keys[name]) {
1208
+ this.keys[name] = user_keys[name];
1209
+ }
1210
+ else {
1211
+ perform_user_keys_save = true;
1675
1212
  if (typeof key === "string") {
1676
- doc[key] = this.generate_crypto_key(32);
1213
+ if (!key) {
1214
+ throw Error(`Crypto key "${key}" is an invalid key name.`);
1215
+ }
1216
+ const generated_key = this.generate_crypto_key(32);
1217
+ user_keys.keys[key] = generated_key;
1218
+ this.keys[key] = generated_key;
1677
1219
  }
1678
1220
  else {
1221
+ if (!key.name) {
1222
+ throw Error(`Crypto key "${key.name}" is an invalid key name.`);
1223
+ }
1679
1224
  if (key.length == null) {
1680
- throw Error(`Crypto key object "${JSON.stringify(key)}" does not contain a "length" attribute.`);
1225
+ throw Error(`Crypto key "${key.name}" does not contain a "length" attribute.`);
1681
1226
  }
1682
1227
  if (typeof key.length !== "number") {
1683
- throw Error(`Crypto key object "${JSON.stringify(key)}" has an invalid type fo attribute "length", the valid type is "number".`);
1684
- }
1685
- if (key.name == null) {
1686
- throw Error(`Crypto key object "${JSON.stringify(key)}" does not contain a "name" attribute.`);
1687
- }
1688
- if (typeof key.name !== "string") {
1689
- throw Error(`Crypto key object "${JSON.stringify(key)}" has an invalid type fo attribute "name", the valid type is "string".`);
1690
- }
1691
- doc[key.name] = this.generate_crypto_key(key.length);
1692
- this.keys[key.name] = doc[key.name];
1693
- }
1694
- };
1695
- if (keys_document == null) {
1696
- this._hash_key = this.generate_crypto_key(32);
1697
- const doc = {
1698
- _master_sha256: this._hash_key,
1699
- };
1700
- this._keys.forEach((key) => {
1701
- gen_user_crypto_key(doc, key);
1702
- });
1703
- await this._sys_db.save("keys", doc);
1704
- }
1705
- else {
1706
- // Check hash key.
1707
- this._hash_key = keys_document._master_sha256;
1708
- let perform_save = false;
1709
- if (this._hash_key === undefined) {
1710
- this._hash_key = this.generate_crypto_key(32);
1711
- keys_document._master_sha256 = this._hash_key;
1712
- perform_save = true;
1713
- }
1714
- // Check crypto keys.
1715
- this._keys.forEach((key) => {
1716
- let name = typeof key === "string" ? key : key.name;
1717
- if (keys_document[name] == null) {
1718
- gen_user_crypto_key(keys_document, key);
1719
- perform_save = true;
1228
+ throw Error(`Crypto key "${key.name}" has an invalid type for attribute "length", the valid type is "number".`);
1720
1229
  }
1721
- this.keys[name] = keys_document[name];
1722
- });
1723
- // Save.
1724
- if (perform_save) {
1725
- await this._sys_db.save("keys", keys_document);
1726
- }
1727
- }
1728
- /* @performance */ this.performance.end("load-keys");
1729
- }
1730
- // Initialize default headers.
1731
- this._init_default_headers();
1732
- /* @performance */ this.performance.end("init-default-headers");
1733
- // Create default endpoints.
1734
- this._create_default_endpoints();
1735
- /* @performance */ this.performance.end("create-default-endpoints");
1736
- // Create admin endpoints.
1737
- // this._create_admin_endpoint();
1738
- // /* @performance */ this.performance.end("create-admin-endpoints");
1739
- // Create static endpoints.
1740
- const promises = [];
1741
- promises.push(this._initialize_statics());
1742
- // /* @performance */ this.performance.end("create-static-endpoints");
1743
- // Initialize users.
1744
- if (this.db) {
1745
- promises.push(this.users._initialize());
1746
- // /* @performance */ this.performance.end("init-users");
1747
- }
1748
- // Database preview endpoints (only when production mode is disabled).
1749
- // if (this.db) {
1750
- // this.db._initialize_db_preview();
1751
- // /* @performance */ this.performance.end("init-db-preview");
1752
- // }
1753
- // Payments.
1754
- if (this.payments !== undefined) {
1755
- promises.push(this.payments._initialize());
1756
- }
1757
- // /* @performance */ this.performance.end("init-payments");
1758
- // Create sitemap when it does not exist.
1759
- // Must be done at the end of initialization func since some funcs might still create endpoints.
1760
- if (this._find_endpoint("sitemap.xml") == null) {
1761
- promises.push(this._create_sitemap());
1762
- }
1763
- // /* @performance */ this.performance.end("create-sitemap");
1764
- // Create robots.txt when it does not exist.
1765
- // Must be done at the end of initialization func since some funcs might still create endpoints.
1766
- if (this._find_endpoint("robots.txt") == null) {
1767
- promises.push(this._create_robots_txt());
1768
- }
1769
- // /* @performance */ this.performance.end("create-robots.txt");
1770
- // Await all promises.
1771
- await Promise.all(promises);
1772
- // Get the icon and stroke icon file paths when defined.
1773
- if (this.company.stroke_icon || this.company.icon) {
1774
- for (const endpoint of this.endpoints.values()) {
1775
- if (this.company.stroke_icon_path == null && endpoint.route.endpoint === this.company.stroke_icon) {
1776
- this.company.stroke_icon_path = endpoint._static_path ?? undefined;
1230
+ const generated_key = this.generate_crypto_key(key.length);
1231
+ user_keys.keys[key.name] = generated_key;
1232
+ this.keys[key.name] = generated_key;
1777
1233
  }
1778
- if (this.company.icon_path == null && endpoint.route.endpoint === this.company.icon) {
1779
- this.company.icon_path = endpoint._static_path ?? undefined;
1780
- }
1781
- }
1782
- if (this.company.stroke_icon != null && this.company.stroke_icon_path == null) {
1783
- throw Error(`Unable to find the company's stroke icon endpoint "${this.company.stroke_icon}".`);
1784
- }
1785
- if (this.company.icon != null && this.company.icon_path == null) {
1786
- throw Error(`Unable to find the company's icon endpoint "${this.company.icon}".`);
1787
1234
  }
1788
1235
  }
1789
- // Initialize all endpoints.
1790
- for (const endpoint of this.endpoints.values()) {
1791
- endpoint._initialize(this);
1792
- }
1793
- for (const endpoint of this.err_endpoints.values()) {
1794
- endpoint._initialize(this);
1795
- }
1796
- // On initialize callbacks.
1797
- for (const callback of this._on_initialize) {
1798
- const res = callback();
1799
- if (res instanceof Promise) {
1800
- await res;
1801
- }
1236
+ if (perform_user_keys_save) {
1237
+ await this._keys_db.set({ id: "user_keys" }, user_keys);
1802
1238
  }
1803
1239
  }
1804
1240
  /**
1805
- * Add callback to be called when the server is initialized.
1806
- * @param callback The callback to be called when the server is initialized.
1241
+ * Checks if an endpoint route already exists.
1242
+ * @param method HTTP method
1243
+ * @param endpoint String path or RegExp
1807
1244
  */
1808
- on_initialize(callback) {
1809
- this._on_initialize.append(callback);
1245
+ _check_duplicate_route(route) {
1246
+ const e = this._find_endpoint(route);
1247
+ if (e) {
1248
+ throw new Error(`Duplicate "${route.method}:${route.endpoint_str}" endpoint route, it is already defined by endpoint "${e.id}".`);
1249
+ }
1810
1250
  }
1811
1251
  // Serve a client.
1812
1252
  // @todo implement rate limiting.
@@ -1820,12 +1260,12 @@ export class Server {
1820
1260
  let method;
1821
1261
  let endpoint_url;
1822
1262
  // Log endpoint result.
1823
- const log_endpoint_result = (message = null, status = null) => {
1263
+ const log_endpoint_result = (message, status) => {
1824
1264
  let log_level = endpoint && endpoint.is_static ? 3 : 0;
1825
1265
  if (status == null) {
1826
1266
  status = stream.status_code;
1827
1267
  }
1828
- log(log_level, `${method}:${endpoint_url}: ${message ? message : Status.get_description(status)} [${status}] (${stream.ip}).`);
1268
+ this.log(log_level, `${method}:${endpoint_url}: ${message ? message : Status.get_description(status ?? "unknown")} [${status}] (${stream.ip}).`);
1829
1269
  };
1830
1270
  // Serve error endpoint.
1831
1271
  const serve_error_endpoint = async (status_code) => {
@@ -1874,7 +1314,7 @@ export class Server {
1874
1314
  await err_endpoint._serve(stream, status_code);
1875
1315
  }
1876
1316
  catch (err) {
1877
- error(`Error endpoint ${status_code}: `, err);
1317
+ this.log.error(`Error endpoint ${status_code}: `, err);
1878
1318
  stream.send(default_response);
1879
1319
  }
1880
1320
  }
@@ -1882,30 +1322,31 @@ export class Server {
1882
1322
  }
1883
1323
  };
1884
1324
  // Check ip against blacklist.
1885
- if (this.online && this.blacklist !== undefined && !this.blacklist.verify(stream.ip)) {
1886
- await serve_error_endpoint(403);
1887
- log_endpoint_result();
1888
- return;
1889
- }
1325
+ // if (!this.offline && this.blacklist !== undefined && !this.blacklist.verify(stream.ip)) {
1326
+ // await serve_error_endpoint(403);
1327
+ // this.log_endpoint_result();
1328
+ // return;
1329
+ // }
1890
1330
  // Check if the request matches any of the defined endpoints.
1891
1331
  method = stream.method;
1892
1332
  endpoint_url = stream.endpoint;
1893
1333
  // endpoint = this._find_endpoint(endpoint_url, method);
1894
1334
  // Find endpoint manually so the optional path params can be extracted.
1895
- log(3, "Searching for endpoint: ", `${method}:${endpoint_url}`);
1335
+ this.log(3, "Searching for endpoint: ", `${method}:${endpoint_url}`);
1896
1336
  endpoint = this.endpoints.get(`${method}:${endpoint_url}`);
1897
1337
  if (!endpoint) {
1898
1338
  // Check regex endpoints.
1899
1339
  const route = new Route(method, endpoint_url);
1900
1340
  for (const e of this.endpoints.values()) {
1901
1341
  if (e.route.is_regex) {
1902
- if (e.route.match(route)) {
1903
- log(3, "Matched regex route: ", e.route.id);
1342
+ const matched_params = e.route.match(route);
1343
+ if (matched_params !== false) {
1344
+ this.log(3, "Matched regex route: ", e.route.id);
1904
1345
  endpoint = e;
1905
1346
  // insert path params into the stream when not already defined.
1906
- Object.keys(route.matched_params).walk((k) => {
1347
+ Object.keys(matched_params).walk((k) => {
1907
1348
  if (stream.params[k] == null) {
1908
- stream.params[k] = route.matched_params[k];
1349
+ stream.params[k] = matched_params[k];
1909
1350
  }
1910
1351
  });
1911
1352
  break;
@@ -1914,7 +1355,7 @@ export class Server {
1914
1355
  }
1915
1356
  }
1916
1357
  else {
1917
- log(3, "Matched route: ", endpoint.route.id);
1358
+ this.log(3, "Matched route: ", endpoint.route.id);
1918
1359
  }
1919
1360
  // No endpoint found.
1920
1361
  if (!endpoint) {
@@ -1926,11 +1367,6 @@ export class Server {
1926
1367
  // Set headers.
1927
1368
  this._set_header_defaults(stream);
1928
1369
  original_endpoint._set_headers(stream);
1929
- // When any cors origin is allowed and origin is present then respond with that origin.
1930
- if (stream.headers.origin && this.default_headers["Access-Control-Allow-Origin"] === "*") {
1931
- stream.remove_header("Access-Control-Allow-Origin", "access-control-allow-origin");
1932
- stream.set_header("Access-Control-Allow-Origin", stream.headers.origin);
1933
- }
1934
1370
  // Send.
1935
1371
  stream.send({ status: Status.no_content });
1936
1372
  log_endpoint_result();
@@ -1947,19 +1383,14 @@ export class Server {
1947
1383
  // Set all headers so we can send options.
1948
1384
  // Set default headers.
1949
1385
  this._set_header_defaults(stream);
1950
- // When any cors origin is allowed and origin is present then respond with that origin.
1951
- if (stream.headers.origin && this.default_headers["Access-Control-Allow-Origin"] === "*") {
1952
- stream.remove_header("Access-Control-Allow-Origin", "access-control-allow-origin");
1953
- stream.set_header("Access-Control-Allow-Origin", stream.headers.origin);
1954
- }
1955
1386
  // Serve options request.
1956
1387
  if (method === "OPTIONS") {
1957
1388
  try {
1958
1389
  await endpoint._serve_options(stream);
1959
1390
  }
1960
1391
  catch (err) {
1961
- error(`${method}:${endpoint_url}: `, err);
1962
- if (!stream.destroyed && !stream.closed) {
1392
+ this.log.error(`${method}:${endpoint_url}: `, err);
1393
+ if (!stream.destroyed && !stream.finished) {
1963
1394
  await serve_error_endpoint(500);
1964
1395
  log_endpoint_result();
1965
1396
  }
@@ -1969,7 +1400,7 @@ export class Server {
1969
1400
  return;
1970
1401
  }
1971
1402
  // Check rate limit.
1972
- if (this.online && this.production && this.rate_limit !== undefined && endpoint.rate_limit_groups.length > 0) {
1403
+ if (!this.offline && this.production && this.rate_limit !== undefined && endpoint.rate_limit_groups.length > 0) {
1973
1404
  const result = await this.rate_limit.limit(stream.ip, endpoint.rate_limit_groups);
1974
1405
  if (result != null) {
1975
1406
  stream.send({
@@ -1989,7 +1420,7 @@ export class Server {
1989
1420
  await stream.join();
1990
1421
  }
1991
1422
  catch (err) {
1992
- error(`${method}:${endpoint_url}: `, err);
1423
+ this.log.error(`${method}:${endpoint_url}: `, err);
1993
1424
  await serve_error_endpoint(500);
1994
1425
  log_endpoint_result();
1995
1426
  return;
@@ -1998,7 +1429,7 @@ export class Server {
1998
1429
  stream._parse_params();
1999
1430
  }
2000
1431
  catch (err) {
2001
- error(`${method}:${endpoint_url}: `, err);
1432
+ this.log.error(`${method}:${endpoint_url}: `, err);
2002
1433
  await serve_error_endpoint(400);
2003
1434
  log_endpoint_result();
2004
1435
  return;
@@ -2011,6 +1442,10 @@ export class Server {
2011
1442
  if (auth_result != null && !endpoint.is_static) {
2012
1443
  this.users._reset_cookies(stream);
2013
1444
  }
1445
+ // When the endpoint has a view or is text/html then redirect to signin page.
1446
+ if (auth_result != null && !endpoint.is_static && (endpoint.view != null || endpoint.content_type === "text/html")) {
1447
+ stream.set_header("Location", `/signin?next=${encodeURIComponent(stream.endpoint)}`);
1448
+ }
2014
1449
  // When the endpoint is authenticated and the authentication has failed then send the error response.
2015
1450
  if (auth_result != null && endpoint.authenticated) {
2016
1451
  stream.send(auth_result);
@@ -2023,8 +1458,8 @@ export class Server {
2023
1458
  await endpoint._serve(stream);
2024
1459
  }
2025
1460
  catch (err) {
2026
- error(`${method}:${endpoint_url}: `, err);
2027
- if (!stream.destroyed && !stream.closed) {
1461
+ this.log.error(`${method}:${endpoint_url}: `, err);
1462
+ if (!stream.destroyed && !stream.finished) {
2028
1463
  await serve_error_endpoint(500);
2029
1464
  log_endpoint_result();
2030
1465
  }
@@ -2032,7 +1467,7 @@ export class Server {
2032
1467
  }
2033
1468
  // Check if the response has been sent.
2034
1469
  if (!stream.finished) {
2035
- error(`${method}:${endpoint_url}: `, "Unfinished response.");
1470
+ this.log.error(`${method}:${endpoint_url}: `, "Unfinished response.");
2036
1471
  await serve_error_endpoint(500);
2037
1472
  log_endpoint_result();
2038
1473
  return;
@@ -2041,42 +1476,191 @@ export class Server {
2041
1476
  log_endpoint_result();
2042
1477
  }
2043
1478
  catch (err) {
2044
- error("Fatal error:", err);
1479
+ this.log.error(err);
2045
1480
  }
2046
1481
  }
2047
1482
  // ---------------------------------------------------------
2048
- // Server.
2049
- // Start the server.
2050
- /* @docs:
2051
- * @title: Start
2052
- * @description:
2053
- * Start the server.
2054
- * @usage:
2055
- * ...
2056
- * server.start();
2057
- */
2058
- async start() {
2059
- // Always initialize, even when forking.
2060
- await this.initialize();
2061
- // On production bundle all view endpoints.
2062
- if (this.production) {
2063
- for (const endpoint of this.endpoints.values()) {
2064
- if (endpoint.view) {
2065
- await endpoint.view.ensure_bundle();
2066
- }
2067
- }
2068
- }
2069
- // Start the rate limiting client/server, also when forking.
2070
- if (this.db && this.rate_limit) {
2071
- /* @performance */ this.performance.start();
2072
- await this.rate_limit.start();
2073
- /* @performance */ this.performance.end("init-rate-limit");
2074
- }
2075
- // Production & Master.
2076
- let forked = false;
2077
- if (this.production && this.multiprocessing && libcluster.isPrimary && this.processes > 1) {
2078
- this.log(0, `Starting ${this.processes} threads.`);
2079
- // Vars.
1483
+ // Server (private).
1484
+ /** The promise of database initialization and connecting. */
1485
+ _db_init_promise;
1486
+ // Initialize.
1487
+ // Initialize.
1488
+ async initialize() {
1489
+ // Logs.
1490
+ this.log(1, "Initializing server.");
1491
+ /* @performance */ const initialize_start = Date.now();
1492
+ /* @performance */ this.performance.start();
1493
+ // Initialize the database & connect first since this takes the longest.
1494
+ this._db_init_promise = (async () => {
1495
+ /* @performance */ let start = Date.now();
1496
+ await this.db.initialize();
1497
+ /* @performance */ this.performance.end("init-db", start);
1498
+ /* @performance */ start = Date.now();
1499
+ await this.db.connect();
1500
+ /* @performance */ this.performance.end("connect-db", start);
1501
+ })();
1502
+ // Create HTTPS server.
1503
+ if (this.tls) {
1504
+ this.https = http2.createSecureServer({
1505
+ key: new vlib.Path(this.tls.key).load_sync({ encoding: 'utf8' }),
1506
+ cert: new vlib.Path(this.tls.cert).load_sync({ encoding: 'utf8' }),
1507
+ ca: this.tls.ca == null ? undefined : new vlib.Path(this.tls.ca).load_sync({ encoding: 'utf8' }),
1508
+ passphrase: this.tls.passphrase,
1509
+ allowHTTP1: true,
1510
+ });
1511
+ this.https.on('stream', (stream, headers) => {
1512
+ this._serve(stream, headers, undefined, undefined);
1513
+ });
1514
+ // HTTP/1.1 (compatibility 'request' is also emitted for HTTP/2; filter it out)
1515
+ this.https.on("request", (req, res) => {
1516
+ if (req.httpVersionMajor === 1) {
1517
+ this._serve(undefined, undefined, req, res);
1518
+ }
1519
+ });
1520
+ }
1521
+ // Payments require HTTPS in production.
1522
+ else if (this.production && this.payments) {
1523
+ throw Error("Accepting payments in production mode requires HTTPS.");
1524
+ }
1525
+ /* @performance */ this.performance.end("create-https-server");
1526
+ // Create http server.
1527
+ if (this.tls) {
1528
+ // Redirect HTTP requests to HTTPS.
1529
+ this.http = http.createServer((request, response) => {
1530
+ const reqUrl = typeof request.url === "string" ? request.url : "/";
1531
+ // Build redirect using the canonical configured domain, not the untrusted Host header.
1532
+ const location = `https://${this.domain}${reqUrl}`;
1533
+ // 308 preserves method and body; safe for non-GET as well.
1534
+ response.writeHead(308, { Location: location });
1535
+ response.end();
1536
+ });
1537
+ }
1538
+ else {
1539
+ // Serve http.
1540
+ this.http = http.createServer((req, res) => {
1541
+ this._serve(undefined, undefined, req, res);
1542
+ });
1543
+ }
1544
+ /* @performance */ this.performance.end("create-http-server");
1545
+ // Initialize default headers.
1546
+ this._init_default_headers();
1547
+ /* @performance */ this.performance.end("init-default-headers");
1548
+ // Create default endpoints.
1549
+ this._create_default_endpoints();
1550
+ /* @performance */ this.performance.end("create-default-endpoints");
1551
+ // Create admin endpoints.
1552
+ // this._create_admin_endpoint();
1553
+ // /* @performance */ this.performance.end("create-admin-endpoints");
1554
+ // Create static endpoints.
1555
+ await this._initialize_statics();
1556
+ /* @performance */ this.performance.end("_initialize_statics()");
1557
+ // Add promises using the database.
1558
+ const promises = [];
1559
+ /* @performance */ this.performance.start();
1560
+ // Initialize keys,
1561
+ // uses the database so dont await in non production,
1562
+ // to speed up restarts.
1563
+ if (this.production) {
1564
+ promises.push(this._initialize_keys());
1565
+ }
1566
+ else {
1567
+ this._initialize_keys().then(() => {
1568
+ this.log(1, "Finished loading keys.");
1569
+ }).catch((err) => {
1570
+ this.log(0, `Error while loading keys.`);
1571
+ this.log.error(err);
1572
+ });
1573
+ }
1574
+ // /* @performance */ this.performance.end("load-keys");
1575
+ // Initialize users.
1576
+ promises.push(this.users._initialize());
1577
+ // /* @performance */ this.performance.end("users._initialize()");
1578
+ // Payments.
1579
+ if (this.payments !== undefined) {
1580
+ promises.push(this.payments._initialize());
1581
+ // /* @performance */ this.performance.end("payments._initialize()");
1582
+ }
1583
+ // Create sitemap when it does not exist.
1584
+ // Must be done at the end of initialization func since some funcs might still create endpoints.
1585
+ if (this._find_endpoint("/sitemap.xml") == null) {
1586
+ promises.push(this._create_sitemap());
1587
+ // /* @performance */ this.performance.end("_create_sitemap()");
1588
+ }
1589
+ // Create robots.txt when it does not exist.
1590
+ // Must be done at the end of initialization func since some funcs might still create endpoints.
1591
+ if (this._find_endpoint("/robots.txt") == null) {
1592
+ promises.push(this._create_robots_txt());
1593
+ // /* @performance */ this.performance.end("_create_robots_txt()");
1594
+ }
1595
+ // Get the icon and stroke icon file paths when defined.
1596
+ if (this.company.stroke_icon || this.company.icon) {
1597
+ for (const endpoint of this.endpoints.values()) {
1598
+ if (this.company.stroke_icon_path == null && endpoint.route.endpoint === this.company.stroke_icon) {
1599
+ this.company.stroke_icon_path = endpoint.file_path?.str() || undefined;
1600
+ }
1601
+ if (this.company.icon_path == null && endpoint.route.endpoint === this.company.icon) {
1602
+ this.company.icon_path = endpoint.file_path?.str() || undefined;
1603
+ }
1604
+ }
1605
+ if (this.company.stroke_icon != null && this.company.stroke_icon_path == null) {
1606
+ throw Error(`Unable to find the company's stroke icon endpoint "${this.company.stroke_icon}".`);
1607
+ }
1608
+ if (this.company.icon != null && this.company.icon_path == null) {
1609
+ throw Error(`Unable to find the company's icon endpoint "${this.company.icon}".`);
1610
+ }
1611
+ }
1612
+ // Await all promises.
1613
+ await Promise.all(promises);
1614
+ /* @performance */ this.performance.end("awaiting-promise-list");
1615
+ // Initialize all endpoints.
1616
+ this.performance.start();
1617
+ for (const endpoint of this.endpoints.values()) {
1618
+ endpoint._initialize(this);
1619
+ }
1620
+ for (const endpoint of this.err_endpoints.values()) {
1621
+ endpoint._initialize(this);
1622
+ }
1623
+ /* @performance */ this.performance.end("initialize-endpoints");
1624
+ // On initialize callbacks.
1625
+ for (const callback of this.events.get("initialize")) {
1626
+ const res = callback();
1627
+ if (res instanceof Promise) {
1628
+ await res;
1629
+ }
1630
+ }
1631
+ /* @performance */ this.performance.end("on-initialize-callbacks");
1632
+ /* @performance */ this.performance.end("initialize()", initialize_start);
1633
+ }
1634
+ // ---------------------------------------------------------
1635
+ // Server.
1636
+ /**
1637
+ * Start the server.
1638
+ * @example
1639
+ * ...
1640
+ * server.start();
1641
+ */
1642
+ async start() {
1643
+ // Always initialize, even when forking.
1644
+ await this.initialize();
1645
+ // On production bundle all view endpoints.
1646
+ if (this.production) {
1647
+ for (const endpoint of this.endpoints.values()) {
1648
+ if (endpoint.view) {
1649
+ await endpoint.view.ensure_bundle();
1650
+ }
1651
+ }
1652
+ }
1653
+ // Start the rate limiting client/server, also when forking.
1654
+ if (this.rate_limit) {
1655
+ /* @performance */ this.performance.start();
1656
+ await this.rate_limit.start();
1657
+ /* @performance */ this.performance.end("init-rate-limit");
1658
+ }
1659
+ // Production & Master.
1660
+ let forked = false;
1661
+ if (this.production && this.threading.enabled && libcluster.isPrimary && this.threading.threads > 1) {
1662
+ this.log(0, `Starting ${this.threading.threads} threads.`);
1663
+ // Vars.
2080
1664
  let active_threads = 0;
2081
1665
  const thread_ids = {};
2082
1666
  const restart_limiters = {};
@@ -2085,14 +1669,14 @@ export class Server {
2085
1669
  // Fork.
2086
1670
  const worker = libcluster.fork();
2087
1671
  // Log.
2088
- log(restart ? 0 : 1, `Starting thread ${worker.process.pid}.`);
1672
+ this.log(restart ? 0 : 1, `Starting thread ${worker.process.pid}.`);
2089
1673
  // Cache thread id.
2090
1674
  thread_ids[worker.process.pid] = thread_id;
2091
1675
  // Increment active threads.
2092
1676
  ++active_threads;
2093
1677
  };
2094
1678
  // Fork workers.
2095
- for (let i = 0; i < this.processes; i++) {
1679
+ for (let i = 0; i < this.threading.threads; i++) {
2096
1680
  // Generate thread id.
2097
1681
  let thread_id;
2098
1682
  while ((thread_id = vlib.String.random(8)) && Object.values(thread_ids).includes(thread_id)) { }
@@ -2102,7 +1686,7 @@ export class Server {
2102
1686
  start_thread(thread_id);
2103
1687
  }
2104
1688
  // Save status.
2105
- await this._sys_db.save("status", {
1689
+ await this._website_status_db.set({ id: "status" }, {
2106
1690
  running_since: Date.now(),
2107
1691
  total_threads: active_threads,
2108
1692
  running_threads: active_threads,
@@ -2113,7 +1697,7 @@ export class Server {
2113
1697
  const thread_id = thread_ids[worker.process.pid];
2114
1698
  delete thread_ids[worker.process.pid];
2115
1699
  // Logs.
2116
- error(`Thread ${worker.process.pid} crashed.`);
1700
+ this.log.error(`Thread ${worker.process.pid} crashed.`);
2117
1701
  // Restart with limit.
2118
1702
  const limiter = restart_limiters[thread_id];
2119
1703
  if (limiter != null && limiter.limit()) {
@@ -2122,18 +1706,18 @@ export class Server {
2122
1706
  }
2123
1707
  // Reached limit, shutdown thread.
2124
1708
  else {
2125
- error(`Thread ${worker.process.pid} is being shut down due too its periodic restart limit.`);
1709
+ this.log.error(`Thread ${worker.process.pid} is being shut down due to its periodic restart limit.`);
2126
1710
  --active_threads;
2127
- await this._sys_db.save("status", { running_threads: active_threads });
1711
+ await this._website_status_db.save({ id: "status" }, { $inc: { running_threads: -1 } });
2128
1712
  if (active_threads === 0) {
2129
- error(`All threads died, stopping server.`);
1713
+ this.log.error(`All threads died, stopping server.`);
2130
1714
  process.exit(0);
2131
1715
  }
2132
1716
  }
2133
1717
  });
2134
1718
  }
2135
1719
  else {
2136
- forked = this.production && this.multiprocessing;
1720
+ forked = this.production && this.threading.enabled;
2137
1721
  // Load worker class modules.
2138
1722
  // if (libcluster.isWorker) {
2139
1723
  // const worker = new WorkerClass();
@@ -2145,10 +1729,10 @@ export class Server {
2145
1729
  if (!is_running) {
2146
1730
  is_running = true;
2147
1731
  if (this.https !== undefined) {
2148
- log(0, `Running on http://${this.ip}:${this.port} and https://${this.ip}:${this.https_port}.`);
1732
+ this.log(0, `Running on http://${this.ip}:${this.port} and https://${this.ip}:${this.https_port}.`);
2149
1733
  }
2150
1734
  else {
2151
- log(0, `Running on http://${this.ip}:${this.port}.`);
1735
+ this.log(0, `Running on http://${this.ip}:${this.port}.`);
2152
1736
  }
2153
1737
  }
2154
1738
  };
@@ -2177,8 +1761,23 @@ export class Server {
2177
1761
  this.https.on("error", on_error);
2178
1762
  }
2179
1763
  // Set signals.
2180
- process.on('SIGTERM', () => process.exit(0));
2181
- process.on('SIGINT', () => process.exit(0));
1764
+ let graceful_shutdown_shutting_down = false;
1765
+ const graceful_shutdown = async () => {
1766
+ if (graceful_shutdown_shutting_down)
1767
+ return;
1768
+ graceful_shutdown_shutting_down = true;
1769
+ try {
1770
+ await this.stop();
1771
+ }
1772
+ catch (e) {
1773
+ this.log.error("Shutdown error:", e);
1774
+ }
1775
+ finally {
1776
+ process.exit(0);
1777
+ }
1778
+ };
1779
+ process.on('SIGTERM', graceful_shutdown);
1780
+ process.on('SIGINT', graceful_shutdown);
2182
1781
  // Send running message.
2183
1782
  if (process.env.VOLT_FILE_WATCHER === "1") {
2184
1783
  new vlib.Path(process.env.VOLT_STARTED_FILE).save_sync("1");
@@ -2191,45 +1790,35 @@ export class Server {
2191
1790
  /* @performance */ this.performance.end("listen");
2192
1791
  }
2193
1792
  // On start callbacks.
2194
- for (const callback of this._on_start) {
1793
+ this.performance.start();
1794
+ for (const callback of this.events.get("start")) {
2195
1795
  const res = callback({ forked });
2196
1796
  if (res instanceof Promise) {
2197
1797
  await res;
2198
1798
  }
2199
1799
  }
1800
+ /* @performance */ this.performance.end("on-start-callbacks");
2200
1801
  // Start browser preview on primary node.
2201
1802
  // if (this.browser_preview && !forked) {
2202
1803
  // await this.browser_preview.start();
2203
1804
  // await this.browser_preview.navigate(this.full_domain);
2204
1805
  // }
2205
1806
  /* @performance */
2206
- debug(2, () => this.performance.dump(v => v >= 50));
2207
- }
2208
- /* @docs:
2209
- * @title: On start
2210
- * @description:
2211
- * Add an (async) callback which will be executed at the end of `server.start()`.
2212
- * The callback may take arguments `({forked <boolean>})`.
2213
- * @usage:
2214
- * ...
2215
- * server.on_start(({forked}) => console.log("Hello World!"));
2216
- */
2217
- on_start(callback) {
2218
- this._on_start.append(callback);
1807
+ console.log(this.performance.dump());
1808
+ // console.log(this.performance.dump(v => v >= 50));
1809
+ // debug(2, () => this.performance.dump(v => v >= 50));
2219
1810
  }
2220
1811
  // Stop the server.
2221
- /* @docs:
2222
- * @title: Stop
2223
- * @description:
2224
- * Stop the server.
2225
- * @usage:
2226
- * ...
2227
- * server.stop();
1812
+ /**
1813
+ * Stop the server.
1814
+ * @example
1815
+ * ...
1816
+ * server.stop();
2228
1817
  */
2229
1818
  async stop() {
2230
- log(0, "Stopping the server...");
1819
+ this.log(0, "Stopping the server...");
2231
1820
  // On stop callbacks.
2232
- for (const callback of this._on_stop) {
1821
+ for (const callback of this.events.get("stop")) {
2233
1822
  const res = callback();
2234
1823
  if (res instanceof Promise) {
2235
1824
  await res;
@@ -2239,23 +1828,14 @@ export class Server {
2239
1828
  if (this.rate_limit) {
2240
1829
  await this.rate_limit.stop();
2241
1830
  }
2242
- // Stop view source file watcher.
2243
- if (this._stop_tscompiler_watcher) {
2244
- log(0, "Stopping typescript watcher.");
2245
- this._stop_tscompiler_watcher();
2246
- }
2247
1831
  // Stop sockets.
2248
- if (this.https) {
2249
- await this.https.close();
2250
- }
2251
- if (this.http) {
2252
- await this.http.close();
2253
- }
2254
- if (this.db) {
2255
- await this.db.close();
2256
- }
1832
+ if (this.https)
1833
+ this.https.close();
1834
+ if (this.http)
1835
+ this.http.close();
1836
+ await this.db.close();
2257
1837
  // Stop the logger.
2258
- logger.stop();
1838
+ this.log.stop();
2259
1839
  // setTimeout(() => {
2260
1840
  // thread_monitor.dump_active_resources({
2261
1841
  // // min_age: 5000,
@@ -2264,76 +1844,74 @@ export class Server {
2264
1844
  // });
2265
1845
  // }, 6000);
2266
1846
  }
2267
- /* @docs:
2268
- * @title: On stop
2269
- * @description:
2270
- * Set an (async) callback which will be executed at the start of `server.stop()`.
2271
- * @usage:
2272
- * ...
2273
- * server.on_stop(() => console.log("Hello World!"));
1847
+ // ---------------------------------------------------------
1848
+ // Events.
1849
+ /** Add an event callback. */
1850
+ on(name, callback) {
1851
+ this.events.add(name, callback);
1852
+ return this;
1853
+ }
1854
+ /** Remove an event callback. */
1855
+ off(name, callback) {
1856
+ this.events.remove(name, callback);
1857
+ return this;
1858
+ }
1859
+ // ---------------------------------------------------------
1860
+ // Endpoints.
1861
+ /**
1862
+ * Add a single endpoint.
1863
+ * Only supports a single endpoint due to parameter inference.
1864
+ * @note An error is thrown when the endpoint route already exists.
1865
+ * @template Response User inputted response type that will be returned as response, optionaly typing used for consistency.
1866
+ * @template S system template for inferring the endpoint callback parameters.
1867
+ * @param endpoint The endpoint or endpoint options to add.
1868
+ * @returns A registered endpoint object that can for instance be used to infer the endpoint parameters.
2274
1869
  */
2275
- on_stop(callback) {
2276
- this._on_stop.append(callback);
1870
+ endpoint(endpoint) {
1871
+ const e = endpoint instanceof Endpoint ? endpoint : new Endpoint(endpoint);
1872
+ this._check_duplicate_route(e.route);
1873
+ this.endpoints.set(e.route.id, e);
1874
+ return {
1875
+ Params: undefined,
1876
+ method: e.route.method,
1877
+ Method: e.route.method,
1878
+ endpoint: e.route.endpoint,
1879
+ Endpoint: e.route.endpoint,
1880
+ route: e.route,
1881
+ };
2277
1882
  }
2278
- // Fetch status.
2279
- /* @docs:
2280
- @title: Fetch status.
2281
- @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.
2282
- @note: This function can be called without initializing the server.
2283
- @param:
2284
- @name: type
2285
- @desc: The wanted output type. Either an `object` or a `string` type for CLI purposes.
1883
+ // Add an error endpoint.
1884
+ /**
1885
+ * Add an endpoint per error status code.
1886
+ * @param status_code
1887
+ * The status code of the error.
1888
+ *
1889
+ * The supported status codes are:
1890
+ * * `404`
1891
+ * * `400` (Will not be used when the endpoint uses an API callback).
1892
+ * * `403`
1893
+ * * `404`
1894
+ * * `500`
1895
+ * @param endpoint The error endpoint or error endpoint options
2286
1896
  */
2287
- async fetch_status(type = "object") {
2288
- // Load key.
2289
- const key_path = this.source.join(".status/key");
2290
- if (!key_path.exists()) {
2291
- throw new Error("No status key has been generated yet. Start your server first.");
2292
- }
2293
- const key = key_path.load_sync();
2294
- // Make request.
2295
- const { body: status } = await vlib.request({
2296
- host: this.domain,
2297
- endpoint: "/.status",
2298
- method: "GET",
2299
- params: { key },
2300
- query: true,
2301
- json: true,
2302
- });
2303
- // String type.
2304
- if (type === "string") {
2305
- if (status.running_since != null) {
2306
- status.running_since = new vlib.Date(status.running_since).format("%d-%m-%y %H:%M:%S");
2307
- }
2308
- let str = `${this.domain}:\n`;
2309
- Object.keys(status).forEach((key) => {
2310
- str += ` * ${key}: ${status[key]}\n`;
2311
- });
2312
- str = str.substr(0, str.length - 1);
2313
- return str;
2314
- }
2315
- // Response.
2316
- return status;
1897
+ error_endpoint(status_code, endpoint) {
1898
+ const e = endpoint instanceof Endpoint ? endpoint : new Endpoint(endpoint);
1899
+ this._check_duplicate_route(e.route);
1900
+ this.err_endpoints.set(status_code, e);
1901
+ return this;
2317
1902
  }
2318
1903
  // ---------------------------------------------------------
2319
1904
  // Content Security Policy.
2320
1905
  // Add a csp.
2321
- /* @docs:
2322
- * @title: Add CSP
2323
- * @description: Add an url to the Content-Security-Policy. This function does not overwrite the existing key's value.
2324
- * @warning: This function no longer has any effect when `Server.start()` has been called.
2325
- * @parameter:
2326
- * @name: key
2327
- * @description: The Content-Security-Policy key, e.g. `script-src`.
2328
- * @type: string
2329
- * @parameter:
2330
- * @name: value
2331
- * @description: The value to add to the Content-Security-Policy key.
2332
- * @type: null, string, string[]
2333
- * @usage:
2334
- * ...
2335
- * server.add_csp("script-src", "somewebsite.com");
2336
- * server.add_csp("upgrade-insecure-requests");
1906
+ /**
1907
+ * Add an url to the Content-Security-Policy. This function does not overwrite the existing key's value.
1908
+ * @warning This function no longer has any effect when `Server.start()` has been called.
1909
+ * @param key The Content-Security-Policy key, e.g. `script-src`.
1910
+ * @param value The value to add to the Content-Security-Policy key.
1911
+ * @example
1912
+ * ...
1913
+ * server.add_csp("script-src", "somewebsite.com");
1914
+ * server.add_csp("upgrade-insecure-requests");
2337
1915
  */
2338
1916
  add_csp(key, value = null) {
2339
1917
  if (this.csp[key] === undefined) {
@@ -2351,22 +1929,15 @@ export class Server {
2351
1929
  }
2352
1930
  }
2353
1931
  // Remove a csp.
2354
- /* @docs:
2355
- * @title: Remove CSP
2356
- * @description: Remove an url from the Content-Security-Policy. This function does not overwrite the existing key's value.
2357
- * @warning: This function no longer has any effect when `Server.start()` has been called.
2358
- * @parameter:
2359
- * @name: key
2360
- * @description: The Content-Security-Policy key, e.g. `script-src`.
2361
- * @type: string
2362
- * @parameter:
2363
- * @name: value
2364
- * @description: The value to remove from the Content-Security-Policy key.
2365
- * @type: null, string
2366
- * @usage:
2367
- * ...
2368
- * server.remove_csp("script-src", "somewebsite.com");
2369
- * server.remove_csp("upgrade-insecure-requests");
1932
+ /**
1933
+ * Remove an url from the Content-Security-Policy. This function does not overwrite the existing key's value.
1934
+ * @warning This function no longer has any effect when `Server.start()` has been called.
1935
+ * @param key The Content-Security-Policy key, e.g. `script-src`.
1936
+ * @param value The value to remove from the Content-Security-Policy key.
1937
+ * @example
1938
+ * ...
1939
+ * server.remove_csp("script-src", "somewebsite.com");
1940
+ * server.remove_csp("upgrade-insecure-requests");
2370
1941
  */
2371
1942
  remove_csp(key, value = null) {
2372
1943
  if (this.csp[key] === undefined) {
@@ -2380,25 +1951,60 @@ export class Server {
2380
1951
  }
2381
1952
  }
2382
1953
  // Delete a csp key.
2383
- /* @docs:
2384
- * @title: Delete CSP
2385
- * @description: Delete an key from the Content-Security-Policy.
2386
- * @warning: This function no longer has any effect when `Server.start()` has been called.
2387
- * @parameter:
2388
- * @name: key
2389
- * @description: The Content-Security-Policy key, e.g. `script-src`.
2390
- * @type: string
2391
- * @usage:
2392
- * ...
2393
- * server.del_csp("script-src");
2394
- * server.del_csp("upgrade-insecure-requests");
1954
+ /**
1955
+ * Delete an key from the Content-Security-Policy.
1956
+ * @warning This function no longer has any effect when `Server.start()` has been called.
1957
+ * @param key The Content-Security-Policy key, e.g. `script-src`.
1958
+ * @example
1959
+ * ...
1960
+ * server.del_csp("script-src");
1961
+ * server.del_csp("upgrade-insecure-requests");
2395
1962
  */
2396
1963
  del_csp(key) {
2397
1964
  delete this.csp[key];
2398
1965
  }
2399
1966
  // ---------------------------------------------------------
1967
+ // Status.
1968
+ // Fetch status.
1969
+ /**
1970
+ * 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.
1971
+ * @note This function can be called without initializing the server.
1972
+ * @param type The wanted output type. Either an `object` or a `string` type for CLI purposes.
1973
+ */
1974
+ async fetch_status(type = "object") {
1975
+ // Load key.
1976
+ const key_path = this.source.join(".status/key");
1977
+ if (!key_path.exists()) {
1978
+ throw new Error("No status key has been generated yet. Start your server first.");
1979
+ }
1980
+ const key = key_path.load_sync();
1981
+ // Make request.
1982
+ const { body: status } = await vlib.request({
1983
+ host: this.domain,
1984
+ endpoint: "/.status",
1985
+ method: "GET",
1986
+ params: { key },
1987
+ query: true,
1988
+ json: true,
1989
+ });
1990
+ // String type.
1991
+ if (type === "string") {
1992
+ if (status.running_since != null) {
1993
+ status.running_since = new vlib.Date(status.running_since).format("%d-%m-%y %H:%M:%S");
1994
+ }
1995
+ let str = `${this.domain}:\n`;
1996
+ Object.keys(status).forEach((key) => {
1997
+ str += ` * ${key}: ${status[key]}\n`;
1998
+ });
1999
+ str = str.substr(0, str.length - 1);
2000
+ return str;
2001
+ }
2002
+ // Response.
2003
+ return status;
2004
+ }
2005
+ // ---------------------------------------------------------
2400
2006
  // TLS.
2401
- // Generate a key and csr for tls.
2007
+ /** Generate a key and csr for tls. */
2402
2008
  async generate_ssl_key({ output_path, ec = true, }) {
2403
2009
  // Args.
2404
2010
  if (output_path == null) {
@@ -2422,7 +2028,7 @@ export class Server {
2422
2028
  throw Error(`Encountered an error while generating the private key [${proc.exit_status}]: ${proc.err}`);
2423
2029
  }
2424
2030
  }
2425
- // Generate a csr for tls.
2031
+ /** Generate a csr for tls. */
2426
2032
  async generate_csr({ output_path, key_path, name, domain, organization_unit, country_code, province, city, }) {
2427
2033
  // Args.
2428
2034
  if (key_path == null) {
@@ -2434,7 +2040,7 @@ export class Server {
2434
2040
  // Paths.
2435
2041
  const key = new vlib.Path(key_path);
2436
2042
  if (!key.exists()) {
2437
- throw Error(`Key path "${key.str()}" already exists, remove the file manually to continue.`);
2043
+ throw Error(`Key path "${key.str()}" does not exist.`);
2438
2044
  }
2439
2045
  const csr = new vlib.Path(output_path);
2440
2046
  if (csr.exists()) {
@@ -2447,237 +2053,52 @@ export class Server {
2447
2053
  args: [
2448
2054
  "req", "-new", "-key", key.str(), "-out", csr.str(),
2449
2055
  "-subj",
2450
- "\"" +
2451
- "/C=" + country_code +
2452
- "/ST=" + province +
2453
- "/L=" + city +
2454
- "/O=" + name +
2455
- "/OU=" + organization_unit +
2456
- "/CN=" + domain +
2457
- "\""
2056
+ `/C=${country_code}/ST=${province}/L=${city}/O=${name}/OU=${organization_unit}/CN=${domain}`
2458
2057
  ],
2459
2058
  opts: { stdio: "inherit" },
2460
2059
  });
2461
2060
  if (proc.exit_status != 0) {
2462
2061
  throw Error(`Encountered an error while generating the CSR [${proc.exit_status}]: ${proc.err}`);
2463
2062
  }
2464
- log(0, `Generated the tls key with CSR for domain "${this.domain}".`);
2063
+ this.log(0, `Generated the tls key with CSR for domain "${this.domain}".`);
2465
2064
  }
2466
2065
  // ---------------------------------------------------------
2467
- // Endpoints.
2468
- // private registered_routes: Map<string, Array<string | RegExp>> = new Map();
2469
- /**
2470
- * Checks if an endpoint route already exists.
2471
- * @param method HTTP method
2472
- * @param endpoint String path or RegExp
2473
- */
2474
- _check_duplicate_route(route) {
2475
- const e = this._find_endpoint(route);
2476
- if (e) {
2477
- throw new Error(`Duplicate "${route.method}:${route.endpoint_str}" endpoint route, it is already defined by endpoint "${e.id}".`);
2478
- }
2479
- }
2480
- /**
2481
- * Add a single endpoint.
2482
- * Only supports a single endpoint due to parameter inference.
2483
- * @param endpoint The endpoint or endpoint options to add.
2484
- */
2485
- endpoint(endpoint) {
2486
- const e = endpoint instanceof Endpoint ? endpoint : new Endpoint(endpoint);
2487
- this._check_duplicate_route(e.route);
2488
- this.endpoints.set(e.route.id, e);
2489
- return this;
2490
- }
2491
- // Add an error endpoint.
2492
- /**
2493
- * Add an endpoint per error status code.
2494
- * @param status_code
2495
- * The status code of the error.
2496
- *
2497
- * The supported status codes are:
2498
- * * `404`
2499
- * * `400` (Will not be used when the endpoint uses an API callback).
2500
- * * `403`
2501
- * * `404`
2502
- * * `500`
2503
- * @param endpoint The error endpoint or error endpoint options
2504
- */
2505
- error_endpoint(status_code, endpoint) {
2506
- const e = endpoint instanceof Endpoint ? endpoint : new Endpoint(endpoint);
2507
- this._check_duplicate_route(e.route);
2508
- this.err_endpoints.set(status_code, e);
2509
- return this;
2510
- }
2511
- // ---------------------------------------------------------
2512
- // Functions.
2513
- // Send a mail.
2514
- /* @docs:
2515
- * @title: Send Mail
2516
- * @description: Send one or multiple mails.
2517
- * @note: Make sure the domain's DNS records SPF and DKIM are properly configured when sending attachments.
2518
- * @return:
2519
- * Returns a promise that will be resolved or rejected when the mail has been sent.
2520
- * @parameter:
2521
- * @name: sender
2522
- * @description:
2523
- * The sender address.
2524
- * A sender address may either be a string with the email address, e.g. `your@email.com`.
2525
- * Or an array with the sender name and email address, e.g. `["Sender", "your@email.com"]`.
2526
- * @type: string, array
2527
- * @parameter:
2528
- * @name: recipients
2529
- * @description:
2530
- * The recipient addresses.
2531
- * A reciepient address may either be a string with the email address, e.g. `your@email.com`.
2532
- * Or an array with the sender name and email address, e.g. `["Sender", "your@email.com"]`.
2533
- * @type: array[string, array]
2534
- * @parameter:
2535
- * @name: subject
2536
- * @description: The subject text.
2537
- * @type: string
2538
- * @parameter:
2539
- * @name: body
2540
- * @description: The body text.
2541
- * @type: string
2542
- * @parameter:
2543
- * @name: attachments
2544
- * @description: An array with absolute file paths for attachments, or an array with nodemailer attachment objects.
2545
- * @type: array[string], array[object]
2546
- * @usage:
2547
- * ...
2548
- * await server.send_mail({
2549
- * sender: ["Sender Name", "sender\@email.com"],
2550
- * recipients: [
2551
- * ["Recipient Name", "recipient1\@email.com"],
2552
- * "recipient2\@email.com",
2553
- * },
2554
- * subject: "Example Mail",
2555
- * body: "Hello World!",
2556
- * attachments: ["/path/to/image.png"]
2557
- * });
2558
- */
2559
- async send_mail({ sender = undefined, recipients = [], subject = undefined, body = "", attachments = [], }) {
2560
- // Not enabled.
2561
- if (this.smtp === undefined) {
2562
- throw new Error("SMTP is not enabled, define the required server argument on initialization to enable smtp.");
2563
- }
2564
- // Convert MailElement to html.
2565
- if (body instanceof Mail.MailElement) {
2566
- body = body.html();
2567
- }
2568
- // Check args.
2569
- if (sender == null && this.smtp_sender != null) {
2570
- sender = this.smtp_sender;
2571
- }
2572
- if (recipients.length === 0) {
2573
- throw new Error(`The mail has no recipients.`);
2574
- }
2575
- if (sender == null) {
2576
- throw new Error(`Parameter "sender" should be a defined value of type "string" or "array".`);
2577
- }
2578
- // Format address wrapper.
2579
- const format_address = (address) => {
2580
- if (Array.isArray(address)) {
2581
- return `${address[0]} <${address[1]}>`;
2582
- }
2583
- return address;
2584
- };
2585
- // Create to array.
2586
- const to = [];
2587
- recipients.forEach((address) => to.push(format_address(address)));
2588
- // Create attachments array.
2589
- let attached_files = [];
2590
- if (attachments != null) {
2591
- attachments.forEach((path) => {
2592
- if (path instanceof vlib.Path) {
2593
- attached_files.push({
2594
- filename: path.full_name(),
2595
- path: path.str(),
2596
- content: path.load_sync(),
2597
- });
2598
- }
2599
- else if (typeof path === "string") {
2600
- const p = new vlib.Path(path);
2601
- attached_files.push({
2602
- filename: p.full_name(),
2603
- path: path,
2604
- content: p.load_sync(),
2605
- });
2606
- }
2607
- else {
2608
- attached_files.push(path);
2609
- }
2610
- });
2611
- }
2612
- // Send mail.
2613
- try {
2614
- await this.smtp.sendMail({
2615
- from: format_address(sender),
2616
- to: to,
2617
- subject: subject,
2618
- html: body,
2619
- attachments: attached_files,
2620
- });
2621
- }
2622
- catch (error) {
2623
- throw new Error(error.message); // to keep readable stacktrace.
2624
- }
2625
- }
2626
- // ---------------------------------------------------------
2627
- // Default callbacks.
2628
- // These can all be overwritten by the user.
2629
- // @todo add scheme for payment params.
2630
- // On delete user.
2631
- /* @docs:
2632
- * @title: On delete user
2633
- * @description: This function can be overridden with a callback for when a user is deleted.
2634
- * @parameter:
2635
- * @name: uid
2636
- * @description: The uid of the deleted user.
2637
- * @type: string, array
2638
- * @usage:
2639
- * ...
2640
- * server.on_delete_user = ({uid}) => {}
2641
- */
2642
- async on_delete_user({ uid }) { }
2643
- // On successfull one-time payment.
2644
- // This gets called for every product in the payment.
2066
+ // DEPRECATED
2067
+ // these will all be removed and replaced when using stripe instead of paddle.
2068
+ /** Called for each product in a successful one-time payment. Override to implement your logic. */
2645
2069
  async on_payment({ product, payment }) { }
2646
- // On successfull subscription.
2647
- // This gets called for every product in the payment.
2070
+ /** Called for each product in a successful subscription. Override to implement your logic. */
2648
2071
  async on_subscription({ product, payment }) { }
2649
2072
  // On failed one-time or recurring payment.
2650
2073
  // async on_failed_payment({ payment }: { payment: any }): Promise<void> {}
2651
- // On successfull cancellation.
2074
+ /** Called when a cancellation succeeds. Override to implement your logic. */
2652
2075
  async on_cancellation({ payment, line_items }) { }
2653
2076
  // On failed cancellation.
2654
2077
  // async on_failed_cancellation({ payment, line_items }: { payment: any; line_items: any[] }): Promise<void> {}
2655
- // On successfull refund.
2656
- // The line items array are the items were refunded.
2078
+ /** Called when a refund succeeds. The line items array are the items that were refunded. */
2657
2079
  async on_refund({ payment, line_items }) { }
2658
- // On failed refund.
2659
- // The line items array are the items were the refund failed.
2080
+ /** Called when a refund fails. The line items array are the items where the refund failed. */
2660
2081
  async on_failed_refund({ payment, line_items }) { }
2661
- // On chargeback.
2662
- // The line items array are the items were charged back.
2082
+ /** Called when a chargeback occurs. The line items array are the items that were charged back. */
2663
2083
  async on_chargeback({ payment, line_items }) { }
2664
- // On failed chargeback.
2665
- // The line items array are the items were the chargeback failed.
2084
+ /** Called when a chargeback fails. The line items array are the items where the chargeback failed. */
2666
2085
  async on_failed_chargeback({ payment, line_items }) { }
2667
2086
  // Mail template.
2087
+ /** Build the base email layout used by the various transactional email builders. */
2668
2088
  _mail_template({ max_width = 400, children = [], }) {
2669
- const style = this.mail_style;
2670
- const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2089
+ this.assert_mail();
2090
+ const style = this.mail.style;
2091
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = MailUI;
2671
2092
  // Create header.
2672
2093
  let header;
2673
2094
  if (this.company.stroke_icon != null) {
2674
2095
  header = [
2675
- Image(`${this.full_domain}/${this.company.stroke_icon}`).height(16),
2096
+ Image(`${this.full_domain}${this.company.stroke_icon ?? ""}`).height(16),
2676
2097
  ];
2677
2098
  }
2678
2099
  else if (this.company.icon != null) {
2679
2100
  header = [
2680
- Image(`${this.full_domain}/${this.company.icon}`).frame(20, 40),
2101
+ Image(`${this.full_domain}${this.company.icon ?? ""}`).frame(20, 40),
2681
2102
  ];
2682
2103
  }
2683
2104
  if (header) {
@@ -2687,7 +2108,7 @@ export class Server {
2687
2108
  .center_vertical()).margin_bottom(15);
2688
2109
  }
2689
2110
  // Create mail.
2690
- return Mail.Mail(Table(TableData(Table(
2111
+ return MailUI.Mail(Table(TableData(Table(
2691
2112
  // Header.
2692
2113
  header,
2693
2114
  // Widget.
@@ -2708,10 +2129,14 @@ export class Server {
2708
2129
  .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);
2709
2130
  }
2710
2131
  // Render payment line items.
2132
+ /** Helper that renders a list of payment line items for use in transactional emails. */
2711
2133
  _render_mail_payment_line_items({ payment, line_items, show_total_due = false }) {
2134
+ if (!this.payments)
2135
+ throw new Error("Payments not initialized");
2136
+ this.assert_mail();
2712
2137
  // Shortcuts.
2713
- const style = this.mail_style;
2714
- const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2138
+ const style = this.mail.style;
2139
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = MailUI;
2715
2140
  // Render payment line item for a mail.
2716
2141
  const _render_mail_payment_line_item = ({ name, desc, unit_cost, quantity, total_cost, font_weight = "normal", divider = true, color = style.text_fg, }) => {
2717
2142
  return [
@@ -2766,14 +2191,16 @@ export class Server {
2766
2191
  let subtotal = 0;
2767
2192
  let subtotal_tax = 0;
2768
2193
  let total = 0;
2769
- payment.line_items.iterate((item) => {
2194
+ payment.line_items.walk((item) => {
2195
+ if (!this.payments)
2196
+ throw new Error("Payments not initialized");
2770
2197
  if (typeof item.product === "string") {
2771
2198
  item.product = this.payments.get_product_sync(item.product);
2772
2199
  }
2773
2200
  if (currency == null) {
2774
2201
  const c = Utils.get_currency_symbol(item.product.currency);
2775
2202
  if (c == null) {
2776
- error(`Failed to create a payment mail: `, new Error(`Unable to determine the currency of payment "${payment.id}".`));
2203
+ this.log.error(`Failed to create a payment mail: `, new Error(`Unable to determine the currency of payment "${payment.id}".`));
2777
2204
  }
2778
2205
  currency = c ?? "?";
2779
2206
  }
@@ -2824,10 +2251,18 @@ export class Server {
2824
2251
  })),
2825
2252
  ];
2826
2253
  }
2254
+ /** Assert mail is configured. */
2255
+ assert_mail() {
2256
+ if (!this.mail) {
2257
+ throw new ExternalError({ message: "Mail is not configured." });
2258
+ }
2259
+ }
2827
2260
  // On 2fa mail.
2261
+ /** Build the 2FA verification email content. */
2828
2262
  on_2fa_mail({ code, username, email, date, ip, device }) {
2829
- const style = this.mail_style;
2830
- const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2263
+ this.assert_mail();
2264
+ const style = this.mail.style;
2265
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = MailUI;
2831
2266
  return this._mail_template({
2832
2267
  max_width: 400,
2833
2268
  children: [
@@ -2885,10 +2320,12 @@ export class Server {
2885
2320
  });
2886
2321
  }
2887
2322
  // On successfull payment mail.
2323
+ /** Build the successful payment email content. */
2888
2324
  on_payment_mail({ payment }) {
2325
+ this.assert_mail();
2889
2326
  // Shortcuts.
2890
- const style = this.mail_style;
2891
- const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2327
+ const style = this.mail.style;
2328
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = MailUI;
2892
2329
  // Create mail.
2893
2330
  return this._mail_template({
2894
2331
  max_width: 600,
@@ -2913,7 +2350,7 @@ export class Server {
2913
2350
  .color(style.subtitle_fg)
2914
2351
  .font_size(18)
2915
2352
  .margin(0)),
2916
- TableRow(Text("A summary of your order can be found below or in the attachmed invoice pdf.")
2353
+ TableRow(Text("A summary of your order can be found below or in the attached invoice PDF.")
2917
2354
  .margin(5, 0, 20, 0)
2918
2355
  .color(style.text_fg)
2919
2356
  .font_size(16)),
@@ -2926,10 +2363,12 @@ export class Server {
2926
2363
  });
2927
2364
  }
2928
2365
  // On failed payment mail.
2366
+ /** Build the failed payment email content. */
2929
2367
  on_failed_payment_mail({ payment }) {
2368
+ this.assert_mail();
2930
2369
  // Shortcuts.
2931
- const style = this.mail_style;
2932
- const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
2370
+ const style = this.mail.style;
2371
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = MailUI;
2933
2372
  // Create mail.
2934
2373
  return this._mail_template({
2935
2374
  max_width: 800,
@@ -2940,7 +2379,7 @@ export class Server {
2940
2379
  .width("fit-content")
2941
2380
  .font_size(26)).center(),
2942
2381
  // Text.
2943
- 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.")
2382
+ TableRow(Text("We regret to inform you that your payment could not be processed successfully. We understand the inconvenience this may cause. Please try again, or contact customer support if the problem persists.")
2944
2383
  .margin(10, 0, 20, 0)
2945
2384
  .color(style.text_fg)
2946
2385
  .font_size(16)
@@ -2968,16 +2407,18 @@ export class Server {
2968
2407
  });
2969
2408
  }
2970
2409
  // On cancellation mail.
2410
+ /** Build the successful cancellation email content. */
2971
2411
  on_cancellation_mail({ payment, line_items }) {
2412
+ this.assert_mail();
2972
2413
  // Shortcuts.
2973
- const style = this.mail_style;
2974
- const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2414
+ const style = this.mail.style;
2415
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = MailUI;
2975
2416
  // Create mail.
2976
2417
  return this._mail_template({
2977
2418
  max_width: 800,
2978
2419
  children: [
2979
2420
  // Title.
2980
- TableRow(Title("Successfull Cancellation")
2421
+ TableRow(Title("Successful Cancellation")
2981
2422
  .color(style.title_fg)
2982
2423
  .width("fit-content")
2983
2424
  .font_size(26)).center(),
@@ -3009,10 +2450,12 @@ export class Server {
3009
2450
  });
3010
2451
  }
3011
2452
  // On refund mail.
2453
+ /** Build the failed cancellation email content. */
3012
2454
  on_failed_cancellation_mail({ payment }) {
2455
+ this.assert_mail();
3013
2456
  // Shortcuts.
3014
- const style = this.mail_style;
3015
- const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
2457
+ const style = this.mail.style;
2458
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = MailUI;
3016
2459
  // Create mail.
3017
2460
  return this._mail_template({
3018
2461
  max_width: 800,
@@ -3051,16 +2494,18 @@ export class Server {
3051
2494
  });
3052
2495
  }
3053
2496
  // On refund mail.
2497
+ /** Build the successful refund email content. */
3054
2498
  on_refund_mail({ payment, line_items }) {
2499
+ this.assert_mail();
3055
2500
  // Shortcuts.
3056
- const style = this.mail_style;
3057
- const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2501
+ const style = this.mail.style;
2502
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = MailUI;
3058
2503
  // Create mail.
3059
2504
  return this._mail_template({
3060
2505
  max_width: 800,
3061
2506
  children: [
3062
2507
  // Title.
3063
- TableRow(Title("Successful Refund")
2508
+ TableRow(Title("Chargeback Successful")
3064
2509
  .color(style.title_fg)
3065
2510
  .width("fit-content")
3066
2511
  .font_size(26)).center(),
@@ -3092,10 +2537,12 @@ export class Server {
3092
2537
  });
3093
2538
  }
3094
2539
  // On refund mail.
2540
+ /** Build the failed refund email content. */
3095
2541
  on_failed_refund_mail({ payment, line_items }) {
2542
+ this.assert_mail();
3096
2543
  // Shortcuts.
3097
- const style = this.mail_style;
3098
- const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
2544
+ const style = this.mail.style;
2545
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = MailUI;
3099
2546
  // Create mail.
3100
2547
  return this._mail_template({
3101
2548
  max_width: 800,
@@ -3134,10 +2581,12 @@ export class Server {
3134
2581
  });
3135
2582
  }
3136
2583
  // On refund mail.
2584
+ /** Build the successful chargeback email content. */
3137
2585
  on_chargeback_mail({ payment, line_items }) {
2586
+ this.assert_mail();
3138
2587
  // Shortcuts.
3139
- const style = this.mail_style;
3140
- const { Title, Text, Image, Table, TableRow, TableData, VStack } = Mail;
2588
+ const style = this.mail.style;
2589
+ const { Title, Text, Image, Table, TableRow, TableData, VStack } = MailUI;
3141
2590
  // Create mail.
3142
2591
  return this._mail_template({
3143
2592
  max_width: 800,
@@ -3162,7 +2611,7 @@ export class Server {
3162
2611
  .color(style.subtitle_fg)
3163
2612
  .font_size(18)
3164
2613
  .margin(0)),
3165
- TableRow(Text("A summary of your refundend products.")
2614
+ TableRow(Text("A summary of the items charged back.")
3166
2615
  .margin(5, 0, 20, 0)
3167
2616
  .color(style.text_fg)
3168
2617
  .font_size(16)),
@@ -3175,10 +2624,12 @@ export class Server {
3175
2624
  });
3176
2625
  }
3177
2626
  // On refund mail.
2627
+ /** Build the failed chargeback email content. */
3178
2628
  on_failed_chargeback_mail({ payment, line_items }) {
2629
+ this.assert_mail();
3179
2630
  // Shortcuts.
3180
- const style = this.mail_style;
3181
- const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = Mail;
2631
+ const style = this.mail.style;
2632
+ const { Title, Text, Image, ImageMask, Table, TableRow, TableData, VStack } = MailUI;
3182
2633
  // Create mail.
3183
2634
  return this._mail_template({
3184
2635
  max_width: 800,
@@ -3217,4 +2668,3 @@ export class Server {
3217
2668
  });
3218
2669
  }
3219
2670
  }
3220
- export default Server;