@coursebuilder/adapter-drizzle 0.4.0 → 0.5.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 (255) hide show
  1. package/dist/{chunk-57CX5SOI.js → chunk-2UU2F4DM.js} +5 -4
  2. package/dist/{chunk-V23SCQL7.js → chunk-3NUCFLQJ.js} +2 -2
  3. package/dist/{chunk-Y7QZGEPT.js → chunk-4ZWS76VQ.js} +2 -2
  4. package/dist/{chunk-PPBEA4EM.js → chunk-6ON2O6UD.js} +2 -2
  5. package/dist/{chunk-ULEUIL2Y.js → chunk-7ISZNMPU.js} +3 -3
  6. package/dist/{chunk-DL3UMWB4.js → chunk-7X7TMG4A.js} +3 -3
  7. package/dist/{chunk-IDINCRHK.js → chunk-DAP5KOAO.js} +3 -3
  8. package/dist/{chunk-J4LBG2TH.js → chunk-GINRTL6J.js} +2 -2
  9. package/dist/{chunk-GLPDG7UE.js → chunk-KDEWFXEP.js} +2 -2
  10. package/dist/chunk-KDOSK7KN.js +62 -0
  11. package/dist/{chunk-MBB4GIJI.js → chunk-LERR6HQ3.js} +2 -2
  12. package/dist/{chunk-VDVCCU7D.js → chunk-LZHZDFNZ.js} +2 -2
  13. package/dist/{chunk-BX5SO2JG.js → chunk-P4VXXTVA.js} +2 -2
  14. package/dist/{chunk-VXZRMVNY.js → chunk-QP6YMUQX.js} +2 -2
  15. package/dist/{chunk-XJHSXR42.js → chunk-QPXUGIXO.js} +4 -4
  16. package/dist/{chunk-ZD3R6UPJ.js → chunk-SRTIWWLG.js} +7 -7
  17. package/dist/{chunk-N7UDJAI3.js → chunk-UHH4OVKA.js} +464 -349
  18. package/dist/chunk-UK6EHYS5.js +54 -0
  19. package/dist/{chunk-A26D3TCA.js → chunk-VUS77FG3.js} +577 -185
  20. package/dist/{chunk-FNYW3XKR.js → chunk-XIGJFVUM.js} +8 -8
  21. package/dist/{chunk-JKOYOL7A.js → chunk-YGRYHMCG.js} +2 -2
  22. package/dist/chunk-YVVXDJHI.js +56 -0
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.js +26 -23
  25. package/dist/lib/mysql/index.cjs +1424 -786
  26. package/dist/lib/mysql/index.cjs.map +1 -1
  27. package/dist/lib/mysql/index.d.cts +2056 -1162
  28. package/dist/lib/mysql/index.d.ts +2056 -1162
  29. package/dist/lib/mysql/index.js +24 -19
  30. package/dist/lib/mysql/schemas/auth/accounts.cjs +85 -77
  31. package/dist/lib/mysql/schemas/auth/accounts.cjs.map +1 -1
  32. package/dist/lib/mysql/schemas/auth/accounts.d.cts +17 -17
  33. package/dist/lib/mysql/schemas/auth/accounts.d.ts +17 -17
  34. package/dist/lib/mysql/schemas/auth/accounts.js +12 -12
  35. package/dist/lib/mysql/schemas/auth/device-access-token.cjs +214 -0
  36. package/dist/lib/mysql/schemas/auth/device-access-token.cjs.map +1 -0
  37. package/dist/lib/mysql/schemas/auth/device-access-token.d.cts +52 -0
  38. package/dist/lib/mysql/schemas/auth/device-access-token.d.ts +52 -0
  39. package/dist/lib/mysql/schemas/auth/device-access-token.js +22 -0
  40. package/dist/lib/mysql/schemas/auth/device-access-token.js.map +1 -0
  41. package/dist/lib/mysql/schemas/auth/device-verification.cjs +222 -0
  42. package/dist/lib/mysql/schemas/auth/device-verification.cjs.map +1 -0
  43. package/dist/lib/mysql/schemas/auth/device-verification.d.cts +88 -0
  44. package/dist/lib/mysql/schemas/auth/device-verification.d.ts +88 -0
  45. package/dist/lib/mysql/schemas/auth/device-verification.js +22 -0
  46. package/dist/lib/mysql/schemas/auth/device-verification.js.map +1 -0
  47. package/dist/lib/mysql/schemas/auth/permissions.cjs +1 -1
  48. package/dist/lib/mysql/schemas/auth/permissions.cjs.map +1 -1
  49. package/dist/lib/mysql/schemas/auth/permissions.d.cts +9 -9
  50. package/dist/lib/mysql/schemas/auth/permissions.d.ts +9 -9
  51. package/dist/lib/mysql/schemas/auth/permissions.js +1 -1
  52. package/dist/lib/mysql/schemas/auth/role-permissions.cjs +74 -66
  53. package/dist/lib/mysql/schemas/auth/role-permissions.cjs.map +1 -1
  54. package/dist/lib/mysql/schemas/auth/role-permissions.d.cts +10 -10
  55. package/dist/lib/mysql/schemas/auth/role-permissions.d.ts +10 -10
  56. package/dist/lib/mysql/schemas/auth/role-permissions.js +13 -13
  57. package/dist/lib/mysql/schemas/auth/roles.cjs +72 -64
  58. package/dist/lib/mysql/schemas/auth/roles.cjs.map +1 -1
  59. package/dist/lib/mysql/schemas/auth/roles.d.cts +10 -10
  60. package/dist/lib/mysql/schemas/auth/roles.d.ts +10 -10
  61. package/dist/lib/mysql/schemas/auth/roles.js +12 -12
  62. package/dist/lib/mysql/schemas/auth/sessions.cjs +75 -67
  63. package/dist/lib/mysql/schemas/auth/sessions.cjs.map +1 -1
  64. package/dist/lib/mysql/schemas/auth/sessions.d.cts +6 -6
  65. package/dist/lib/mysql/schemas/auth/sessions.d.ts +6 -6
  66. package/dist/lib/mysql/schemas/auth/sessions.js +13 -13
  67. package/dist/lib/mysql/schemas/auth/user-permissions.cjs +77 -69
  68. package/dist/lib/mysql/schemas/auth/user-permissions.cjs.map +1 -1
  69. package/dist/lib/mysql/schemas/auth/user-permissions.d.cts +10 -10
  70. package/dist/lib/mysql/schemas/auth/user-permissions.d.ts +10 -10
  71. package/dist/lib/mysql/schemas/auth/user-permissions.js +12 -12
  72. package/dist/lib/mysql/schemas/auth/user-prefs.cjs +222 -0
  73. package/dist/lib/mysql/schemas/auth/user-prefs.cjs.map +1 -0
  74. package/dist/lib/mysql/schemas/auth/user-prefs.d.cts +100 -0
  75. package/dist/lib/mysql/schemas/auth/user-prefs.d.ts +100 -0
  76. package/dist/lib/mysql/schemas/auth/user-prefs.js +21 -0
  77. package/dist/lib/mysql/schemas/auth/user-prefs.js.map +1 -0
  78. package/dist/lib/mysql/schemas/auth/user-roles.cjs +77 -69
  79. package/dist/lib/mysql/schemas/auth/user-roles.cjs.map +1 -1
  80. package/dist/lib/mysql/schemas/auth/user-roles.d.cts +10 -10
  81. package/dist/lib/mysql/schemas/auth/user-roles.d.ts +10 -10
  82. package/dist/lib/mysql/schemas/auth/user-roles.js +12 -12
  83. package/dist/lib/mysql/schemas/auth/users.cjs +263 -180
  84. package/dist/lib/mysql/schemas/auth/users.cjs.map +1 -1
  85. package/dist/lib/mysql/schemas/auth/users.d.cts +35 -21
  86. package/dist/lib/mysql/schemas/auth/users.d.ts +35 -21
  87. package/dist/lib/mysql/schemas/auth/users.js +12 -12
  88. package/dist/lib/mysql/schemas/auth/verification-tokens.cjs +1 -1
  89. package/dist/lib/mysql/schemas/auth/verification-tokens.cjs.map +1 -1
  90. package/dist/lib/mysql/schemas/auth/verification-tokens.d.cts +5 -5
  91. package/dist/lib/mysql/schemas/auth/verification-tokens.d.ts +5 -5
  92. package/dist/lib/mysql/schemas/auth/verification-tokens.js +1 -1
  93. package/dist/lib/mysql/schemas/commerce/coupon.cjs +76 -77
  94. package/dist/lib/mysql/schemas/commerce/coupon.cjs.map +1 -1
  95. package/dist/lib/mysql/schemas/commerce/coupon.d.cts +18 -30
  96. package/dist/lib/mysql/schemas/commerce/coupon.d.ts +18 -30
  97. package/dist/lib/mysql/schemas/commerce/coupon.js +12 -12
  98. package/dist/lib/mysql/schemas/commerce/merchant-account.cjs +1 -1
  99. package/dist/lib/mysql/schemas/commerce/merchant-account.cjs.map +1 -1
  100. package/dist/lib/mysql/schemas/commerce/merchant-account.d.cts +6 -6
  101. package/dist/lib/mysql/schemas/commerce/merchant-account.d.ts +6 -6
  102. package/dist/lib/mysql/schemas/commerce/merchant-account.js +1 -1
  103. package/dist/lib/mysql/schemas/commerce/merchant-charge.cjs +6 -5
  104. package/dist/lib/mysql/schemas/commerce/merchant-charge.cjs.map +1 -1
  105. package/dist/lib/mysql/schemas/commerce/merchant-charge.d.cts +13 -13
  106. package/dist/lib/mysql/schemas/commerce/merchant-charge.d.ts +13 -13
  107. package/dist/lib/mysql/schemas/commerce/merchant-charge.js +4 -4
  108. package/dist/lib/mysql/schemas/commerce/merchant-coupon.cjs +1 -1
  109. package/dist/lib/mysql/schemas/commerce/merchant-coupon.cjs.map +1 -1
  110. package/dist/lib/mysql/schemas/commerce/merchant-coupon.d.cts +7 -7
  111. package/dist/lib/mysql/schemas/commerce/merchant-coupon.d.ts +7 -7
  112. package/dist/lib/mysql/schemas/commerce/merchant-coupon.js +1 -1
  113. package/dist/lib/mysql/schemas/commerce/merchant-customer.cjs +3 -2
  114. package/dist/lib/mysql/schemas/commerce/merchant-customer.cjs.map +1 -1
  115. package/dist/lib/mysql/schemas/commerce/merchant-customer.d.cts +7 -7
  116. package/dist/lib/mysql/schemas/commerce/merchant-customer.d.ts +7 -7
  117. package/dist/lib/mysql/schemas/commerce/merchant-customer.js +1 -1
  118. package/dist/lib/mysql/schemas/commerce/merchant-price.cjs +1 -1
  119. package/dist/lib/mysql/schemas/commerce/merchant-price.cjs.map +1 -1
  120. package/dist/lib/mysql/schemas/commerce/merchant-price.d.cts +8 -8
  121. package/dist/lib/mysql/schemas/commerce/merchant-price.d.ts +8 -8
  122. package/dist/lib/mysql/schemas/commerce/merchant-price.js +1 -1
  123. package/dist/lib/mysql/schemas/commerce/merchant-product.cjs +1 -1
  124. package/dist/lib/mysql/schemas/commerce/merchant-product.cjs.map +1 -1
  125. package/dist/lib/mysql/schemas/commerce/merchant-product.d.cts +7 -7
  126. package/dist/lib/mysql/schemas/commerce/merchant-product.d.ts +7 -7
  127. package/dist/lib/mysql/schemas/commerce/merchant-product.js +1 -1
  128. package/dist/lib/mysql/schemas/commerce/merchant-session.cjs +1 -1
  129. package/dist/lib/mysql/schemas/commerce/merchant-session.cjs.map +1 -1
  130. package/dist/lib/mysql/schemas/commerce/merchant-session.d.cts +4 -4
  131. package/dist/lib/mysql/schemas/commerce/merchant-session.d.ts +4 -4
  132. package/dist/lib/mysql/schemas/commerce/merchant-session.js +1 -1
  133. package/dist/lib/mysql/schemas/commerce/price.cjs +60 -52
  134. package/dist/lib/mysql/schemas/commerce/price.cjs.map +1 -1
  135. package/dist/lib/mysql/schemas/commerce/price.d.cts +11 -11
  136. package/dist/lib/mysql/schemas/commerce/price.d.ts +11 -11
  137. package/dist/lib/mysql/schemas/commerce/price.js +12 -12
  138. package/dist/lib/mysql/schemas/commerce/product.cjs +75 -67
  139. package/dist/lib/mysql/schemas/commerce/product.cjs.map +1 -1
  140. package/dist/lib/mysql/schemas/commerce/product.d.cts +13 -13
  141. package/dist/lib/mysql/schemas/commerce/product.d.ts +13 -13
  142. package/dist/lib/mysql/schemas/commerce/product.js +12 -12
  143. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs +105 -96
  144. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs.map +1 -1
  145. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.d.cts +15 -15
  146. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.d.ts +15 -15
  147. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.js +13 -13
  148. package/dist/lib/mysql/schemas/commerce/purchase.cjs +136 -131
  149. package/dist/lib/mysql/schemas/commerce/purchase.cjs.map +1 -1
  150. package/dist/lib/mysql/schemas/commerce/purchase.d.cts +25 -25
  151. package/dist/lib/mysql/schemas/commerce/purchase.d.ts +25 -25
  152. package/dist/lib/mysql/schemas/commerce/purchase.js +12 -12
  153. package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs +66 -58
  154. package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs.map +1 -1
  155. package/dist/lib/mysql/schemas/commerce/upgradable-products.d.cts +11 -11
  156. package/dist/lib/mysql/schemas/commerce/upgradable-products.d.ts +11 -11
  157. package/dist/lib/mysql/schemas/commerce/upgradable-products.js +13 -13
  158. package/dist/lib/mysql/schemas/communication/comment.cjs +220 -0
  159. package/dist/lib/mysql/schemas/communication/comment.cjs.map +1 -0
  160. package/dist/lib/mysql/schemas/communication/comment.d.cts +100 -0
  161. package/dist/lib/mysql/schemas/communication/comment.d.ts +100 -0
  162. package/dist/lib/mysql/schemas/communication/comment.js +21 -0
  163. package/dist/lib/mysql/schemas/communication/comment.js.map +1 -0
  164. package/dist/lib/mysql/schemas/communication/communication-channel.cjs +1 -1
  165. package/dist/lib/mysql/schemas/communication/communication-channel.cjs.map +1 -1
  166. package/dist/lib/mysql/schemas/communication/communication-channel.d.cts +8 -8
  167. package/dist/lib/mysql/schemas/communication/communication-channel.d.ts +8 -8
  168. package/dist/lib/mysql/schemas/communication/communication-channel.js +1 -1
  169. package/dist/lib/mysql/schemas/communication/communication-preference-types.cjs +1 -1
  170. package/dist/lib/mysql/schemas/communication/communication-preference-types.cjs.map +1 -1
  171. package/dist/lib/mysql/schemas/communication/communication-preference-types.d.cts +8 -8
  172. package/dist/lib/mysql/schemas/communication/communication-preference-types.d.ts +8 -8
  173. package/dist/lib/mysql/schemas/communication/communication-preference-types.js +1 -1
  174. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs +99 -91
  175. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs.map +1 -1
  176. package/dist/lib/mysql/schemas/communication/communication-preferences.d.cts +16 -16
  177. package/dist/lib/mysql/schemas/communication/communication-preferences.d.ts +16 -16
  178. package/dist/lib/mysql/schemas/communication/communication-preferences.js +12 -12
  179. package/dist/lib/mysql/schemas/content/content-contributions.cjs +67 -59
  180. package/dist/lib/mysql/schemas/content/content-contributions.cjs.map +1 -1
  181. package/dist/lib/mysql/schemas/content/content-contributions.d.cts +13 -13
  182. package/dist/lib/mysql/schemas/content/content-contributions.d.ts +13 -13
  183. package/dist/lib/mysql/schemas/content/content-contributions.js +12 -12
  184. package/dist/lib/mysql/schemas/content/content-resource-product.cjs +61 -53
  185. package/dist/lib/mysql/schemas/content/content-resource-product.cjs.map +1 -1
  186. package/dist/lib/mysql/schemas/content/content-resource-product.d.cts +11 -11
  187. package/dist/lib/mysql/schemas/content/content-resource-product.d.ts +11 -11
  188. package/dist/lib/mysql/schemas/content/content-resource-product.js +12 -12
  189. package/dist/lib/mysql/schemas/content/content-resource-resource.cjs +58 -50
  190. package/dist/lib/mysql/schemas/content/content-resource-resource.cjs.map +1 -1
  191. package/dist/lib/mysql/schemas/content/content-resource-resource.d.cts +11 -11
  192. package/dist/lib/mysql/schemas/content/content-resource-resource.d.ts +11 -11
  193. package/dist/lib/mysql/schemas/content/content-resource-resource.js +12 -12
  194. package/dist/lib/mysql/schemas/content/content-resource.cjs +114 -66
  195. package/dist/lib/mysql/schemas/content/content-resource.cjs.map +1 -1
  196. package/dist/lib/mysql/schemas/content/content-resource.d.cts +14 -13
  197. package/dist/lib/mysql/schemas/content/content-resource.d.ts +14 -13
  198. package/dist/lib/mysql/schemas/content/content-resource.js +12 -12
  199. package/dist/lib/mysql/schemas/content/contribution-types.cjs +1 -1
  200. package/dist/lib/mysql/schemas/content/contribution-types.cjs.map +1 -1
  201. package/dist/lib/mysql/schemas/content/contribution-types.d.cts +10 -10
  202. package/dist/lib/mysql/schemas/content/contribution-types.d.ts +10 -10
  203. package/dist/lib/mysql/schemas/content/contribution-types.js +1 -1
  204. package/dist/lib/mysql/schemas/content/lesson-progress.cjs +77 -0
  205. package/dist/lib/mysql/schemas/content/lesson-progress.cjs.map +1 -0
  206. package/dist/lib/mysql/schemas/content/lesson-progress.d.cts +132 -0
  207. package/dist/lib/mysql/schemas/content/lesson-progress.d.ts +132 -0
  208. package/dist/lib/mysql/schemas/content/lesson-progress.js +8 -0
  209. package/dist/lib/mysql/schemas/content/lesson-progress.js.map +1 -0
  210. package/dist/lib/mysql/schemas/content/resource-progress.cjs +7 -7
  211. package/dist/lib/mysql/schemas/content/resource-progress.cjs.map +1 -1
  212. package/dist/lib/mysql/schemas/content/resource-progress.d.cts +9 -9
  213. package/dist/lib/mysql/schemas/content/resource-progress.d.ts +9 -9
  214. package/dist/lib/mysql/schemas/content/resource-progress.js +1 -1
  215. package/dist/lib/utils.d.cts +1 -0
  216. package/dist/lib/utils.d.ts +1 -0
  217. package/package.json +7 -5
  218. package/src/index.ts +14 -4
  219. package/src/lib/mysql/index.ts +675 -153
  220. package/src/lib/mysql/schemas/auth/accounts.ts +1 -1
  221. package/src/lib/mysql/schemas/auth/device-access-token.ts +40 -0
  222. package/src/lib/mysql/schemas/auth/device-verification.ts +48 -0
  223. package/src/lib/mysql/schemas/auth/permissions.ts +1 -1
  224. package/src/lib/mysql/schemas/auth/role-permissions.ts +1 -1
  225. package/src/lib/mysql/schemas/auth/roles.ts +1 -1
  226. package/src/lib/mysql/schemas/auth/sessions.ts +1 -1
  227. package/src/lib/mysql/schemas/auth/user-permissions.ts +1 -1
  228. package/src/lib/mysql/schemas/auth/user-prefs.ts +52 -0
  229. package/src/lib/mysql/schemas/auth/user-roles.ts +1 -1
  230. package/src/lib/mysql/schemas/auth/users.ts +14 -2
  231. package/src/lib/mysql/schemas/auth/verification-tokens.ts +1 -1
  232. package/src/lib/mysql/schemas/commerce/coupon.ts +4 -13
  233. package/src/lib/mysql/schemas/commerce/merchant-account.ts +1 -1
  234. package/src/lib/mysql/schemas/commerce/merchant-charge.ts +1 -1
  235. package/src/lib/mysql/schemas/commerce/merchant-coupon.ts +1 -1
  236. package/src/lib/mysql/schemas/commerce/merchant-customer.ts +3 -1
  237. package/src/lib/mysql/schemas/commerce/merchant-price.ts +1 -1
  238. package/src/lib/mysql/schemas/commerce/merchant-product.ts +1 -1
  239. package/src/lib/mysql/schemas/commerce/merchant-session.ts +1 -1
  240. package/src/lib/mysql/schemas/commerce/price.ts +1 -1
  241. package/src/lib/mysql/schemas/commerce/product.ts +1 -1
  242. package/src/lib/mysql/schemas/commerce/purchase-user-transfer.ts +1 -1
  243. package/src/lib/mysql/schemas/commerce/purchase.ts +5 -4
  244. package/src/lib/mysql/schemas/commerce/upgradable-products.ts +1 -1
  245. package/src/lib/mysql/schemas/communication/comment.ts +52 -0
  246. package/src/lib/mysql/schemas/communication/communication-channel.ts +1 -1
  247. package/src/lib/mysql/schemas/communication/communication-preference-types.ts +1 -1
  248. package/src/lib/mysql/schemas/communication/communication-preferences.ts +1 -1
  249. package/src/lib/mysql/schemas/content/content-contributions.ts +1 -1
  250. package/src/lib/mysql/schemas/content/content-resource-product.ts +1 -1
  251. package/src/lib/mysql/schemas/content/content-resource-resource.ts +1 -1
  252. package/src/lib/mysql/schemas/content/content-resource.ts +6 -2
  253. package/src/lib/mysql/schemas/content/contribution-types.ts +1 -1
  254. package/src/lib/mysql/schemas/content/lesson-progress.ts +39 -0
  255. package/src/lib/mysql/schemas/content/resource-progress.ts +6 -8
@@ -1,8 +1,10 @@
1
1
  import type { AdapterSession, AdapterUser } from '@auth/core/adapters'
2
+ import slugify from '@sindresorhus/slugify'
2
3
  import { addSeconds, isAfter } from 'date-fns'
3
4
  import {
4
5
  and,
5
6
  asc,
7
+ count,
6
8
  desc,
7
9
  eq,
8
10
  gte,
@@ -18,6 +20,7 @@ import {
18
20
  MySqlDatabase,
19
21
  MySqlTableFn,
20
22
  } from 'drizzle-orm/mysql-core'
23
+ import { customAlphabet } from 'nanoid'
21
24
  import { v4 } from 'uuid'
22
25
  import { z } from 'zod'
23
26
 
@@ -33,6 +36,7 @@ import {
33
36
  merchantPriceSchema,
34
37
  MerchantProduct,
35
38
  merchantProductSchema,
39
+ NewProduct,
36
40
  Price,
37
41
  priceSchema,
38
42
  Product,
@@ -50,13 +54,16 @@ import {
50
54
  userSchema,
51
55
  } from '@coursebuilder/core/schemas'
52
56
  import {
57
+ ContentResourceProductSchema,
53
58
  ContentResourceResourceSchema,
54
59
  ContentResourceSchema,
60
+ type ContentResource,
55
61
  } from '@coursebuilder/core/schemas/content-resource-schema'
56
62
  import { merchantAccountSchema } from '@coursebuilder/core/schemas/merchant-account-schema'
57
63
  import { merchantCustomerSchema } from '@coursebuilder/core/schemas/merchant-customer-schema'
58
64
  import { type ModuleProgress } from '@coursebuilder/core/schemas/resource-progress-schema'
59
65
  import { VideoResourceSchema } from '@coursebuilder/core/schemas/video-resource'
66
+ import { PaymentsProviderConfig } from '@coursebuilder/core/types'
60
67
  import { logger } from '@coursebuilder/core/utils/logger'
61
68
  import { validateCoupon } from '@coursebuilder/core/utils/validate-coupon'
62
69
 
@@ -64,6 +71,14 @@ import {
64
71
  getAccountsRelationsSchema,
65
72
  getAccountsSchema,
66
73
  } from './schemas/auth/accounts.js'
74
+ import {
75
+ getDeviceAccessTokenRelationsSchema,
76
+ getDeviceAccessTokenSchema,
77
+ } from './schemas/auth/device-access-token.js'
78
+ import {
79
+ getDeviceVerificationRelationsSchema,
80
+ getDeviceVerificationSchema,
81
+ } from './schemas/auth/device-verification.js'
67
82
  import {
68
83
  getPermissionsRelationsSchema,
69
84
  getPermissionsSchema,
@@ -84,6 +99,10 @@ import {
84
99
  getUserPermissionsRelationsSchema,
85
100
  getUserPermissionsSchema,
86
101
  } from './schemas/auth/user-permissions.js'
102
+ import {
103
+ getUserPrefsRelationsSchema,
104
+ getUserPrefsSchema,
105
+ } from './schemas/auth/user-prefs.js'
87
106
  import {
88
107
  getUserRolesRelationsSchema,
89
108
  getUserRolesSchema,
@@ -124,6 +143,10 @@ import {
124
143
  getUpgradableProductsRelationsSchema,
125
144
  getUpgradableProductsSchema,
126
145
  } from './schemas/commerce/upgradable-products.js'
146
+ import {
147
+ getCommentRelationsSchema,
148
+ getCommentsSchema,
149
+ } from './schemas/communication/comment.js'
127
150
  import { getCommunicationChannelSchema } from './schemas/communication/communication-channel.js'
128
151
  import { getCommunicationPreferenceTypesSchema } from './schemas/communication/communication-preference-types.js'
129
152
  import {
@@ -150,8 +173,11 @@ import {
150
173
  getContributionTypesRelationsSchema,
151
174
  getContributionTypesSchema,
152
175
  } from './schemas/content/contribution-types.js'
176
+ import { getLessonProgressSchema } from './schemas/content/lesson-progress.js'
153
177
  import { getResourceProgressSchema } from './schemas/content/resource-progress.js'
154
178
 
179
+ export const guid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 5)
180
+
155
181
  export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
156
182
  return {
157
183
  accounts: getAccountsSchema(mysqlTable),
@@ -173,6 +199,7 @@ export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
173
199
  verificationTokens: getVerificationTokensSchema(mysqlTable),
174
200
  coupon: getCouponSchema(mysqlTable),
175
201
  couponRelations: getCouponRelationsSchema(mysqlTable),
202
+ lessonProgress: getLessonProgressSchema(mysqlTable),
176
203
  merchantAccount: getMerchantAccountSchema(mysqlTable),
177
204
  merchantCharge: getMerchantChargeSchema(mysqlTable),
178
205
  merchantChargeRelations: getMerchantChargeRelationsSchema(mysqlTable),
@@ -212,6 +239,15 @@ export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
212
239
  contentResourceProductRelations:
213
240
  getContentResourceProductRelationsSchema(mysqlTable),
214
241
  productRelations: getProductRelationsSchema(mysqlTable),
242
+ comments: getCommentsSchema(mysqlTable),
243
+ commentsRelations: getCommentRelationsSchema(mysqlTable),
244
+ deviceVerifications: getDeviceVerificationSchema(mysqlTable),
245
+ deviceVerificationRelations:
246
+ getDeviceVerificationRelationsSchema(mysqlTable),
247
+ deviceAccessToken: getDeviceAccessTokenSchema(mysqlTable),
248
+ deviceAccessTokenRelations: getDeviceAccessTokenRelationsSchema(mysqlTable),
249
+ userPrefs: getUserPrefsSchema(mysqlTable),
250
+ userPrefsRelations: getUserPrefsRelationsSchema(mysqlTable),
215
251
  } as const
216
252
  }
217
253
 
@@ -224,6 +260,7 @@ export type DefaultSchema = ReturnType<typeof createTables>
224
260
  export function mySqlDrizzleAdapter(
225
261
  client: InstanceType<typeof MySqlDatabase>,
226
262
  tableFn = defaultMySqlTableFn,
263
+ paymentProvider?: PaymentsProviderConfig,
227
264
  ): CourseBuilderAdapter<typeof MySqlDatabase> {
228
265
  const {
229
266
  users,
@@ -232,6 +269,7 @@ export function mySqlDrizzleAdapter(
232
269
  verificationTokens,
233
270
  contentResource,
234
271
  contentResourceResource,
272
+ contentResourceProduct,
235
273
  purchases: purchaseTable,
236
274
  purchaseUserTransfer,
237
275
  coupon,
@@ -246,6 +284,7 @@ export function mySqlDrizzleAdapter(
246
284
  products,
247
285
  upgradableProducts,
248
286
  resourceProgress,
287
+ comments,
249
288
  } = createTables(tableFn)
250
289
 
251
290
  const adapter: CourseBuilderAdapter = {
@@ -273,7 +312,7 @@ export function mySqlDrizzleAdapter(
273
312
  if (coupon && couponValidation.isRedeemable) {
274
313
  // if the Coupon is the Bulk Coupon of a Bulk Purchase,
275
314
  // then a bulk coupon is being redeemed
276
- const bulkCouponRedemption = Boolean(coupon.bulkPurchase?.bulkCouponId)
315
+ const bulkCouponRedemption = Boolean(coupon.maxUses > 1)
277
316
 
278
317
  const { user } = await adapter.findOrCreateUser(email)
279
318
 
@@ -294,8 +333,18 @@ export function mySqlDrizzleAdapter(
294
333
  productId,
295
334
  })
296
335
 
297
- if (existingPurchases.length > 0)
298
- throw new Error(`already-purchased-${email}`)
336
+ if (existingPurchases.length > 0) {
337
+ const errorMessage = `already-purchased-${email}`
338
+ console.error(errorMessage)
339
+ return {
340
+ error: {
341
+ message: errorMessage,
342
+ },
343
+ redeemingForCurrentUser,
344
+ purchase: null,
345
+ }
346
+ throw new Error(errorMessage)
347
+ }
299
348
 
300
349
  const purchaseId = `purchase-${v4()}`
301
350
 
@@ -379,7 +428,10 @@ export function mySqlDrizzleAdapter(
379
428
  },
380
429
  getMerchantPriceForProductId: async (productId) => {
381
430
  const merchantPriceData = await client.query.merchantPrice.findFirst({
382
- where: eq(merchantPrice.merchantProductId, productId),
431
+ where: and(
432
+ eq(merchantPrice.merchantProductId, productId),
433
+ eq(merchantPrice.status, 1),
434
+ ),
383
435
  })
384
436
 
385
437
  const parsedMerchantPrice =
@@ -432,7 +484,7 @@ export function mySqlDrizzleAdapter(
432
484
  )
433
485
 
434
486
  if (previousPurchaseProductIds.length > 0) {
435
- return await client.query.upgradableProducts.findMany({
487
+ return client.query.upgradableProducts.findMany({
436
488
  where: and(
437
489
  eq(upgradableProducts.upgradableToId, productId),
438
490
  inArray(
@@ -461,7 +513,7 @@ export function mySqlDrizzleAdapter(
461
513
  let lessonProgress = await client.query.resourceProgress.findFirst({
462
514
  where: and(
463
515
  eq(resourceProgress.userId, options.userId),
464
- eq(resourceProgress.contentResourceId, options.lessonId),
516
+ eq(resourceProgress.resourceId, options.lessonId),
465
517
  ),
466
518
  })
467
519
 
@@ -475,12 +527,12 @@ export function mySqlDrizzleAdapter(
475
527
  completedAt: now,
476
528
  updatedAt: now,
477
529
  })
478
- .where(eq(resourceProgress.contentResourceId, options.lessonId))
530
+ .where(eq(resourceProgress.resourceId, options.lessonId))
479
531
  }
480
532
  } else {
481
533
  await client.insert(resourceProgress).values({
482
534
  userId: options.userId,
483
- contentResourceId: options.lessonId,
535
+ resourceId: options.lessonId,
484
536
  completedAt: now,
485
537
  updatedAt: now,
486
538
  })
@@ -488,7 +540,7 @@ export function mySqlDrizzleAdapter(
488
540
  lessonProgress = await client.query.resourceProgress.findFirst({
489
541
  where: and(
490
542
  eq(resourceProgress.userId, options.userId),
491
- eq(resourceProgress.contentResourceId, options.lessonId),
543
+ eq(resourceProgress.resourceId, options.lessonId),
492
544
  ),
493
545
  })
494
546
  const parsedLessonProgress =
@@ -530,11 +582,21 @@ export function mySqlDrizzleAdapter(
530
582
 
531
583
  if (!couponForIdOrCode) return null
532
584
 
533
- return couponSchema
585
+ const parsedCoupon = couponSchema
534
586
  .extend({
535
587
  merchantCoupon: merchantCouponSchema,
536
588
  })
537
- .parse(couponForIdOrCode)
589
+ .safeParse(couponForIdOrCode)
590
+
591
+ if (!parsedCoupon.success) {
592
+ console.error(
593
+ 'Error parsing coupon',
594
+ JSON.stringify(parsedCoupon.error),
595
+ )
596
+ return null
597
+ }
598
+
599
+ return parsedCoupon.data
538
600
  },
539
601
  async createMerchantChargeAndPurchase(options): Promise<Purchase> {
540
602
  const purchaseId = await client.transaction(async (trx) => {
@@ -620,7 +682,7 @@ export function mySqlDrizzleAdapter(
620
682
  ),
621
683
  )
622
684
  .then((res) => {
623
- return res[0]?.coupons ?? null
685
+ return res[0]?.Coupon ?? null
624
686
  }),
625
687
  )
626
688
 
@@ -830,20 +892,21 @@ export function mySqlDrizzleAdapter(
830
892
  },
831
893
  async getCouponWithBulkPurchases(couponId: string): Promise<
832
894
  | (Coupon & {
833
- bulkPurchase?: Purchase | null
834
- bulkCouponPurchases: { bulkCouponId?: string | null }[]
895
+ bulkPurchases?: Purchase[] | null
896
+ redeemedBulkCouponPurchases: { bulkCouponId?: string | null }[]
835
897
  })
836
898
  | null
837
899
  > {
838
900
  logger.debug('getCouponWithBulkPurchases', { couponId })
839
901
  let couponData
902
+ let bulkCouponPurchases
840
903
  try {
841
904
  couponData =
842
905
  (await client.query.coupon.findFirst({
843
906
  where: eq(coupon.id, couponId),
844
907
  with: {
845
- bulkPurchase: true,
846
- bulkCouponPurchases: true,
908
+ bulkPurchases: true,
909
+ redeemedBulkCouponPurchases: true,
847
910
  },
848
911
  })) || null
849
912
  } catch (e) {
@@ -851,6 +914,19 @@ export function mySqlDrizzleAdapter(
851
914
  logger.error(e as Error)
852
915
  }
853
916
 
917
+ try {
918
+ bulkCouponPurchases = await client.query.purchases.findMany({
919
+ where: eq(purchaseTable.redeemedBulkCouponId, couponId),
920
+ with: {
921
+ user: true,
922
+ },
923
+ })
924
+ console.log('purchases with redeemedBulkCouponId', bulkCouponPurchases)
925
+ } catch (e) {
926
+ console.log('getCouponWithBulkPurchases')
927
+ logger.error(e as Error)
928
+ }
929
+
854
930
  if (!couponData) {
855
931
  logger.debug('getCouponWithBulkPurchases', {
856
932
  couponId,
@@ -859,20 +935,29 @@ export function mySqlDrizzleAdapter(
859
935
  return null
860
936
  }
861
937
 
938
+ const couponWithBulkPurchases = {
939
+ ...couponData,
940
+ redeemedBulkCouponPurchases: bulkCouponPurchases || [],
941
+ }
942
+
862
943
  const parsedCoupon = couponSchema
863
944
  .merge(
864
945
  z.object({
865
- bulkCouponPurchases: z.array(purchaseSchema),
866
- bulkPurchase: purchaseSchema.nullable().optional(),
946
+ redeemedBulkCouponPurchases: z.array(purchaseSchema),
867
947
  }),
868
948
  )
869
949
  .nullable()
870
- .safeParse(couponData)
950
+ .safeParse(couponWithBulkPurchases)
871
951
 
872
952
  if (!parsedCoupon.success) {
873
- console.error('Error parsing coupon', couponData)
953
+ console.error(
954
+ 'Error parsing coupon',
955
+ JSON.stringify(parsedCoupon.error),
956
+ couponData,
957
+ )
874
958
  return null
875
959
  }
960
+
876
961
  return parsedCoupon.data
877
962
  },
878
963
  async getDefaultCoupon(productIds?: string[]): Promise<{
@@ -941,146 +1026,115 @@ export function mySqlDrizzleAdapter(
941
1026
  async getModuleProgressForUser(
942
1027
  userIdOrEmail: string,
943
1028
  moduleIdOrSlug: string,
944
- ): Promise<ModuleProgress> {
945
- const module = await client.query.contentResource.findFirst({
946
- where: or(
947
- eq(contentResource.id, moduleIdOrSlug),
948
- eq(
949
- sql`JSON_EXTRACT (${contentResource.fields}, "$.slug")`,
950
- moduleIdOrSlug,
951
- ),
952
- ),
953
- with: {
954
- resources: {
955
- orderBy: asc(contentResourceResource.position),
956
- },
1029
+ ): Promise<ModuleProgress | null> {
1030
+ // First, get the user ID
1031
+ const user = await client.query.users.findFirst({
1032
+ where: or(eq(users.id, userIdOrEmail), eq(users.email, userIdOrEmail)),
1033
+ columns: {
1034
+ id: true,
957
1035
  },
958
1036
  })
959
1037
 
960
- const parsedModule = ContentResourceSchema.parse(module)
961
-
962
- const moduleResources =
963
- await client.query.contentResourceResource.findMany({
964
- where: inArray(
965
- contentResourceResource.resourceOfId,
966
- parsedModule.resources?.map((r) => r.resourceId) ?? [],
967
- ),
968
- orderBy: asc(contentResourceResource.position),
969
- })
1038
+ if (!user) {
1039
+ return null
1040
+ }
970
1041
 
971
- const parsedModuleResources = z
972
- .array(ContentResourceResourceSchema)
973
- .safeParse(moduleResources)
1042
+ const ResultRowSchema = z.object({
1043
+ resource_id: z.string(),
1044
+ resource_type: z.enum(['lesson', 'exercise']),
1045
+ resource_slug: z.string().nullable(),
1046
+ completed_at: z
1047
+ .string()
1048
+ .nullable()
1049
+ .transform((val) => (val ? new Date(val) : null)),
1050
+ })
974
1051
 
975
- if (!parsedModuleResources.success) {
976
- console.error(
977
- 'Error parsing module resources',
978
- parsedModuleResources.error,
979
- )
1052
+ // Execute the optimized query
1053
+ const results: any = await client.execute(sql`
1054
+ SELECT
1055
+ cr.id AS resource_id,
1056
+ cr.type AS resource_type,
1057
+ cr.fields->>'$.slug' AS resource_slug,
1058
+ rp.completedAt AS completed_at
1059
+ FROM
1060
+ (SELECT id, fields->>'$.slug' AS slug
1061
+ FROM ${contentResource}
1062
+ WHERE id = ${moduleIdOrSlug}
1063
+ OR fields->>'$.slug' = ${moduleIdOrSlug}) AS w
1064
+ LEFT JOIN
1065
+ (SELECT
1066
+ crr.resourceOfId AS workshop_id,
1067
+ crr.resourceId,
1068
+ crr.position
1069
+ FROM ${contentResourceResource} crr) AS tlr ON w.id = tlr.workshop_id
1070
+ LEFT JOIN ${contentResource} sections ON sections.id = tlr.resourceId AND sections.type = 'section'
1071
+ LEFT JOIN
1072
+ (SELECT
1073
+ crr.resourceOfId AS section_id,
1074
+ crr.resourceId AS resource_id,
1075
+ crr.position AS position_in_section,
1076
+ section_crr.position AS section_position
1077
+ FROM ${contentResourceResource} crr
1078
+ JOIN ${contentResourceResource} section_crr ON crr.resourceOfId = section_crr.resourceId) AS sr
1079
+ ON sr.section_id = sections.id
1080
+ JOIN ${contentResource} cr ON cr.id = COALESCE(sr.resource_id, tlr.resourceId)
1081
+ LEFT JOIN ${resourceProgress} rp ON rp.resourceId = cr.id
1082
+ AND rp.userId = ${user.id}
1083
+ WHERE
1084
+ cr.type IN ('lesson', 'exercise')
1085
+
1086
+ `)
1087
+
1088
+ // Process the results
1089
+ const completedLessons: ResourceProgress[] = []
1090
+ let nextResource: Partial<ContentResource> | null = null
1091
+ let completedLessonsCount = 0
1092
+ let totalLessonsCount = results.rows.length
1093
+
1094
+ const parsedRows = z.array(ResultRowSchema).safeParse(results.rows)
1095
+
1096
+ if (!parsedRows.success) {
1097
+ console.error('Error parsing rows', parsedRows.error)
980
1098
  return {
981
1099
  completedLessons: [],
982
1100
  nextResource: null,
983
1101
  percentCompleted: 0,
984
1102
  completedLessonsCount: 0,
985
- totalLessonsCount: 0,
1103
+ totalLessonsCount,
986
1104
  }
987
1105
  }
988
1106
 
989
- const user = await client.query.users
990
- .findFirst({
991
- where: or(
992
- eq(users.id, userIdOrEmail),
993
- eq(users.email, userIdOrEmail),
994
- ),
995
- with: {
996
- roles: {
997
- with: {
998
- role: true,
999
- },
1107
+ for (const row of parsedRows.data) {
1108
+ if (row.completed_at) {
1109
+ completedLessonsCount++
1110
+ completedLessons.push({
1111
+ userId: user.id as string,
1112
+ resourceId: row.resource_id,
1113
+ completedAt: new Date(row.completed_at),
1114
+ // Add other fields as needed
1115
+ })
1116
+ } else if (!nextResource) {
1117
+ nextResource = {
1118
+ id: row.resource_id,
1119
+ type: row.resource_type,
1120
+ fields: {
1121
+ slug: row.resource_slug,
1000
1122
  },
1001
- },
1002
- })
1003
- .then(async (res) => {
1004
- if (res) {
1005
- return {
1006
- ...res,
1007
- roles: res.roles.map((r) => r.role),
1008
- }
1009
1123
  }
1010
- })
1011
-
1012
- if (!user) {
1013
- console.error('User not found', userIdOrEmail)
1014
- return {
1015
- completedLessons: [],
1016
- nextResource: null,
1017
- percentCompleted: 0,
1018
- completedLessonsCount: 0,
1019
- totalLessonsCount: parsedModuleResources.data.length,
1020
1124
  }
1021
1125
  }
1022
1126
 
1023
- const parsedUser = userSchema.safeParse(user)
1024
-
1025
- if (!parsedUser.success) {
1026
- console.error('Error parsing user', parsedUser.error)
1027
- return {
1028
- completedLessons: [],
1029
- nextResource: null,
1030
- percentCompleted: 0,
1031
- completedLessonsCount: 0,
1032
- totalLessonsCount: parsedModuleResources.data.length,
1033
- }
1034
- }
1035
-
1036
- const userProgress = await client.query.resourceProgress.findMany({
1037
- where: and(
1038
- eq(resourceProgress.userId, parsedUser.data.id),
1039
- isNotNull(resourceProgress.completedAt),
1040
- inArray(
1041
- resourceProgress.contentResourceId,
1042
- parsedModuleResources.data.map((r) => r.resourceId),
1043
- ),
1044
- ),
1045
- orderBy: asc(resourceProgress.completedAt),
1046
- })
1047
-
1048
- const nextResourceId = moduleResources.find(
1049
- (r) => !userProgress.find((p) => p.contentResourceId === r.resourceId),
1050
- )?.resourceId
1051
-
1052
- const nextResource = await client.query.contentResource.findFirst({
1053
- where: eq(contentResource.id, nextResourceId as string),
1054
- })
1055
-
1056
- const parsedNextResource = ContentResourceSchema.nullable()
1057
- .optional()
1058
- .default(null)
1059
- .parse(nextResource)
1060
-
1061
- const parsedProgress = z
1062
- .array(resourceProgressSchema)
1063
- .safeParse(userProgress)
1064
- if (!parsedProgress.success) {
1065
- console.error('Error parsing user progress', parsedProgress.error)
1066
- return {
1067
- completedLessons: [],
1068
- nextResource: null,
1069
- percentCompleted: 0,
1070
- completedLessonsCount: 0,
1071
- totalLessonsCount: parsedModuleResources.data.length,
1072
- }
1073
- }
1074
- const percentCompleted = Math.round(
1075
- (parsedProgress.data.length / parsedModuleResources.data.length) * 100,
1076
- )
1127
+ const percentCompleted =
1128
+ totalLessonsCount > 0
1129
+ ? Math.ceil((completedLessonsCount / totalLessonsCount) * 100)
1130
+ : 0
1077
1131
 
1078
1132
  return {
1079
- completedLessons: parsedProgress.data,
1080
- nextResource: parsedNextResource,
1133
+ completedLessons,
1134
+ nextResource,
1081
1135
  percentCompleted,
1082
- completedLessonsCount: parsedProgress.data.length,
1083
- totalLessonsCount: parsedModuleResources.data.length,
1136
+ completedLessonsCount,
1137
+ totalLessonsCount,
1084
1138
  }
1085
1139
  },
1086
1140
  getLessonProgresses(): Promise<ResourceProgress[]> {
@@ -1094,7 +1148,7 @@ export function mySqlDrizzleAdapter(
1094
1148
  })
1095
1149
  const parsed = merchantChargeSchema.safeParse(mCharge)
1096
1150
  if (!parsed.success) {
1097
- console.error('Error parsing merchantCharge', mCharge)
1151
+ console.info('Error parsing merchantCharge', mCharge)
1098
1152
  return null
1099
1153
  }
1100
1154
  return parsed.data
@@ -1173,12 +1227,366 @@ export function mySqlDrizzleAdapter(
1173
1227
  }),
1174
1228
  )
1175
1229
  },
1176
- async getProduct(productId: string): Promise<Product | null> {
1177
- return productSchema.nullable().parse(
1178
- await client.query.products.findFirst({
1179
- where: eq(products.id, productId),
1230
+ async archiveProduct(productId) {
1231
+ if (!paymentProvider) throw new Error('Payment provider not found')
1232
+ const product = await adapter.getProduct(productId)
1233
+
1234
+ if (!product) {
1235
+ throw new Error(`Product not found for id (${productId})`)
1236
+ }
1237
+
1238
+ if (!product.price) {
1239
+ throw new Error(`Product has no price`)
1240
+ }
1241
+
1242
+ await client
1243
+ .update(products)
1244
+ .set({ status: 0, name: `${product.name} (Archived)` })
1245
+ .where(eq(products.id, productId))
1246
+
1247
+ await client
1248
+ .update(prices)
1249
+ .set({ status: 0, nickname: `${product.name} (Archived)` })
1250
+ .where(eq(prices.productId, productId))
1251
+
1252
+ await client
1253
+ .update(merchantProduct)
1254
+ .set({ status: 0 })
1255
+ .where(eq(merchantProduct.productId, productId))
1256
+
1257
+ await client
1258
+ .update(merchantPrice)
1259
+ .set({ status: 0 })
1260
+ .where(eq(merchantPrice.priceId, product.price.id))
1261
+
1262
+ const currentMerchantProduct = merchantProductSchema.nullish().parse(
1263
+ await client.query.merchantProduct.findFirst({
1264
+ where: eq(merchantProduct.productId, productId),
1180
1265
  }),
1181
1266
  )
1267
+
1268
+ if (!currentMerchantProduct || !currentMerchantProduct.identifier) {
1269
+ throw new Error(`Merchant product not found for id (${productId})`)
1270
+ }
1271
+
1272
+ await paymentProvider.updateProduct(currentMerchantProduct.identifier, {
1273
+ active: false,
1274
+ })
1275
+
1276
+ const currentMerchantPrice = merchantPriceSchema.nullish().parse(
1277
+ await client.query.merchantPrice.findFirst({
1278
+ where: and(
1279
+ eq(merchantPrice.priceId, product.price.id),
1280
+ eq(merchantPrice.status, 1),
1281
+ ),
1282
+ }),
1283
+ )
1284
+
1285
+ if (!currentMerchantPrice || !currentMerchantPrice.identifier) {
1286
+ throw new Error(`Merchant price not found for id (${productId})`)
1287
+ }
1288
+
1289
+ await paymentProvider.updatePrice(currentMerchantPrice.identifier, {
1290
+ active: false,
1291
+ })
1292
+
1293
+ return adapter.getProduct(productId)
1294
+ },
1295
+ async updateProduct(input: Product) {
1296
+ if (!paymentProvider) throw new Error('Payment provider not found')
1297
+ const currentProduct = await adapter.getProduct(input.id)
1298
+ if (!currentProduct) {
1299
+ throw new Error(`Product not found`)
1300
+ }
1301
+ if (!currentProduct.price) {
1302
+ throw new Error(`Product has no price`)
1303
+ }
1304
+
1305
+ const merchantProduct = merchantProductSchema.nullish().parse(
1306
+ await client.query.merchantProduct.findFirst({
1307
+ where: (merchantProduct, { eq }) =>
1308
+ eq(merchantProduct.productId, input.id),
1309
+ }),
1310
+ )
1311
+
1312
+ if (!merchantProduct || !merchantProduct.identifier) {
1313
+ throw new Error(`Merchant product not found`)
1314
+ }
1315
+
1316
+ // TODO: handle upgrades
1317
+
1318
+ const stripeProduct = await paymentProvider.getProduct(
1319
+ merchantProduct.identifier,
1320
+ )
1321
+
1322
+ const priceChanged =
1323
+ currentProduct.price.unitAmount.toString() !==
1324
+ input.price?.unitAmount.toString()
1325
+
1326
+ if (priceChanged) {
1327
+ const currentMerchantPrice = merchantPriceSchema.nullish().parse(
1328
+ await client.query.merchantPrice.findFirst({
1329
+ where: (merchantPrice, { eq, and }) =>
1330
+ and(
1331
+ eq(merchantPrice.merchantProductId, merchantProduct.id),
1332
+ eq(merchantPrice.status, 1),
1333
+ ),
1334
+ }),
1335
+ )
1336
+
1337
+ if (!currentMerchantPrice || !currentMerchantPrice.identifier) {
1338
+ throw new Error(`Merchant price not found`)
1339
+ }
1340
+
1341
+ const currentStripePrice = await paymentProvider.getPrice(
1342
+ currentMerchantPrice.identifier,
1343
+ )
1344
+
1345
+ const newStripePrice = await paymentProvider.createPrice({
1346
+ product: stripeProduct.id,
1347
+ unit_amount: Math.floor(Number(input.price?.unitAmount || 0) * 100),
1348
+ currency: 'usd',
1349
+ metadata: {
1350
+ slug: input.fields.slug,
1351
+ },
1352
+ active: true,
1353
+ })
1354
+
1355
+ await paymentProvider.updateProduct(stripeProduct.id, {
1356
+ default_price: newStripePrice.id,
1357
+ })
1358
+
1359
+ const newMerchantPriceId = `mprice_${v4()}`
1360
+ await client.insert(merchantPrice).values({
1361
+ id: newMerchantPriceId,
1362
+ merchantProductId: merchantProduct.id,
1363
+ merchantAccountId: merchantProduct.merchantAccountId,
1364
+ priceId: currentProduct.price.id,
1365
+ status: 1,
1366
+ identifier: newStripePrice.id,
1367
+ })
1368
+
1369
+ if (currentMerchantPrice) {
1370
+ await client
1371
+ .update(merchantPrice)
1372
+ .set({
1373
+ status: 0,
1374
+ })
1375
+ .where(eq(merchantPrice.id, currentMerchantPrice.id))
1376
+ }
1377
+
1378
+ await client
1379
+ .update(prices)
1380
+ .set({
1381
+ unitAmount: Math.floor(
1382
+ Number(input.price?.unitAmount || 0),
1383
+ ).toString(),
1384
+ nickname: input.name,
1385
+ })
1386
+ .where(eq(prices.id, currentProduct.price.id))
1387
+
1388
+ if (currentStripePrice) {
1389
+ await paymentProvider.updatePrice(currentStripePrice.id, {
1390
+ active: false,
1391
+ })
1392
+ }
1393
+ }
1394
+
1395
+ await paymentProvider.updateProduct(stripeProduct.id, {
1396
+ name: input.name,
1397
+ active: true,
1398
+ images: input.fields.image?.url ? [input.fields.image.url] : undefined,
1399
+ description: input.fields.description || '',
1400
+ metadata: {
1401
+ slug: input.fields.slug,
1402
+ },
1403
+ })
1404
+
1405
+ const { image, ...fieldsNoImage } = input.fields
1406
+
1407
+ await client
1408
+ .update(products)
1409
+ .set({
1410
+ name: input.name,
1411
+ quantityAvailable: input.quantityAvailable,
1412
+ status: 1,
1413
+ fields: {
1414
+ ...fieldsNoImage,
1415
+ ...(image?.url && { image }),
1416
+ },
1417
+ })
1418
+ .where(eq(products.id, currentProduct.id))
1419
+
1420
+ return adapter.getProduct(currentProduct.id)
1421
+ },
1422
+ async createProduct(input: NewProduct) {
1423
+ if (!paymentProvider) throw new Error('Payment provider not found')
1424
+ const merchantAccount = merchantAccountSchema.nullish().parse(
1425
+ await client.query.merchantAccount.findFirst({
1426
+ where: (merchantAccount, { eq }) =>
1427
+ eq(merchantAccount.label, 'stripe'),
1428
+ }),
1429
+ )
1430
+
1431
+ if (!merchantAccount) {
1432
+ throw new Error('Merchant account not found')
1433
+ }
1434
+
1435
+ const hash = guid()
1436
+ const newProductId = slugify(`product-${hash}`)
1437
+
1438
+ const newProduct = {
1439
+ id: newProductId,
1440
+ name: input.name,
1441
+ status: 1,
1442
+ type: 'self-paced',
1443
+ quantityAvailable: input.quantityAvailable,
1444
+ fields: {
1445
+ state: 'draft',
1446
+ visibility: 'unlisted',
1447
+ slug: slugify(`${input.name}-${hash}`),
1448
+ },
1449
+ }
1450
+
1451
+ await client.insert(products).values(newProduct)
1452
+
1453
+ const priceHash = guid()
1454
+ const newPriceId = `price-${priceHash}`
1455
+
1456
+ await client.insert(prices).values({
1457
+ id: newPriceId,
1458
+ productId: newProductId,
1459
+ unitAmount: input.price.toString(),
1460
+ status: 1,
1461
+ })
1462
+
1463
+ const product = await adapter.getProduct(newProductId)
1464
+
1465
+ const stripeProduct = await paymentProvider.createProduct({
1466
+ name: input.name,
1467
+ metadata: {
1468
+ slug: product?.fields?.slug || null,
1469
+ },
1470
+ })
1471
+
1472
+ const stripePrice = await paymentProvider.createPrice({
1473
+ product: stripeProduct.id,
1474
+ unit_amount: Math.floor(Number(input.price) * 100),
1475
+ currency: 'usd',
1476
+ nickname: input.name,
1477
+ metadata: {
1478
+ slug: product?.fields?.slug || null,
1479
+ },
1480
+ })
1481
+
1482
+ const newMerchantProductId = `mproduct_${v4()}`
1483
+
1484
+ await client.insert(merchantProduct).values({
1485
+ id: newMerchantProductId,
1486
+ merchantAccountId: merchantAccount.id,
1487
+ productId: newProductId,
1488
+ identifier: stripeProduct.id,
1489
+ status: 1,
1490
+ })
1491
+
1492
+ const newMerchantPriceId = `mprice_${v4()}`
1493
+ await client.insert(merchantPrice).values({
1494
+ id: newMerchantPriceId,
1495
+ merchantAccountId: merchantAccount.id,
1496
+ merchantProductId: newMerchantProductId,
1497
+ priceId: newPriceId,
1498
+ identifier: stripePrice.id,
1499
+ status: 1,
1500
+ })
1501
+
1502
+ // TODO: handle upgrades
1503
+
1504
+ return product
1505
+ },
1506
+ async getProduct(
1507
+ productSlugOrId?: string,
1508
+ withResources: boolean = true,
1509
+ ): Promise<Product | null> {
1510
+ if (!productSlugOrId) {
1511
+ return null
1512
+ }
1513
+
1514
+ try {
1515
+ const productData = await client.query.products.findFirst({
1516
+ where: and(
1517
+ or(
1518
+ eq(
1519
+ sql`JSON_EXTRACT (${products.fields}, "$.slug")`,
1520
+ `${productSlugOrId}`,
1521
+ ),
1522
+ eq(products.id, productSlugOrId),
1523
+ ),
1524
+ ),
1525
+ with: {
1526
+ price: true,
1527
+ ...(withResources && {
1528
+ resources: {
1529
+ with: {
1530
+ resource: {
1531
+ with: {
1532
+ resources: true,
1533
+ },
1534
+ },
1535
+ },
1536
+ },
1537
+ }),
1538
+ },
1539
+ })
1540
+ const parsedProduct = productSchema.safeParse(productData)
1541
+ if (!parsedProduct.success) {
1542
+ console.error(
1543
+ 'Error parsing product',
1544
+ JSON.stringify(parsedProduct.error),
1545
+ JSON.stringify(productData),
1546
+ )
1547
+ return null
1548
+ }
1549
+ return parsedProduct.data
1550
+ } catch (e) {
1551
+ console.log('getProduct error', e)
1552
+ return null
1553
+ }
1554
+ },
1555
+ async getProductResources(
1556
+ productId: string,
1557
+ ): Promise<ContentResource[] | null> {
1558
+ const contentResourceProductsForProduct = z
1559
+ .array(ContentResourceProductSchema)
1560
+ .nullable()
1561
+ .parse(
1562
+ await client.query.contentResourceProduct.findMany({
1563
+ where: eq(contentResourceProduct.productId, productId),
1564
+ }),
1565
+ )
1566
+
1567
+ if (!contentResourceProductsForProduct) {
1568
+ return null
1569
+ } else {
1570
+ const contentResources = z.array(ContentResourceSchema).parse(
1571
+ await client.query.contentResource.findMany({
1572
+ where: inArray(
1573
+ contentResource.id,
1574
+ contentResourceProductsForProduct.map((crp) => crp.resourceId),
1575
+ ),
1576
+ }),
1577
+ )
1578
+ return contentResources
1579
+ }
1580
+ },
1581
+ async getPurchaseCountForProduct(productId: string): Promise<number> {
1582
+ return await client.query.purchases
1583
+ .findMany({
1584
+ where: and(
1585
+ eq(purchaseTable.productId, productId),
1586
+ inArray(purchaseTable.status, ['Valid', 'Restricted']),
1587
+ ),
1588
+ })
1589
+ .then((res) => res.length)
1182
1590
  },
1183
1591
  async getPurchase(purchaseId: string): Promise<Purchase | null> {
1184
1592
  const purchase = await client.query.purchases.findFirst({
@@ -1211,7 +1619,11 @@ export function mySqlDrizzleAdapter(
1211
1619
  )
1212
1620
 
1213
1621
  if (!chargeForPurchase) {
1214
- logger.error(new Error('No charge found for purchase'))
1622
+ logger.error(
1623
+ new Error(
1624
+ `No charge found for purchase: Stripe Charge ID: ${stripeChargeId}`,
1625
+ ),
1626
+ )
1215
1627
  return null
1216
1628
  }
1217
1629
 
@@ -1421,6 +1833,34 @@ export function mySqlDrizzleAdapter(
1421
1833
  purchase: parsedPurchase.data,
1422
1834
  }
1423
1835
  },
1836
+ async getUserWithPurchasersByEmail(email: string): Promise<any | null> {
1837
+ const user = await client.query.users.findFirst({
1838
+ where: eq(users.email, email.trim().toLowerCase()),
1839
+ with: {
1840
+ // merchantCustomer: true,
1841
+ roles: {
1842
+ with: {
1843
+ role: true,
1844
+ },
1845
+ },
1846
+ purchases: {
1847
+ with: {
1848
+ bulkCoupon: true,
1849
+ merchantCharge: true,
1850
+ product: {
1851
+ columns: {
1852
+ id: true,
1853
+ name: true,
1854
+ key: true,
1855
+ },
1856
+ },
1857
+ },
1858
+ },
1859
+ },
1860
+ })
1861
+
1862
+ return user
1863
+ },
1424
1864
  async getUserById(userId: string): Promise<User | null> {
1425
1865
  const user = await client.query.users
1426
1866
  .findFirst({
@@ -1497,7 +1937,7 @@ export function mySqlDrizzleAdapter(
1497
1937
  let lessonProgress = await client.query.resourceProgress.findFirst({
1498
1938
  where: and(
1499
1939
  eq(resourceProgress.userId, options.userId),
1500
- eq(resourceProgress.contentResourceId, options.lessonId),
1940
+ eq(resourceProgress.resourceId, options.lessonId),
1501
1941
  ),
1502
1942
  })
1503
1943
 
@@ -1510,11 +1950,11 @@ export function mySqlDrizzleAdapter(
1510
1950
  completedAt: lessonProgress.completedAt ? null : now,
1511
1951
  updatedAt: now,
1512
1952
  })
1513
- .where(eq(resourceProgress.contentResourceId, options.lessonId))
1953
+ .where(eq(resourceProgress.resourceId, options.lessonId))
1514
1954
  } else {
1515
1955
  await client.insert(resourceProgress).values({
1516
1956
  userId: options.userId,
1517
- contentResourceId: options.lessonId,
1957
+ resourceId: options.lessonId,
1518
1958
  completedAt: now,
1519
1959
  updatedAt: now,
1520
1960
  })
@@ -1523,7 +1963,7 @@ export function mySqlDrizzleAdapter(
1523
1963
  lessonProgress = await client.query.resourceProgress.findFirst({
1524
1964
  where: and(
1525
1965
  eq(resourceProgress.userId, options.userId),
1526
- eq(resourceProgress.contentResourceId, options.lessonId),
1966
+ eq(resourceProgress.resourceId, options.lessonId),
1527
1967
  ),
1528
1968
  })
1529
1969
 
@@ -1543,11 +1983,56 @@ export function mySqlDrizzleAdapter(
1543
1983
  }): Promise<unknown> {
1544
1984
  throw new Error('Method not implemented.')
1545
1985
  },
1546
- updatePurchaseStatusForCharge(
1986
+ async updatePurchaseStatusForCharge(
1547
1987
  chargeId: string,
1548
1988
  status: 'Valid' | 'Refunded' | 'Disputed' | 'Banned' | 'Restricted',
1549
1989
  ): Promise<Purchase | undefined> {
1550
- throw new Error('updatePurchaseStatusForCharge Method not implemented.')
1990
+ try {
1991
+ console.log('updatePurchaseStatusForCharge', { chargeId, status })
1992
+ const merchantChargeForPurchase =
1993
+ await client.query.merchantCharge.findFirst({
1994
+ where: or(
1995
+ eq(merchantCharge.identifier, chargeId),
1996
+ eq(merchantCharge.id, chargeId),
1997
+ ),
1998
+ })
1999
+
2000
+ console.log('merchantChargeForPurchase', { merchantChargeForPurchase })
2001
+
2002
+ const parsedMerchantChargeForPurchase = merchantChargeSchema.parse(
2003
+ merchantChargeForPurchase,
2004
+ )
2005
+
2006
+ if (!parsedMerchantChargeForPurchase)
2007
+ throw new Error(`no-charge-found-for-purchase ${chargeId}`)
2008
+
2009
+ const purchase = await client.query.purchases.findFirst({
2010
+ where: eq(
2011
+ purchaseTable.merchantChargeId,
2012
+ parsedMerchantChargeForPurchase.id,
2013
+ ),
2014
+ })
2015
+
2016
+ const parsedPurchase = purchaseSchema.nullable().parse(purchase)
2017
+
2018
+ if (parsedPurchase) {
2019
+ await client
2020
+ .update(purchaseTable)
2021
+ .set({ status: status })
2022
+ .where(eq(purchaseTable.id, parsedPurchase.id))
2023
+
2024
+ const newPurchase = await client.query.purchases.findFirst({
2025
+ where: eq(purchaseTable.id, parsedPurchase.id),
2026
+ })
2027
+
2028
+ return purchaseSchema.optional().parse(newPurchase)
2029
+ } else {
2030
+ throw new Error(`no-purchase-found-for-charge ${chargeId}`)
2031
+ }
2032
+ } catch (e) {
2033
+ console.log('error updating purchase status', e)
2034
+ throw e
2035
+ }
1551
2036
  },
1552
2037
  async updatePurchaseUserTransferTransferState(options: {
1553
2038
  id: string
@@ -1611,6 +2096,35 @@ export function mySqlDrizzleAdapter(
1611
2096
 
1612
2097
  return parsedResourceJoin.data
1613
2098
  },
2099
+ async removeResourceFromResource(options) {
2100
+ const { childResourceId, parentResourceId } = options
2101
+
2102
+ const resourceJoin = await client.query.contentResourceResource.findFirst(
2103
+ {
2104
+ where: and(
2105
+ eq(contentResourceResource.resourceOfId, parentResourceId),
2106
+ eq(contentResourceResource.resourceId, childResourceId),
2107
+ ),
2108
+ },
2109
+ )
2110
+
2111
+ const parsedResourceJoin =
2112
+ ContentResourceResourceSchema.safeParse(resourceJoin)
2113
+ if (!parsedResourceJoin.success) {
2114
+ return null
2115
+ }
2116
+
2117
+ await client
2118
+ .delete(contentResourceResource)
2119
+ .where(
2120
+ and(
2121
+ eq(contentResourceResource.resourceOfId, parentResourceId),
2122
+ eq(contentResourceResource.resourceId, childResourceId),
2123
+ ),
2124
+ )
2125
+
2126
+ return parsedResourceJoin.data.resource
2127
+ },
1614
2128
  async updateContentResourceFields(options) {
1615
2129
  if (!options.id) {
1616
2130
  throw new Error('No content resource id.')
@@ -1741,7 +2255,15 @@ export function mySqlDrizzleAdapter(
1741
2255
  with: {
1742
2256
  resources: {
1743
2257
  with: {
1744
- resource: true,
2258
+ resource: {
2259
+ with: {
2260
+ resources: {
2261
+ with: {
2262
+ resource: true,
2263
+ },
2264
+ },
2265
+ },
2266
+ },
1745
2267
  },
1746
2268
  orderBy: asc(contentResourceResource.position),
1747
2269
  },
@@ -1864,7 +2386,7 @@ export function mySqlDrizzleAdapter(
1864
2386
  return null
1865
2387
  }
1866
2388
 
1867
- return dbAccount.user
2389
+ return dbAccount.User
1868
2390
  },
1869
2391
  async deleteSession(sessionToken) {
1870
2392
  const session =