@stamhoofd/models 2.1.1

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 (481) hide show
  1. package/dist/src/assets/Metropolis-Black.woff2 +0 -0
  2. package/dist/src/assets/Metropolis-BlackItalic.woff2 +0 -0
  3. package/dist/src/assets/Metropolis-Bold.woff2 +0 -0
  4. package/dist/src/assets/Metropolis-BoldItalic.woff2 +0 -0
  5. package/dist/src/assets/Metropolis-ExtraBold.woff2 +0 -0
  6. package/dist/src/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
  7. package/dist/src/assets/Metropolis-ExtraLight.woff2 +0 -0
  8. package/dist/src/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
  9. package/dist/src/assets/Metropolis-Light.woff2 +0 -0
  10. package/dist/src/assets/Metropolis-LightItalic.woff2 +0 -0
  11. package/dist/src/assets/Metropolis-Medium.woff2 +0 -0
  12. package/dist/src/assets/Metropolis-MediumItalic.woff2 +0 -0
  13. package/dist/src/assets/Metropolis-Regular.woff2 +0 -0
  14. package/dist/src/assets/Metropolis-RegularItalic.woff2 +0 -0
  15. package/dist/src/assets/Metropolis-SemiBold.woff2 +0 -0
  16. package/dist/src/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
  17. package/dist/src/assets/Metropolis-Thin.woff2 +0 -0
  18. package/dist/src/assets/Metropolis-ThinItalic.woff2 +0 -0
  19. package/dist/src/assets/assets/Metropolis-Black.woff2 +0 -0
  20. package/dist/src/assets/assets/Metropolis-BlackItalic.woff2 +0 -0
  21. package/dist/src/assets/assets/Metropolis-Bold.woff2 +0 -0
  22. package/dist/src/assets/assets/Metropolis-BoldItalic.woff2 +0 -0
  23. package/dist/src/assets/assets/Metropolis-ExtraBold.woff2 +0 -0
  24. package/dist/src/assets/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
  25. package/dist/src/assets/assets/Metropolis-ExtraLight.woff2 +0 -0
  26. package/dist/src/assets/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
  27. package/dist/src/assets/assets/Metropolis-Light.woff2 +0 -0
  28. package/dist/src/assets/assets/Metropolis-LightItalic.woff2 +0 -0
  29. package/dist/src/assets/assets/Metropolis-Medium.woff2 +0 -0
  30. package/dist/src/assets/assets/Metropolis-MediumItalic.woff2 +0 -0
  31. package/dist/src/assets/assets/Metropolis-Regular.woff2 +0 -0
  32. package/dist/src/assets/assets/Metropolis-RegularItalic.woff2 +0 -0
  33. package/dist/src/assets/assets/Metropolis-SemiBold.woff2 +0 -0
  34. package/dist/src/assets/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
  35. package/dist/src/assets/assets/Metropolis-Thin.woff2 +0 -0
  36. package/dist/src/assets/assets/Metropolis-ThinItalic.woff2 +0 -0
  37. package/dist/src/assets/assets/logo.png +0 -0
  38. package/dist/src/assets/logo.png +0 -0
  39. package/dist/src/factories/AddressFactory.d.ts +10 -0
  40. package/dist/src/factories/AddressFactory.d.ts.map +1 -0
  41. package/dist/src/factories/AddressFactory.js +42 -0
  42. package/dist/src/factories/AddressFactory.js.map +1 -0
  43. package/dist/src/factories/EmergencyContactFactory.d.ts +9 -0
  44. package/dist/src/factories/EmergencyContactFactory.d.ts.map +1 -0
  45. package/dist/src/factories/EmergencyContactFactory.js +42 -0
  46. package/dist/src/factories/EmergencyContactFactory.js.map +1 -0
  47. package/dist/src/factories/GroupFactory.d.ts +19 -0
  48. package/dist/src/factories/GroupFactory.d.ts.map +1 -0
  49. package/dist/src/factories/GroupFactory.js +52 -0
  50. package/dist/src/factories/GroupFactory.js.map +1 -0
  51. package/dist/src/factories/MemberFactory.d.ts +16 -0
  52. package/dist/src/factories/MemberFactory.d.ts.map +1 -0
  53. package/dist/src/factories/MemberFactory.js +98 -0
  54. package/dist/src/factories/MemberFactory.js.map +1 -0
  55. package/dist/src/factories/OrganizationFactory.d.ts +16 -0
  56. package/dist/src/factories/OrganizationFactory.d.ts.map +1 -0
  57. package/dist/src/factories/OrganizationFactory.js +40 -0
  58. package/dist/src/factories/OrganizationFactory.js.map +1 -0
  59. package/dist/src/factories/ParentFactory.d.ts +10 -0
  60. package/dist/src/factories/ParentFactory.d.ts.map +1 -0
  61. package/dist/src/factories/ParentFactory.js +44 -0
  62. package/dist/src/factories/ParentFactory.js.map +1 -0
  63. package/dist/src/factories/RecordFactory.d.ts +10 -0
  64. package/dist/src/factories/RecordFactory.d.ts.map +1 -0
  65. package/dist/src/factories/RecordFactory.js +14 -0
  66. package/dist/src/factories/RecordFactory.js.map +1 -0
  67. package/dist/src/factories/RegisterCodeFactory.d.ts +18 -0
  68. package/dist/src/factories/RegisterCodeFactory.d.ts.map +1 -0
  69. package/dist/src/factories/RegisterCodeFactory.js +19 -0
  70. package/dist/src/factories/RegisterCodeFactory.js.map +1 -0
  71. package/dist/src/factories/RegistrationFactory.d.ts +14 -0
  72. package/dist/src/factories/RegistrationFactory.d.ts.map +1 -0
  73. package/dist/src/factories/RegistrationFactory.js +26 -0
  74. package/dist/src/factories/RegistrationFactory.js.map +1 -0
  75. package/dist/src/factories/UserFactory.d.ts +21 -0
  76. package/dist/src/factories/UserFactory.d.ts.map +1 -0
  77. package/dist/src/factories/UserFactory.js +41 -0
  78. package/dist/src/factories/UserFactory.js.map +1 -0
  79. package/dist/src/factories/WebshopFactory.d.ts +16 -0
  80. package/dist/src/factories/WebshopFactory.d.ts.map +1 -0
  81. package/dist/src/factories/WebshopFactory.js +35 -0
  82. package/dist/src/factories/WebshopFactory.js.map +1 -0
  83. package/dist/src/helpers/DNSValidator.d.ts +6 -0
  84. package/dist/src/helpers/DNSValidator.d.ts.map +1 -0
  85. package/dist/src/helpers/DNSValidator.js +144 -0
  86. package/dist/src/helpers/DNSValidator.js.map +1 -0
  87. package/dist/src/helpers/EmailBuilder.d.ts +22 -0
  88. package/dist/src/helpers/EmailBuilder.d.ts.map +1 -0
  89. package/dist/src/helpers/EmailBuilder.js +100 -0
  90. package/dist/src/helpers/EmailBuilder.js.map +1 -0
  91. package/dist/src/helpers/GroupBuilder.d.ts +9 -0
  92. package/dist/src/helpers/GroupBuilder.d.ts.map +1 -0
  93. package/dist/src/helpers/GroupBuilder.js +382 -0
  94. package/dist/src/helpers/GroupBuilder.js.map +1 -0
  95. package/dist/src/helpers/Handlebars.d.ts +2 -0
  96. package/dist/src/helpers/Handlebars.d.ts.map +1 -0
  97. package/dist/src/helpers/Handlebars.js +192 -0
  98. package/dist/src/helpers/Handlebars.js.map +1 -0
  99. package/dist/src/helpers/InvoiceBuilder.d.ts +29 -0
  100. package/dist/src/helpers/InvoiceBuilder.d.ts.map +1 -0
  101. package/dist/src/helpers/InvoiceBuilder.js +406 -0
  102. package/dist/src/helpers/InvoiceBuilder.js.map +1 -0
  103. package/dist/src/helpers/InvoiceBuilder.test.d.ts +2 -0
  104. package/dist/src/helpers/InvoiceBuilder.test.d.ts.map +1 -0
  105. package/dist/src/helpers/InvoiceBuilder.test.js +52 -0
  106. package/dist/src/helpers/InvoiceBuilder.test.js.map +1 -0
  107. package/dist/src/helpers/RateLimiter.d.ts +27 -0
  108. package/dist/src/helpers/RateLimiter.d.ts.map +1 -0
  109. package/dist/src/helpers/RateLimiter.js +57 -0
  110. package/dist/src/helpers/RateLimiter.js.map +1 -0
  111. package/dist/src/helpers/WebshopCounter.d.ts +6 -0
  112. package/dist/src/helpers/WebshopCounter.d.ts.map +1 -0
  113. package/dist/src/helpers/WebshopCounter.js +36 -0
  114. package/dist/src/helpers/WebshopCounter.js.map +1 -0
  115. package/dist/src/helpers/WebshopCounter.test.d.ts +2 -0
  116. package/dist/src/helpers/WebshopCounter.test.d.ts.map +1 -0
  117. package/dist/src/helpers/WebshopCounter.test.js +17 -0
  118. package/dist/src/helpers/WebshopCounter.test.js.map +1 -0
  119. package/dist/src/index.d.ts +18 -0
  120. package/dist/src/index.d.ts.map +1 -0
  121. package/dist/src/index.js +23 -0
  122. package/dist/src/index.js.map +1 -0
  123. package/dist/src/migrations/1593773929-create-initial-tables.sql +634 -0
  124. package/dist/src/migrations/1605261999-gemeenten-tmp.sql +2820 -0
  125. package/dist/src/migrations/1605262045-import-postcodes.d.ts +16 -0
  126. package/dist/src/migrations/1605262045-import-postcodes.d.ts.map +1 -0
  127. package/dist/src/migrations/1605262045-import-postcodes.js +116 -0
  128. package/dist/src/migrations/1605262045-import-postcodes.js.map +1 -0
  129. package/dist/src/migrations/1605262046-import-postcodes-nl.d.ts +4 -0
  130. package/dist/src/migrations/1605262046-import-postcodes-nl.d.ts.map +1 -0
  131. package/dist/src/migrations/1605262046-import-postcodes-nl.js +83 -0
  132. package/dist/src/migrations/1605262046-import-postcodes-nl.js.map +1 -0
  133. package/dist/src/migrations/1605279038-drop-gemeenten-tmp.sql +1 -0
  134. package/dist/src/migrations/1648392491-default-templates.sql +9 -0
  135. package/dist/src/migrations/1651245707-default-templates-reminders.sql +6 -0
  136. package/dist/src/migrations/1708607340-tickets-deleted-at.sql +1 -0
  137. package/dist/src/migrations/1710459176-register-code-invoices.sql +3 -0
  138. package/dist/src/migrations/1712158247-discount-codes.sql +17 -0
  139. package/dist/src/migrations/1713178665-drop-invites.sql +1 -0
  140. package/dist/src/migrations/1713178666-drop-keychain.sql +1 -0
  141. package/dist/src/migrations/1713178667-drop-challenges.sql +1 -0
  142. package/dist/src/migrations/1713178668-drop-user-keys.sql +7 -0
  143. package/dist/src/migrations/1713178669-drop-organization-key.sql +2 -0
  144. package/dist/src/migrations/data/postcodes/nl/Drenthe +291 -0
  145. package/dist/src/migrations/data/postcodes/nl/Flevoland +107 -0
  146. package/dist/src/migrations/data/postcodes/nl/Friesland +518 -0
  147. package/dist/src/migrations/data/postcodes/nl/Gelderland +601 -0
  148. package/dist/src/migrations/data/postcodes/nl/Groningen +279 -0
  149. package/dist/src/migrations/data/postcodes/nl/Limburg +324 -0
  150. package/dist/src/migrations/data/postcodes/nl/Noord-Brabant +620 -0
  151. package/dist/src/migrations/data/postcodes/nl/Noord-Holland +566 -0
  152. package/dist/src/migrations/data/postcodes/nl/Overrijsel +344 -0
  153. package/dist/src/migrations/data/postcodes/nl/Utrecht +278 -0
  154. package/dist/src/migrations/data/postcodes/nl/Zeeland +179 -0
  155. package/dist/src/migrations/data/postcodes/nl/Zuid-Holland +662 -0
  156. package/dist/src/models/BalanceItem.d.ts +57 -0
  157. package/dist/src/models/BalanceItem.d.ts.map +1 -0
  158. package/dist/src/models/BalanceItem.js +346 -0
  159. package/dist/src/models/BalanceItem.js.map +1 -0
  160. package/dist/src/models/BalanceItemPayment.d.ts +31 -0
  161. package/dist/src/models/BalanceItemPayment.d.ts.map +1 -0
  162. package/dist/src/models/BalanceItemPayment.js +101 -0
  163. package/dist/src/models/BalanceItemPayment.js.map +1 -0
  164. package/dist/src/models/BuckarooPayment.d.ts +8 -0
  165. package/dist/src/models/BuckarooPayment.d.ts.map +1 -0
  166. package/dist/src/models/BuckarooPayment.js +24 -0
  167. package/dist/src/models/BuckarooPayment.js.map +1 -0
  168. package/dist/src/models/Document.d.ts +44 -0
  169. package/dist/src/models/Document.d.ts.map +1 -0
  170. package/dist/src/models/Document.js +194 -0
  171. package/dist/src/models/Document.js.map +1 -0
  172. package/dist/src/models/DocumentTemplate.d.ts +45 -0
  173. package/dist/src/models/DocumentTemplate.d.ts.map +1 -0
  174. package/dist/src/models/DocumentTemplate.js +533 -0
  175. package/dist/src/models/DocumentTemplate.js.map +1 -0
  176. package/dist/src/models/EmailTemplate.d.ts +22 -0
  177. package/dist/src/models/EmailTemplate.d.ts.map +1 -0
  178. package/dist/src/models/EmailTemplate.js +70 -0
  179. package/dist/src/models/EmailTemplate.js.map +1 -0
  180. package/dist/src/models/EmailVerificationCode.d.ts +60 -0
  181. package/dist/src/models/EmailVerificationCode.d.ts.map +1 -0
  182. package/dist/src/models/EmailVerificationCode.js +307 -0
  183. package/dist/src/models/EmailVerificationCode.js.map +1 -0
  184. package/dist/src/models/Group.d.ts +36 -0
  185. package/dist/src/models/Group.d.ts.map +1 -0
  186. package/dist/src/models/Group.js +231 -0
  187. package/dist/src/models/Group.js.map +1 -0
  188. package/dist/src/models/Image.d.ts +12 -0
  189. package/dist/src/models/Image.d.ts.map +1 -0
  190. package/dist/src/models/Image.js +137 -0
  191. package/dist/src/models/Image.js.map +1 -0
  192. package/dist/src/models/Member.d.ts +66 -0
  193. package/dist/src/models/Member.d.ts.map +1 -0
  194. package/dist/src/models/Member.js +309 -0
  195. package/dist/src/models/Member.js.map +1 -0
  196. package/dist/src/models/MemberResponsibilityRecord.d.ts +11 -0
  197. package/dist/src/models/MemberResponsibilityRecord.d.ts.map +1 -0
  198. package/dist/src/models/MemberResponsibilityRecord.js +47 -0
  199. package/dist/src/models/MemberResponsibilityRecord.js.map +1 -0
  200. package/dist/src/models/MolliePayment.d.ts +8 -0
  201. package/dist/src/models/MolliePayment.d.ts.map +1 -0
  202. package/dist/src/models/MolliePayment.js +24 -0
  203. package/dist/src/models/MolliePayment.js.map +1 -0
  204. package/dist/src/models/MollieToken.d.ts +45 -0
  205. package/dist/src/models/MollieToken.d.ts.map +1 -0
  206. package/dist/src/models/MollieToken.js +333 -0
  207. package/dist/src/models/MollieToken.js.map +1 -0
  208. package/dist/src/models/OneTimeToken.d.ts +38 -0
  209. package/dist/src/models/OneTimeToken.d.ts.map +1 -0
  210. package/dist/src/models/OneTimeToken.js +126 -0
  211. package/dist/src/models/OneTimeToken.js.map +1 -0
  212. package/dist/src/models/Order.d.ts +99 -0
  213. package/dist/src/models/Order.d.ts.map +1 -0
  214. package/dist/src/models/Order.js +912 -0
  215. package/dist/src/models/Order.js.map +1 -0
  216. package/dist/src/models/Organization.d.ts +119 -0
  217. package/dist/src/models/Organization.d.ts.map +1 -0
  218. package/dist/src/models/Organization.js +900 -0
  219. package/dist/src/models/Organization.js.map +1 -0
  220. package/dist/src/models/OrganizationRegistrationPeriod.d.ts +14 -0
  221. package/dist/src/models/OrganizationRegistrationPeriod.d.ts.map +1 -0
  222. package/dist/src/models/OrganizationRegistrationPeriod.js +62 -0
  223. package/dist/src/models/OrganizationRegistrationPeriod.js.map +1 -0
  224. package/dist/src/models/PasswordToken.d.ts +29 -0
  225. package/dist/src/models/PasswordToken.d.ts.map +1 -0
  226. package/dist/src/models/PasswordToken.js +118 -0
  227. package/dist/src/models/PasswordToken.js.map +1 -0
  228. package/dist/src/models/PayconiqPayment.d.ts +18 -0
  229. package/dist/src/models/PayconiqPayment.d.ts.map +1 -0
  230. package/dist/src/models/PayconiqPayment.js +216 -0
  231. package/dist/src/models/PayconiqPayment.js.map +1 -0
  232. package/dist/src/models/Payment.d.ts +62 -0
  233. package/dist/src/models/Payment.d.ts.map +1 -0
  234. package/dist/src/models/Payment.js +199 -0
  235. package/dist/src/models/Payment.js.map +1 -0
  236. package/dist/src/models/Platform.d.ts +15 -0
  237. package/dist/src/models/Platform.d.ts.map +1 -0
  238. package/dist/src/models/Platform.js +77 -0
  239. package/dist/src/models/Platform.js.map +1 -0
  240. package/dist/src/models/RegisterCode.d.ts +27 -0
  241. package/dist/src/models/RegisterCode.d.ts.map +1 -0
  242. package/dist/src/models/RegisterCode.js +162 -0
  243. package/dist/src/models/RegisterCode.js.map +1 -0
  244. package/dist/src/models/Registration.d.ts +47 -0
  245. package/dist/src/models/Registration.d.ts.map +1 -0
  246. package/dist/src/models/Registration.js +369 -0
  247. package/dist/src/models/Registration.js.map +1 -0
  248. package/dist/src/models/RegistrationPeriod.d.ts +15 -0
  249. package/dist/src/models/RegistrationPeriod.d.ts.map +1 -0
  250. package/dist/src/models/RegistrationPeriod.js +64 -0
  251. package/dist/src/models/RegistrationPeriod.js.map +1 -0
  252. package/dist/src/models/STCredit.d.ts +20 -0
  253. package/dist/src/models/STCredit.d.ts.map +1 -0
  254. package/dist/src/models/STCredit.js +129 -0
  255. package/dist/src/models/STCredit.js.map +1 -0
  256. package/dist/src/models/STInvoice.d.ts +51 -0
  257. package/dist/src/models/STInvoice.d.ts.map +1 -0
  258. package/dist/src/models/STInvoice.js +453 -0
  259. package/dist/src/models/STInvoice.js.map +1 -0
  260. package/dist/src/models/STPackage.d.ts +36 -0
  261. package/dist/src/models/STPackage.d.ts.map +1 -0
  262. package/dist/src/models/STPackage.js +300 -0
  263. package/dist/src/models/STPackage.js.map +1 -0
  264. package/dist/src/models/STPendingInvoice.d.ts +45 -0
  265. package/dist/src/models/STPendingInvoice.d.ts.map +1 -0
  266. package/dist/src/models/STPendingInvoice.js +284 -0
  267. package/dist/src/models/STPendingInvoice.js.map +1 -0
  268. package/dist/src/models/StripeAccount.d.ts +17 -0
  269. package/dist/src/models/StripeAccount.d.ts.map +1 -0
  270. package/dist/src/models/StripeAccount.js +81 -0
  271. package/dist/src/models/StripeAccount.js.map +1 -0
  272. package/dist/src/models/StripeCheckoutSession.d.ts +9 -0
  273. package/dist/src/models/StripeCheckoutSession.d.ts.map +1 -0
  274. package/dist/src/models/StripeCheckoutSession.js +31 -0
  275. package/dist/src/models/StripeCheckoutSession.js.map +1 -0
  276. package/dist/src/models/StripePaymentIntent.d.ts +9 -0
  277. package/dist/src/models/StripePaymentIntent.d.ts.map +1 -0
  278. package/dist/src/models/StripePaymentIntent.js +31 -0
  279. package/dist/src/models/StripePaymentIntent.js.map +1 -0
  280. package/dist/src/models/Ticket.d.ts +61 -0
  281. package/dist/src/models/Ticket.d.ts.map +1 -0
  282. package/dist/src/models/Ticket.js +143 -0
  283. package/dist/src/models/Ticket.js.map +1 -0
  284. package/dist/src/models/Token.d.ts +49 -0
  285. package/dist/src/models/Token.d.ts.map +1 -0
  286. package/dist/src/models/Token.js +218 -0
  287. package/dist/src/models/Token.js.map +1 -0
  288. package/dist/src/models/Token.test.d.ts +2 -0
  289. package/dist/src/models/Token.test.d.ts.map +1 -0
  290. package/dist/src/models/Token.test.js +60 -0
  291. package/dist/src/models/Token.test.js.map +1 -0
  292. package/dist/src/models/UsedRegisterCode.d.ts +22 -0
  293. package/dist/src/models/UsedRegisterCode.d.ts.map +1 -0
  294. package/dist/src/models/UsedRegisterCode.js +158 -0
  295. package/dist/src/models/UsedRegisterCode.js.map +1 -0
  296. package/dist/src/models/User.d.ts +55 -0
  297. package/dist/src/models/User.d.ts.map +1 -0
  298. package/dist/src/models/User.js +314 -0
  299. package/dist/src/models/User.js.map +1 -0
  300. package/dist/src/models/UserPermissions.d.ts +15 -0
  301. package/dist/src/models/UserPermissions.d.ts.map +1 -0
  302. package/dist/src/models/UserPermissions.js +57 -0
  303. package/dist/src/models/UserPermissions.js.map +1 -0
  304. package/dist/src/models/Webshop.d.ts +44 -0
  305. package/dist/src/models/Webshop.d.ts.map +1 -0
  306. package/dist/src/models/Webshop.js +184 -0
  307. package/dist/src/models/Webshop.js.map +1 -0
  308. package/dist/src/models/WebshopDiscountCode.d.ts +18 -0
  309. package/dist/src/models/WebshopDiscountCode.d.ts.map +1 -0
  310. package/dist/src/models/WebshopDiscountCode.js +88 -0
  311. package/dist/src/models/WebshopDiscountCode.js.map +1 -0
  312. package/dist/src/models/addresses/City.d.ts +14 -0
  313. package/dist/src/models/addresses/City.d.ts.map +1 -0
  314. package/dist/src/models/addresses/City.js +37 -0
  315. package/dist/src/models/addresses/City.js.map +1 -0
  316. package/dist/src/models/addresses/PostalCode.d.ts +20 -0
  317. package/dist/src/models/addresses/PostalCode.d.ts.map +1 -0
  318. package/dist/src/models/addresses/PostalCode.js +138 -0
  319. package/dist/src/models/addresses/PostalCode.js.map +1 -0
  320. package/dist/src/models/addresses/PostalCode.test.d.ts +2 -0
  321. package/dist/src/models/addresses/PostalCode.test.d.ts.map +1 -0
  322. package/dist/src/models/addresses/PostalCode.test.js +98 -0
  323. package/dist/src/models/addresses/PostalCode.test.js.map +1 -0
  324. package/dist/src/models/addresses/Province.d.ts +9 -0
  325. package/dist/src/models/addresses/Province.d.ts.map +1 -0
  326. package/dist/src/models/addresses/Province.js +24 -0
  327. package/dist/src/models/addresses/Province.js.map +1 -0
  328. package/dist/src/models/addresses/Street.d.ts +10 -0
  329. package/dist/src/models/addresses/Street.d.ts.map +1 -0
  330. package/dist/src/models/addresses/Street.js +26 -0
  331. package/dist/src/models/addresses/Street.js.map +1 -0
  332. package/dist/src/models/index.d.ts +37 -0
  333. package/dist/src/models/index.d.ts.map +1 -0
  334. package/dist/src/models/index.js +53 -0
  335. package/dist/src/models/index.js.map +1 -0
  336. package/dist/src/structures/OrganizationServerMetaData.d.ts +43 -0
  337. package/dist/src/structures/OrganizationServerMetaData.d.ts.map +1 -0
  338. package/dist/src/structures/OrganizationServerMetaData.js +128 -0
  339. package/dist/src/structures/OrganizationServerMetaData.js.map +1 -0
  340. package/dist/tests/jest.global.setup.d.ts +3 -0
  341. package/dist/tests/jest.global.setup.d.ts.map +1 -0
  342. package/dist/tests/jest.global.setup.js +20 -0
  343. package/dist/tests/jest.global.setup.js.map +1 -0
  344. package/dist/tests/jest.setup.d.ts +2 -0
  345. package/dist/tests/jest.setup.d.ts.map +1 -0
  346. package/dist/tests/jest.setup.js +15 -0
  347. package/dist/tests/jest.setup.js.map +1 -0
  348. package/package.json +30 -0
  349. package/src/assets/Metropolis-Black.woff2 +0 -0
  350. package/src/assets/Metropolis-BlackItalic.woff2 +0 -0
  351. package/src/assets/Metropolis-Bold.woff2 +0 -0
  352. package/src/assets/Metropolis-BoldItalic.woff2 +0 -0
  353. package/src/assets/Metropolis-ExtraBold.woff2 +0 -0
  354. package/src/assets/Metropolis-ExtraBoldItalic.woff2 +0 -0
  355. package/src/assets/Metropolis-ExtraLight.woff2 +0 -0
  356. package/src/assets/Metropolis-ExtraLightItalic.woff2 +0 -0
  357. package/src/assets/Metropolis-Light.woff2 +0 -0
  358. package/src/assets/Metropolis-LightItalic.woff2 +0 -0
  359. package/src/assets/Metropolis-Medium.woff2 +0 -0
  360. package/src/assets/Metropolis-MediumItalic.woff2 +0 -0
  361. package/src/assets/Metropolis-Regular.woff2 +0 -0
  362. package/src/assets/Metropolis-RegularItalic.woff2 +0 -0
  363. package/src/assets/Metropolis-SemiBold.woff2 +0 -0
  364. package/src/assets/Metropolis-SemiBoldItalic.woff2 +0 -0
  365. package/src/assets/Metropolis-Thin.woff2 +0 -0
  366. package/src/assets/Metropolis-ThinItalic.woff2 +0 -0
  367. package/src/assets/logo.png +0 -0
  368. package/src/factories/AddressFactory.ts +42 -0
  369. package/src/factories/EmergencyContactFactory.ts +43 -0
  370. package/src/factories/GroupFactory.ts +66 -0
  371. package/src/factories/MemberFactory.ts +122 -0
  372. package/src/factories/OrganizationFactory.ts +45 -0
  373. package/src/factories/ParentFactory.ts +49 -0
  374. package/src/factories/RecordFactory.ts +12 -0
  375. package/src/factories/RegisterCodeFactory.ts +25 -0
  376. package/src/factories/RegistrationFactory.ts +32 -0
  377. package/src/factories/UserFactory.ts +66 -0
  378. package/src/factories/WebshopFactory.ts +43 -0
  379. package/src/helpers/DNSValidator.ts +153 -0
  380. package/src/helpers/EmailBuilder.ts +127 -0
  381. package/src/helpers/GroupBuilder.ts +438 -0
  382. package/src/helpers/Handlebars.ts +203 -0
  383. package/src/helpers/InvoiceBuilder.test.ts +57 -0
  384. package/src/helpers/InvoiceBuilder.ts +501 -0
  385. package/src/helpers/RateLimiter.ts +75 -0
  386. package/src/helpers/WebshopCounter.test.ts +16 -0
  387. package/src/helpers/WebshopCounter.ts +36 -0
  388. package/src/index.ts +21 -0
  389. package/src/migrations/1593773929-create-initial-tables.sql +634 -0
  390. package/src/migrations/1605261999-gemeenten-tmp.sql +2820 -0
  391. package/src/migrations/1605262045-import-postcodes.ts +132 -0
  392. package/src/migrations/1605262046-import-postcodes-nl.ts +97 -0
  393. package/src/migrations/1605279038-drop-gemeenten-tmp.sql +1 -0
  394. package/src/migrations/1648392491-default-templates.sql +9 -0
  395. package/src/migrations/1651245707-default-templates-reminders.sql +6 -0
  396. package/src/migrations/1708607340-tickets-deleted-at.sql +1 -0
  397. package/src/migrations/1710459176-register-code-invoices.sql +3 -0
  398. package/src/migrations/1712158247-discount-codes.sql +17 -0
  399. package/src/migrations/1713178665-drop-invites.sql +1 -0
  400. package/src/migrations/1713178666-drop-keychain.sql +1 -0
  401. package/src/migrations/1713178667-drop-challenges.sql +1 -0
  402. package/src/migrations/1713178668-drop-user-keys.sql +7 -0
  403. package/src/migrations/1713178669-drop-organization-key.sql +2 -0
  404. package/src/migrations/1714985451-user-nullable-organization-id.sql +2 -0
  405. package/src/migrations/1714985452-email-verification-code-nullable-organization-id.sql +2 -0
  406. package/src/migrations/1714985453-user-organization-permissions.sql +2 -0
  407. package/src/migrations/1714985454-user-organization-permissions.sql +2 -0
  408. package/src/migrations/1715079362-platform.sql +6 -0
  409. package/src/migrations/1715181649-registrations-organization-id.sql +2 -0
  410. package/src/migrations/1715181650-registrations-organization-id-fill.sql +1 -0
  411. package/src/migrations/1715181651-registrations-organization-id-drop-null.sql +2 -0
  412. package/src/migrations/1716117067-members-nullable-organization-id.sql +2 -0
  413. package/src/migrations/1719567581-registration-periods.sql +13 -0
  414. package/src/migrations/1719567582-organization-registration-periods.sql +13 -0
  415. package/src/migrations/1719567881-organization-periodId.sql +2 -0
  416. package/src/migrations/1719567882-groups-periodId.sql +2 -0
  417. package/src/migrations/1719567883-platform-periodId.sql +2 -0
  418. package/src/migrations/1719568079-default-period.sql +2 -0
  419. package/src/migrations/1719568080-set-default-period-platform.sql +1 -0
  420. package/src/migrations/1719568081-set-default-period-organizations.sql +1 -0
  421. package/src/migrations/1719568082-set-default-period-groups.sql +1 -0
  422. package/src/migrations/1719580828-registrations-periodId.sql +2 -0
  423. package/src/migrations/1719580829-set-default-period-registrations.sql +1 -0
  424. package/src/migrations/data/postcodes/nl/Drenthe +291 -0
  425. package/src/migrations/data/postcodes/nl/Flevoland +107 -0
  426. package/src/migrations/data/postcodes/nl/Friesland +518 -0
  427. package/src/migrations/data/postcodes/nl/Gelderland +601 -0
  428. package/src/migrations/data/postcodes/nl/Groningen +279 -0
  429. package/src/migrations/data/postcodes/nl/Limburg +324 -0
  430. package/src/migrations/data/postcodes/nl/Noord-Brabant +620 -0
  431. package/src/migrations/data/postcodes/nl/Noord-Holland +566 -0
  432. package/src/migrations/data/postcodes/nl/Overrijsel +344 -0
  433. package/src/migrations/data/postcodes/nl/Utrecht +278 -0
  434. package/src/migrations/data/postcodes/nl/Zeeland +179 -0
  435. package/src/migrations/data/postcodes/nl/Zuid-Holland +662 -0
  436. package/src/models/BalanceItem.ts +392 -0
  437. package/src/models/BalanceItemPayment.ts +106 -0
  438. package/src/models/BuckarooPayment.ts +19 -0
  439. package/src/models/Document.ts +203 -0
  440. package/src/models/DocumentTemplate.ts +583 -0
  441. package/src/models/EmailTemplate.ts +64 -0
  442. package/src/models/EmailVerificationCode.ts +352 -0
  443. package/src/models/Group.ts +293 -0
  444. package/src/models/Image.ts +147 -0
  445. package/src/models/Member.ts +386 -0
  446. package/src/models/MemberResponsibilityRecord.ts +39 -0
  447. package/src/models/MolliePayment.ts +19 -0
  448. package/src/models/MollieToken.ts +369 -0
  449. package/src/models/OneTimeToken.ts +131 -0
  450. package/src/models/Order.ts +1030 -0
  451. package/src/models/Organization.ts +1085 -0
  452. package/src/models/OrganizationRegistrationPeriod.ts +54 -0
  453. package/src/models/PasswordToken.ts +139 -0
  454. package/src/models/PayconiqPayment.ts +241 -0
  455. package/src/models/Payment.ts +216 -0
  456. package/src/models/Platform.ts +76 -0
  457. package/src/models/RegisterCode.ts +164 -0
  458. package/src/models/Registration.ts +405 -0
  459. package/src/models/RegistrationPeriod.ts +55 -0
  460. package/src/models/STCredit.ts +134 -0
  461. package/src/models/STInvoice.ts +507 -0
  462. package/src/models/STPackage.ts +324 -0
  463. package/src/models/STPendingInvoice.ts +308 -0
  464. package/src/models/StripeAccount.ts +71 -0
  465. package/src/models/StripeCheckoutSession.ts +22 -0
  466. package/src/models/StripePaymentIntent.ts +22 -0
  467. package/src/models/Ticket.ts +145 -0
  468. package/src/models/Token.test.ts +69 -0
  469. package/src/models/Token.ts +269 -0
  470. package/src/models/UsedRegisterCode.ts +166 -0
  471. package/src/models/User.ts +445 -0
  472. package/src/models/UserPermissions.ts +54 -0
  473. package/src/models/Webshop.ts +206 -0
  474. package/src/models/WebshopDiscountCode.ts +81 -0
  475. package/src/models/addresses/City.ts +31 -0
  476. package/src/models/addresses/PostalCode.test.ts +117 -0
  477. package/src/models/addresses/PostalCode.ts +164 -0
  478. package/src/models/addresses/Province.ts +20 -0
  479. package/src/models/addresses/Street.ts +25 -0
  480. package/src/models/index.ts +49 -0
  481. package/src/structures/OrganizationServerMetaData.ts +117 -0
@@ -0,0 +1,352 @@
1
+ import { column, Model } from "@simonbackx/simple-database";
2
+ import { SimpleError } from "@simonbackx/simple-errors";
3
+ import { I18n } from "@stamhoofd/backend-i18n";
4
+ import { Email } from "@stamhoofd/email";
5
+ import basex from "base-x";
6
+ import crypto from "crypto";
7
+ import { v4 as uuidv4 } from "uuid";
8
+
9
+ const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
10
+ const bs58 = basex(ALPHABET)
11
+
12
+ async function randomBytes(size: number): Promise<Buffer> {
13
+ return new Promise((resolve, reject) => {
14
+ crypto.randomBytes(size, (err: Error | null, buf: Buffer) => {
15
+ if (err) {
16
+ reject(err);
17
+ return;
18
+ }
19
+ resolve(buf);
20
+ });
21
+ });
22
+ }
23
+
24
+ async function randomInt(max: number): Promise<number> {
25
+ return new Promise((resolve, reject) => {
26
+ crypto.randomInt(max, (err: Error | null, n: number) => {
27
+ if (err) {
28
+ reject(err);
29
+ return;
30
+ }
31
+ resolve(n)
32
+ })
33
+ })
34
+ }
35
+
36
+
37
+ /**
38
+ * Holds the verificationCodes for a given email (not a user, since a user can switch email addresses and might avoid verification that way)
39
+ */
40
+ export class EmailVerificationCode extends Model {
41
+ static table = "email_verification_codes";
42
+
43
+ @column({
44
+ primary: true, type: "string", beforeSave(value) {
45
+ return value ?? uuidv4();
46
+ }
47
+ })
48
+ id!: string;
49
+
50
+ @column({ type: "string", nullable: true })
51
+ organizationId: string|null = null;
52
+
53
+ @column({ type: "string" })
54
+ userId: string;
55
+
56
+ /**
57
+ * The e-mail that will get verified. If on verification, the user e-mail differs from this one,
58
+ * we'll set the user email to this email address
59
+ */
60
+ @column({ type: "string" })
61
+ email: string;
62
+
63
+ /**
64
+ * This one is send to the sender. Allows for polling + extra length + sender authentication
65
+ */
66
+ @column({ type: "string" })
67
+ token = "";
68
+
69
+ @column({ type: "string" })
70
+ code = "";
71
+
72
+ /**
73
+ * The amount of times this code has been generated for the same e-mail address
74
+ */
75
+ @column({ type: "integer" })
76
+ generatedCount = 0;
77
+
78
+ /**
79
+ * The amount of times this unique code has been tried.
80
+ */
81
+ @column({ type: "integer" })
82
+ tries = 0;
83
+
84
+ @column({ type: "datetime" })
85
+ expiresAt: Date;
86
+
87
+ /**
88
+ * createdAt behaves more like createdAt for verificationCode. Since every save is considered to have a new verificationCode
89
+ */
90
+ @column({
91
+ type: "datetime", beforeSave() {
92
+ const date = new Date()
93
+ date.setMilliseconds(0)
94
+ return date
95
+ }
96
+ })
97
+ createdAt: Date
98
+
99
+ static CODE_LENGTH = 6
100
+ static MAX_TRIES = 9 // minus 0..MAX_TRIES_VARY
101
+
102
+ async generateCode() {
103
+ this.code = ((await randomInt(Math.pow(10, EmailVerificationCode.CODE_LENGTH)))+"").padStart(EmailVerificationCode.CODE_LENGTH, "0")
104
+ this.token = bs58.encode(await randomBytes(100)).toLowerCase();
105
+
106
+ // Increase generatedCount if we changed the code
107
+ if (this.tries > 0) {
108
+ // For statistics (how many did they try and had to resend the email)
109
+ this.generatedCount++;
110
+ }
111
+
112
+ // Reset the real tries
113
+ this.tries = 0
114
+
115
+ // Expire in 12 hours
116
+ this.expiresAt = new Date(new Date().getTime() + 1000 * 60 * 60 * 12)
117
+ }
118
+
119
+ getEmailVerificationUrl(user: import('./User').User, organization: import('./Organization').Organization|null, i18n: I18n) {
120
+ let host: string;
121
+ if (user.permissions || !organization || STAMHOOFD.userMode === 'platform') {
122
+ host = "https://"+(STAMHOOFD.domains.dashboard ?? "stamhoofd.app")+"/"+i18n.locale
123
+ } else {
124
+ // Add language if different than default
125
+ host = "https://"+organization.getHost()
126
+
127
+ if (i18n.language != organization.i18n.language) {
128
+ host += "/"+i18n.language
129
+ }
130
+ }
131
+
132
+ return host+"/verify-email"+(user.organizationPermissions && this.organizationId ? "/"+encodeURIComponent(this.organizationId) : "")+"?code="+encodeURIComponent(this.code)+"&token="+encodeURIComponent(this.token);
133
+ }
134
+
135
+ /**
136
+ * Return true if this token is still valid (used for automatic polling in code view)
137
+ */
138
+ static async poll(organizationId: string|null, token: string): Promise<boolean> {
139
+ const verificationCodes = await this.where({
140
+ token,
141
+ organizationId: {
142
+ sign: 'IN',
143
+ value: [organizationId, null]
144
+ }
145
+ }, { limit: 1 })
146
+
147
+ if (verificationCodes.length == 0) {
148
+ return false // = expired or invalid
149
+ }
150
+
151
+ const verificationCode = verificationCodes[0]
152
+
153
+ if (verificationCode.token !== token) {
154
+ // Safety check, is not possible
155
+ console.error("Security check failed for verify: check MySQL optimization")
156
+ return false;
157
+ }
158
+
159
+ if (verificationCode.expiresAt < new Date()) {
160
+ // Expired.
161
+ // Can't expose this because that would expose a user enumeration attack
162
+ // -> we'll include this expiry date in e-mails
163
+ return false
164
+ }
165
+
166
+ if (verificationCode.tries >= EmailVerificationCode.MAX_TRIES) {
167
+ return false
168
+ }
169
+
170
+ return true
171
+ }
172
+
173
+ /**
174
+ * We don't throw errors here, because we don't want to expose any information about the existance of the code.
175
+ * We just expose if it is valid or not, nothing else
176
+ * False = expired or invalid
177
+ * Error => token okay, but too many attempts checking the code
178
+ *
179
+ */
180
+ static async verify(organizationId: string|null, token: string, code: string): Promise<EmailVerificationCode | undefined> {
181
+ if (code.length != EmailVerificationCode.CODE_LENGTH) {
182
+ return
183
+ }
184
+
185
+ const verificationCodes = await this.where({
186
+ token,
187
+ organizationId: {
188
+ sign: 'IN',
189
+ value: [organizationId, null]
190
+ }
191
+ }, { limit: 1 })
192
+
193
+ if (verificationCodes.length == 0) {
194
+ return // = expired or invalid
195
+ }
196
+
197
+ const verificationCode = verificationCodes[0]
198
+
199
+ if (verificationCode.token !== token) {
200
+ // Safety check, is not possible
201
+ console.error("Security check failed for verify: check MySQL optimization")
202
+ return;
203
+ }
204
+
205
+ if (verificationCode.expiresAt < new Date()) {
206
+ // Expired.
207
+ // Can't expose this because that would expose a user enumeration attack
208
+ // -> we'll include this expiry date in e-mails
209
+ return
210
+ }
211
+
212
+ if (verificationCode.tries >= EmailVerificationCode.MAX_TRIES) {
213
+ // We can saferly inform the user, because he is authenticated with the token
214
+ throw new SimpleError({
215
+ code: "too_many_attempts",
216
+ message: "Too many attempts",
217
+ human: "Je hebt de code te veel foutief ingegeven. Verstuur eerst een nieuwe code voor je opnieuw probeert.",
218
+ statusCode: 429
219
+ })
220
+ }
221
+
222
+ if (verificationCode.code === code || (code === "111111" && STAMHOOFD.environment === "development")) {
223
+ // Delete all remaining information!
224
+ // To avoid leaving information about the existince of this user (tries)
225
+ await verificationCode.delete()
226
+
227
+ return verificationCode
228
+ }
229
+
230
+ verificationCode.tries++
231
+ await verificationCode.save()
232
+
233
+ if (verificationCode.tries >= EmailVerificationCode.MAX_TRIES) {
234
+ // We can saferly inform the user, because he is authenticated with the token
235
+ throw new SimpleError({
236
+ code: "too_many_attempts",
237
+ message: "Too many attempts",
238
+ human: "Je hebt de code te veel foutief ingegeven. Verstuur eerst een nieuwe code voor je opnieuw probeert.",
239
+ statusCode: 429
240
+ })
241
+ }
242
+ }
243
+
244
+ send(user: import('./User').User, organization: import('./Organization').Organization|null, i18n: I18n, withCode = true) {
245
+ const { from, replyTo } = {
246
+ from: (user.organizationPermissions || !organization ? Email.getInternalEmailFor(i18n) : organization.getStrongEmail(i18n)),
247
+ replyTo: undefined // Don't use replyTo because it affects deliverability rates due to spam filters
248
+ }
249
+
250
+ const url = this.getEmailVerificationUrl(user, organization, i18n)
251
+
252
+ const footer = (!user.organizationPermissions && organization ? "\n\n—\n\nOnze ledenadministratie werkt via het Stamhoofd platform, op maat van verenigingen. Probeer het ook via https://"+i18n.$t("shared.domains.marketing")+"/ledenadministratie\n\n" : '')
253
+ const footerHTML = (!user.organizationPermissions && organization ? "<br><br>—<br><br>Onze ledenadministratie werkt via het Stamhoofd platform, op maat van verenigingen. Probeer het ook via <a href=\"https://"+i18n.$t("shared.domains.marketing")+"/ledenadministratie\">Stamhoofd</a><br><br>" : '')
254
+
255
+ const name = organization?.name ?? 'Stamhoofd'
256
+
257
+ if (withCode) {
258
+ const formattedCode = this.code.substr(0, 3)+" "+this.code.substr(3)
259
+ Email.send({
260
+ from,
261
+ replyTo,
262
+ to: this.email,
263
+ subject: `[${user.organizationPermissions ? "Stamhoofd" : name}] Verifieer jouw e-mailadres`,
264
+ type: "transactional",
265
+ text: `Hallo${user.firstName ? (" "+user.firstName) : ""}!\n\nVerifieer jouw e-mailadres om te kunnen inloggen bij ${name}. Vul de code "${formattedCode}" in op de website of klik op de onderstaande link om jouw e-mailadres te bevestigen.\n${url}\n\nDit is een automatische e-mail. Gelieve niet op dit e-mailadres te reageren.\n\n${user.organizationPermissions ? "Stamhoofd" : name}`+footer,
266
+ html: `Hallo${user.firstName ? (" "+user.firstName) : ""}!<br><br>Verifieer jouw e-mailadres om te kunnen inloggen bij ${name}. Vul de onderstaande code in op de website<br><br><strong style="font-size: 30px; font-weight: bold;">${formattedCode}</strong><br><br>Of klik op de onderstaande link om jouw e-mailadres te bevestigen:<br>${url}<br><br>Dit is een automatische e-mail. Gelieve niet op dit e-mailadres te reageren.<br><br>${user.organizationPermissions ? "Stamhoofd" : name}`+footerHTML
267
+ })
268
+ } else {
269
+ Email.send({
270
+ from,
271
+ replyTo,
272
+ to: this.email,
273
+ type: "transactional",
274
+ subject: `[${user.organizationPermissions ? "Stamhoofd" : name}] Verifieer jouw e-mailadres`,
275
+ text: `Hallo${user.firstName ? (" "+user.firstName) : ""}!\n\nVerifieer jouw e-mailadres om te kunnen inloggen bij ${name}. Klik op de onderstaande link om jouw e-mailadres te bevestigen.\n${url}\n\nDit is een automatische e-mail. Gelieve niet op dit e-mailadres te reageren.\n\n${user.organizationPermissions ? "Stamhoofd" : name}`+footer
276
+ })
277
+ }
278
+ }
279
+
280
+ static async resend(organization: import('./Organization').Organization|null, token: string, i18n: I18n) {
281
+ const verificationCodes = await this.where({
282
+ token,
283
+ organizationId: organization ? {
284
+ sign: 'IN',
285
+ value: [organization.id, null]
286
+ } : null
287
+ }, { limit: 1 })
288
+
289
+ if (verificationCodes.length == 0) {
290
+ console.log("Can't resend code, no coded found for token", token)
291
+ // TODO: maybe send a note via email
292
+ return
293
+ }
294
+
295
+ const verificationCode = verificationCodes[0]
296
+
297
+ if (verificationCode.expiresAt < new Date()) {
298
+ // Don't report error, could be brute forced
299
+ console.log("Can't resend code, token is expired", token)
300
+ return
301
+ }
302
+
303
+ const {User} = await import('./User')
304
+ const user = await User.getByID(verificationCode.userId)
305
+ if (!user) {
306
+ return
307
+ }
308
+ verificationCode.send(user, organization, i18n)
309
+ }
310
+
311
+ /**
312
+ * Create or reuse a verification code for a given email address
313
+ * If needed, it will update the code.
314
+ * Use this method for sending only, not for verification!
315
+ */
316
+ static async createFor(user: import('./User').User, email: string): Promise<EmailVerificationCode> {
317
+ // TODO: make this constant time to avoid complex timing attacks (especially when under load)
318
+ // Do we already have a verificationCode for this email?
319
+
320
+ // Only user <-> organization binding is unique
321
+ // Since, when changing email, we don't throw an error if it is use (user enumeration)
322
+ // So multiple users should be able to request changing to a password, but only on validation should they fail
323
+ // (or this should be noted in the verification email and accounts could be merged)
324
+ const verificationCodes = await this.where({ userId: user.id }, { limit: 1 })
325
+
326
+ let verificationCode: EmailVerificationCode
327
+ if (verificationCodes.length == 0) {
328
+ verificationCode = new EmailVerificationCode()
329
+ verificationCode.organizationId = user.organizationId
330
+ await verificationCode.generateCode()
331
+
332
+ // Reset the real tries
333
+ verificationCode.tries = 0
334
+
335
+ // Expire in 3 hours
336
+ verificationCode.expiresAt = new Date(new Date().getTime() + 1000 * 60 * 60 * 3)
337
+ } else {
338
+ verificationCode = verificationCodes[0]
339
+
340
+ if (verificationCode.expiresAt < new Date(new Date().getTime() - 15 * 60 * 1000) || verificationCode.tries >= EmailVerificationCode.MAX_TRIES) {
341
+ // Expired: also update the token
342
+ await verificationCode.generateCode()
343
+ }
344
+ }
345
+
346
+ verificationCode.email = email
347
+ verificationCode.userId = user.id
348
+
349
+ await verificationCode.save()
350
+ return verificationCode
351
+ }
352
+ }
@@ -0,0 +1,293 @@
1
+ import { column, Database, ManyToOneRelation, Model, OneToManyRelation } from '@simonbackx/simple-database';
2
+ import { CycleInformation, Group as GroupStruct, GroupCategory, GroupPrivateSettings, GroupSettings, GroupStatus } from '@stamhoofd/structures';
3
+ import { v4 as uuidv4 } from "uuid";
4
+
5
+ import { Member, MemberWithRegistrations, OrganizationRegistrationPeriod, Payment, Registration, User } from './';
6
+ import { Formatter } from '@stamhoofd/utility';
7
+
8
+ if (Member === undefined) {
9
+ throw new Error("Import Member is undefined")
10
+ }
11
+ if (User === undefined) {
12
+ throw new Error("Import User is undefined")
13
+ }
14
+ if (Payment === undefined) {
15
+ throw new Error("Import Payment is undefined")
16
+ }
17
+ if (Registration === undefined) {
18
+ throw new Error("Import Registration is undefined")
19
+ }
20
+
21
+ export class Group extends Model {
22
+ static table = "groups";
23
+
24
+ @column({
25
+ primary: true, type: "string", beforeSave(value) {
26
+ return value ?? uuidv4();
27
+ }
28
+ })
29
+ id!: string;
30
+
31
+ @column({ type: "json", decoder: GroupSettings })
32
+ settings: GroupSettings;
33
+
34
+ @column({
35
+ type: "json", decoder: GroupPrivateSettings
36
+ })
37
+ privateSettings = GroupPrivateSettings.create({})
38
+
39
+ @column({ type: "string" })
40
+ organizationId: string;
41
+
42
+ @column({ type: "string" })
43
+ periodId: string;
44
+
45
+ /**
46
+ * Every time a new registration period starts, this number increases. This is used to mark all older registrations as 'out of date' automatically
47
+ */
48
+ @column({ type: "integer" })
49
+ cycle = 0;
50
+
51
+ @column({
52
+ type: "datetime", beforeSave(old?: any) {
53
+ if (old !== undefined) {
54
+ return old;
55
+ }
56
+ const date = new Date()
57
+ date.setMilliseconds(0)
58
+ return date
59
+ }
60
+ })
61
+ createdAt: Date
62
+
63
+ @column({
64
+ type: "datetime", beforeSave() {
65
+ const date = new Date()
66
+ date.setMilliseconds(0)
67
+ return date
68
+ },
69
+ skipUpdate: true
70
+ })
71
+ updatedAt: Date
72
+
73
+ @column({
74
+ type: "datetime",
75
+ nullable: true
76
+ })
77
+ deletedAt: Date | null = null
78
+
79
+ /**
80
+ * Every time a new registration period starts, this number increases. This is used to mark all older registrations as 'out of date' automatically
81
+ */
82
+ @column({ type: "string" })
83
+ status = GroupStatus.Open;
84
+
85
+ static async getAll(organizationId: string, periodId: string|null, active = true) {
86
+ const w: any = periodId ? {periodId} : {}
87
+ if (active) {
88
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
89
+ return await Group.where({ organizationId, deletedAt: null, ...w, status: {sign: '!=', value: GroupStatus.Archived} })
90
+ }
91
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
92
+ return await Group.where({ organizationId, ...w })
93
+ }
94
+
95
+ /**
96
+ * Returns all parent and grandparents of this group
97
+ */
98
+ getParentCategories(all: GroupCategory[], recursive = true): GroupCategory[] {
99
+ const map = new Map<string, GroupCategory>()
100
+
101
+ const parents = all.filter(g => g.groupIds.includes(this.id))
102
+ for (const parent of parents) {
103
+ map.set(parent.id, parent)
104
+
105
+ if (recursive) {
106
+ const hisParents = parent.getParentCategories(all)
107
+ for (const pp of hisParents) {
108
+ map.set(pp.id, pp)
109
+ }
110
+ }
111
+ }
112
+
113
+ return [...map.values()]
114
+ }
115
+
116
+ /**
117
+ * Fetch all members with their corresponding (valid) registrations, users
118
+ */
119
+ async getMembersWithRegistration(waitingList = false, cycleOffset = 0): Promise<MemberWithRegistrations[]> {
120
+ let query = `SELECT ${Member.getDefaultSelect()}, ${Registration.getDefaultSelect()}, ${User.getDefaultSelect()} from \`${Member.table}\`\n`;
121
+
122
+ query += `JOIN \`${Registration.table}\` ON \`${Registration.table}\`.\`${Member.registrations.foreignKey}\` = \`${Member.table}\`.\`${Member.primary.name}\` AND (\`${Registration.table}\`.\`registeredAt\` is not null OR \`${Registration.table}\`.\`waitingList\` = 1)\n`
123
+
124
+ if (waitingList) {
125
+ query += `JOIN \`${Registration.table}\` as reg_filter ON reg_filter.\`${Member.registrations.foreignKey}\` = \`${Member.table}\`.\`${Member.primary.name}\` AND reg_filter.\`waitingList\` = 1\n`
126
+ } else {
127
+ query += `JOIN \`${Registration.table}\` as reg_filter ON reg_filter.\`${Member.registrations.foreignKey}\` = \`${Member.table}\`.\`${Member.primary.name}\` AND reg_filter.\`waitingList\` = 0 AND reg_filter.\`registeredAt\` is not null\n`
128
+ }
129
+
130
+ query += Member.users.joinQuery(Member.table, User.table)+"\n"
131
+
132
+ // We do an extra join because we also need to get the other registrations of each member (only one regitration has to match the query)
133
+ query += `where reg_filter.\`groupId\` = ? AND reg_filter.\`cycle\` = ?`
134
+
135
+ const [results] = await Database.select(query, [this.id, this.cycle - cycleOffset])
136
+ const members: MemberWithRegistrations[] = []
137
+
138
+ const groupIds = results.map(r => r[Registration.table]?.groupId).filter(id => id) as string[]
139
+ const groups = await Group.getByIDs(...Formatter.uniqueArray(groupIds))
140
+
141
+ for (const row of results) {
142
+ const foundMember = Member.fromRow(row[Member.table])
143
+ if (!foundMember) {
144
+ throw new Error("Expected member in every row")
145
+ }
146
+ const _f = foundMember.setManyRelation(Member.registrations as unknown as OneToManyRelation<"registrations", Member, Registration & {group: Group}>, []).setManyRelation(Member.users, [])
147
+
148
+ // Seach if we already got this member?
149
+ const existingMember = members.find(m => m.id == _f.id)
150
+
151
+ const member: MemberWithRegistrations = (existingMember ?? _f)
152
+ if (!existingMember) {
153
+ members.push(member)
154
+ }
155
+
156
+ // Check if we have a registration with a payment
157
+ const registration = Registration.fromRow(row[Registration.table])
158
+ if (registration) {
159
+ // Check if we already have this registration
160
+ if (!member.registrations.find(r => r.id == registration.id)) {
161
+ const group = groups.find(g => g.id == registration.groupId)
162
+ if (!group) {
163
+ throw new Error("Expected group")
164
+ }
165
+ member.registrations.push(registration.setRelation(Registration.group, group))
166
+ }
167
+ }
168
+
169
+ // Check if we have a user
170
+ const user = User.fromRow(row[User.table])
171
+ if (user) {
172
+ // Check if we already have this registration
173
+ if (!member.users.find(r => r.id == user.id)) {
174
+ member.users.push(user)
175
+ }
176
+ }
177
+ }
178
+
179
+ return members
180
+
181
+ }
182
+
183
+ getStructure() {
184
+ return GroupStruct.create({ ...this, privateSettings: null })
185
+ }
186
+
187
+ getPrivateStructure() {
188
+ return GroupStruct.create(this)
189
+ }
190
+
191
+ private static async getCount(where: string, params: any[]) {
192
+ const query = `select count(*) as c from \`${Registration.table}\` where ${where}`
193
+
194
+ const [results] = await Database.select(query, params)
195
+ const count = results[0]['']['c'];
196
+ if (Number.isInteger(count)) {
197
+ return count as number
198
+ }
199
+ return null
200
+ }
201
+
202
+ async updateOccupancy() {
203
+ this.settings.registeredMembers = await Group.getCount(
204
+ "groupId = ? and cycle = ? and waitingList = 0 and registeredAt is not null",
205
+ [this.id, this.cycle]
206
+ )
207
+ //const query = `select count(*) as c from \`${Registration.table}\` where groupId = ? and cycle = ? and (((registeredAt is not null or reservedUntil >= ?) and waitingList = 0) OR (waitingList = 1 AND canRegister = 1))`
208
+
209
+ this.settings.reservedMembers = await Group.getCount(
210
+ "groupId = ? and cycle = ? and ((waitingList = 0 and registeredAt is null AND reservedUntil >= ?) OR (waitingList = 1 and canRegister = 1))",
211
+ [this.id, this.cycle, new Date()]
212
+ )
213
+
214
+ this.settings.waitingListSize = await Group.getCount(
215
+ "groupId = ? and cycle = ? and waitingList = 1",
216
+ [this.id, this.cycle, new Date()]
217
+ )
218
+
219
+ // Loop cycle -1 until current (excluding current)
220
+ for (let cycle = -1; cycle < this.cycle; cycle++) {
221
+ if (!this.settings.cycleSettings.has(cycle)) {
222
+ this.settings.cycleSettings.set(cycle, CycleInformation.create({
223
+ registeredMembers: 0,
224
+ reservedMembers: 0,
225
+ waitingListSize: 0
226
+ }))
227
+ }
228
+ }
229
+
230
+ // Older cycles
231
+ // todo: optimize this a bit
232
+ for (const [cycle, info] of this.settings.cycleSettings) {
233
+
234
+ info.registeredMembers = await Group.getCount(
235
+ "groupId = ? and cycle = ? and waitingList = 0 and registeredAt is not null",
236
+ [this.id, cycle]
237
+ )
238
+
239
+ info.reservedMembers = await Group.getCount(
240
+ "groupId = ? and cycle = ? and ((waitingList = 0 and registeredAt is null AND reservedUntil >= ?) OR (waitingList = 1 and canRegister = 1))",
241
+ [this.id, cycle, new Date()]
242
+ )
243
+
244
+ info.waitingListSize = await Group.getCount(
245
+ "groupId = ? and cycle = ? and waitingList = 1",
246
+ [this.id, cycle, new Date()]
247
+ )
248
+ }
249
+ }
250
+
251
+ static async deleteUnreachable(organizationId: string, period: OrganizationRegistrationPeriod, allGroups: Group[]) {
252
+ const reachable = new Map<string, boolean>()
253
+
254
+ const visited = new Map<string, boolean>()
255
+ const queue = [period.settings.rootCategoryId]
256
+ visited.set(period.settings.rootCategoryId, true)
257
+
258
+ while (queue.length > 0) {
259
+ const id = queue.shift()
260
+ if (!id) {
261
+ break
262
+ }
263
+
264
+ const category = period.settings.categories.find(c => c.id === id)
265
+ if (!category) {
266
+ continue
267
+ }
268
+
269
+ for (const i of category.categoryIds) {
270
+ if (!visited.get(i)) {
271
+ queue.push(i)
272
+ visited.set(i, true)
273
+ }
274
+ }
275
+
276
+ for (const g of category.groupIds) {
277
+ reachable.set(g, true)
278
+ }
279
+ }
280
+
281
+ for (const group of allGroups) {
282
+ if (!reachable.get(group.id) && group.status !== GroupStatus.Archived) {
283
+ console.log("Archiving unreachable group "+group.id+" from organization "+organizationId + " org period "+period.id)
284
+ group.status = GroupStatus.Archived
285
+ await group.save()
286
+ }
287
+ }
288
+ }
289
+
290
+ }
291
+
292
+ Registration.group = new ManyToOneRelation(Group, "group")
293
+ Registration.group.foreignKey = "groupId"