@coursebuilder/adapter-drizzle 0.0.6 → 0.1.0

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 (161) hide show
  1. package/dist/{chunk-ICL7XZ2Y.js → chunk-27KREOQW.js} +4 -4
  2. package/dist/{chunk-KVN3JV7C.js → chunk-44VDBYFU.js} +6 -6
  3. package/dist/{chunk-VEFCDI73.js → chunk-75V6F7LZ.js} +12 -12
  4. package/dist/{chunk-SQR6UWVO.js → chunk-7LAAAEVE.js} +165 -11
  5. package/dist/{chunk-TA6VLEV5.js → chunk-AFQGYW7Z.js} +4 -4
  6. package/dist/{chunk-US2EXVMI.js → chunk-CPE4P26C.js} +4 -4
  7. package/dist/chunk-GVURJJRV.js +4679 -0
  8. package/dist/{chunk-VZABQCFH.js → chunk-IN6G6MTW.js} +4 -4
  9. package/dist/{chunk-4HDU7QIB.js → chunk-ISURSRBK.js} +345 -324
  10. package/dist/{chunk-AIDEJDKJ.js → chunk-JKRFXYAR.js} +4 -4
  11. package/dist/{chunk-KI47TR7F.js → chunk-L2TQQYG4.js} +4 -4
  12. package/dist/{chunk-UY3DVPP3.js → chunk-LSOPFMDB.js} +4 -4
  13. package/dist/{chunk-PFN4YUQZ.js → chunk-MYRB6GLJ.js} +4 -3
  14. package/dist/chunk-NZDLUQWP.js +44 -0
  15. package/dist/{chunk-22KIKVBY.js → chunk-O2KKII62.js} +163 -10
  16. package/dist/{chunk-MBQ3TMHH.js → chunk-OIUOQ3TT.js} +4 -4
  17. package/dist/chunk-QZOBGJSV.js +78 -0
  18. package/dist/chunk-RVYJDDZD.js +75 -0
  19. package/dist/chunk-V6QEDDFG.js +49 -0
  20. package/dist/{chunk-FRNVLW7N.js → chunk-Y34AIPU7.js} +34 -303
  21. package/dist/index.js +21 -17
  22. package/dist/lib/mysql/index.cjs +5896 -1367
  23. package/dist/lib/mysql/index.cjs.map +1 -1
  24. package/dist/lib/mysql/index.d.ts +325 -129
  25. package/dist/lib/mysql/index.js +19 -15
  26. package/dist/lib/mysql/schemas/auth/accounts.cjs +73 -57
  27. package/dist/lib/mysql/schemas/auth/accounts.cjs.map +1 -1
  28. package/dist/lib/mysql/schemas/auth/accounts.js +7 -1
  29. package/dist/lib/mysql/schemas/auth/permissions.cjs +9 -98
  30. package/dist/lib/mysql/schemas/auth/permissions.cjs.map +1 -1
  31. package/dist/lib/mysql/schemas/auth/permissions.d.ts +1 -3
  32. package/dist/lib/mysql/schemas/auth/permissions.js +1 -3
  33. package/dist/lib/mysql/schemas/auth/role-permissions.cjs +64 -43
  34. package/dist/lib/mysql/schemas/auth/role-permissions.cjs.map +1 -1
  35. package/dist/lib/mysql/schemas/auth/role-permissions.js +8 -1
  36. package/dist/lib/mysql/schemas/auth/roles.cjs +63 -46
  37. package/dist/lib/mysql/schemas/auth/roles.cjs.map +1 -1
  38. package/dist/lib/mysql/schemas/auth/roles.js +7 -1
  39. package/dist/lib/mysql/schemas/auth/sessions.cjs +63 -47
  40. package/dist/lib/mysql/schemas/auth/sessions.cjs.map +1 -1
  41. package/dist/lib/mysql/schemas/auth/sessions.js +8 -2
  42. package/dist/lib/mysql/schemas/auth/user-permissions.cjs +97 -82
  43. package/dist/lib/mysql/schemas/auth/user-permissions.cjs.map +1 -1
  44. package/dist/lib/mysql/schemas/auth/user-permissions.js +7 -1
  45. package/dist/lib/mysql/schemas/auth/user-roles.cjs +65 -48
  46. package/dist/lib/mysql/schemas/auth/user-roles.cjs.map +1 -1
  47. package/dist/lib/mysql/schemas/auth/user-roles.js +7 -1
  48. package/dist/lib/mysql/schemas/auth/users.cjs +233 -132
  49. package/dist/lib/mysql/schemas/auth/users.cjs.map +1 -1
  50. package/dist/lib/mysql/schemas/auth/users.d.ts +2 -1
  51. package/dist/lib/mysql/schemas/auth/users.js +7 -1
  52. package/dist/lib/mysql/schemas/commerce/coupon.cjs +4 -4
  53. package/dist/lib/mysql/schemas/commerce/coupon.cjs.map +1 -1
  54. package/dist/lib/mysql/schemas/commerce/coupon.d.ts +6 -6
  55. package/dist/lib/mysql/schemas/commerce/coupon.js +1 -1
  56. package/dist/lib/mysql/schemas/commerce/merchant-account.cjs +2 -2
  57. package/dist/lib/mysql/schemas/commerce/merchant-account.cjs.map +1 -1
  58. package/dist/lib/mysql/schemas/commerce/merchant-account.d.ts +3 -3
  59. package/dist/lib/mysql/schemas/commerce/merchant-account.js +1 -1
  60. package/dist/lib/mysql/schemas/commerce/merchant-charge.cjs +2 -2
  61. package/dist/lib/mysql/schemas/commerce/merchant-charge.cjs.map +1 -1
  62. package/dist/lib/mysql/schemas/commerce/merchant-charge.d.ts +3 -3
  63. package/dist/lib/mysql/schemas/commerce/merchant-charge.js +1 -1
  64. package/dist/lib/mysql/schemas/commerce/merchant-customer.cjs +2 -2
  65. package/dist/lib/mysql/schemas/commerce/merchant-customer.cjs.map +1 -1
  66. package/dist/lib/mysql/schemas/commerce/merchant-customer.d.ts +3 -3
  67. package/dist/lib/mysql/schemas/commerce/merchant-customer.js +1 -1
  68. package/dist/lib/mysql/schemas/commerce/merchant-price.cjs +2 -2
  69. package/dist/lib/mysql/schemas/commerce/merchant-price.cjs.map +1 -1
  70. package/dist/lib/mysql/schemas/commerce/merchant-price.d.ts +3 -3
  71. package/dist/lib/mysql/schemas/commerce/merchant-price.js +1 -1
  72. package/dist/lib/mysql/schemas/commerce/merchant-product.cjs +2 -2
  73. package/dist/lib/mysql/schemas/commerce/merchant-product.cjs.map +1 -1
  74. package/dist/lib/mysql/schemas/commerce/merchant-product.d.ts +3 -3
  75. package/dist/lib/mysql/schemas/commerce/merchant-product.js +1 -1
  76. package/dist/lib/mysql/schemas/commerce/price.cjs +2 -2
  77. package/dist/lib/mysql/schemas/commerce/price.cjs.map +1 -1
  78. package/dist/lib/mysql/schemas/commerce/price.d.ts +3 -3
  79. package/dist/lib/mysql/schemas/commerce/price.js +1 -1
  80. package/dist/lib/mysql/schemas/commerce/product.cjs +2 -2
  81. package/dist/lib/mysql/schemas/commerce/product.cjs.map +1 -1
  82. package/dist/lib/mysql/schemas/commerce/product.d.ts +3 -3
  83. package/dist/lib/mysql/schemas/commerce/product.js +1 -1
  84. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs +10 -10
  85. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs.map +1 -1
  86. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.d.ts +15 -15
  87. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.js +1 -1
  88. package/dist/lib/mysql/schemas/commerce/purchase.cjs +320 -21
  89. package/dist/lib/mysql/schemas/commerce/purchase.cjs.map +1 -1
  90. package/dist/lib/mysql/schemas/commerce/purchase.d.ts +12 -4
  91. package/dist/lib/mysql/schemas/commerce/purchase.js +11 -1
  92. package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs +130 -0
  93. package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs.map +1 -0
  94. package/dist/lib/mysql/schemas/commerce/upgradable-products.d.ts +101 -0
  95. package/dist/lib/mysql/schemas/commerce/upgradable-products.js +11 -0
  96. package/dist/lib/mysql/schemas/commerce/upgradable-products.js.map +1 -0
  97. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs +90 -72
  98. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs.map +1 -1
  99. package/dist/lib/mysql/schemas/communication/communication-preferences.js +7 -1
  100. package/dist/lib/mysql/schemas/content/content-contributions.cjs +94 -76
  101. package/dist/lib/mysql/schemas/content/content-contributions.cjs.map +1 -1
  102. package/dist/lib/mysql/schemas/content/content-contributions.js +7 -1
  103. package/dist/lib/mysql/schemas/content/content-resource-resource.cjs +68 -53
  104. package/dist/lib/mysql/schemas/content/content-resource-resource.cjs.map +1 -1
  105. package/dist/lib/mysql/schemas/content/content-resource-resource.d.ts +1 -1
  106. package/dist/lib/mysql/schemas/content/content-resource-resource.js +7 -1
  107. package/dist/lib/mysql/schemas/content/content-resource.cjs +81 -103
  108. package/dist/lib/mysql/schemas/content/content-resource.cjs.map +1 -1
  109. package/dist/lib/mysql/schemas/content/content-resource.d.ts +0 -1
  110. package/dist/lib/mysql/schemas/content/content-resource.js +7 -1
  111. package/dist/lib/mysql/schemas/content/contribution-types.cjs +11 -102
  112. package/dist/lib/mysql/schemas/content/contribution-types.cjs.map +1 -1
  113. package/dist/lib/mysql/schemas/content/contribution-types.d.ts +1 -3
  114. package/dist/lib/mysql/schemas/content/contribution-types.js +1 -3
  115. package/dist/lib/mysql/schemas/content/resource-progress.cjs +3 -3
  116. package/dist/lib/mysql/schemas/content/resource-progress.cjs.map +1 -1
  117. package/dist/lib/mysql/schemas/content/resource-progress.d.ts +9 -9
  118. package/dist/lib/mysql/schemas/content/resource-progress.js +1 -1
  119. package/dist/lib/mysql/utils.cjs +51 -0
  120. package/dist/lib/mysql/utils.cjs.map +1 -0
  121. package/dist/lib/mysql/utils.d.ts +12 -0
  122. package/dist/lib/mysql/utils.js +29 -0
  123. package/dist/lib/mysql/utils.js.map +1 -0
  124. package/dist/lib/pg.cjs +4289 -9
  125. package/dist/lib/pg.cjs.map +1 -1
  126. package/dist/lib/pg.d.ts +91 -0
  127. package/dist/lib/pg.js +2 -1
  128. package/dist/lib/sqlite.cjs +4285 -8
  129. package/dist/lib/sqlite.cjs.map +1 -1
  130. package/dist/lib/sqlite.js +2 -1
  131. package/package.json +7 -5
  132. package/src/lib/mysql/index.ts +688 -34
  133. package/src/lib/mysql/schemas/auth/accounts.ts +5 -1
  134. package/src/lib/mysql/schemas/auth/permissions.ts +1 -3
  135. package/src/lib/mysql/schemas/auth/role-permissions.ts +2 -0
  136. package/src/lib/mysql/schemas/auth/roles.ts +1 -1
  137. package/src/lib/mysql/schemas/auth/sessions.ts +5 -1
  138. package/src/lib/mysql/schemas/auth/user-permissions.ts +2 -0
  139. package/src/lib/mysql/schemas/auth/user-roles.ts +2 -0
  140. package/src/lib/mysql/schemas/auth/users.ts +23 -6
  141. package/src/lib/mysql/schemas/commerce/coupon.ts +3 -3
  142. package/src/lib/mysql/schemas/commerce/merchant-account.ts +2 -2
  143. package/src/lib/mysql/schemas/commerce/merchant-charge.ts +2 -2
  144. package/src/lib/mysql/schemas/commerce/merchant-customer.ts +2 -2
  145. package/src/lib/mysql/schemas/commerce/merchant-price.ts +2 -2
  146. package/src/lib/mysql/schemas/commerce/merchant-product.ts +2 -2
  147. package/src/lib/mysql/schemas/commerce/price.ts +2 -2
  148. package/src/lib/mysql/schemas/commerce/product.ts +2 -2
  149. package/src/lib/mysql/schemas/commerce/purchase-user-transfer.ts +6 -6
  150. package/src/lib/mysql/schemas/commerce/purchase.ts +49 -3
  151. package/src/lib/mysql/schemas/commerce/upgradable-products.ts +58 -0
  152. package/src/lib/mysql/schemas/communication/communication-preferences.ts +3 -0
  153. package/src/lib/mysql/schemas/content/content-contributions.ts +3 -0
  154. package/src/lib/mysql/schemas/content/content-resource-resource.ts +1 -1
  155. package/src/lib/mysql/schemas/content/content-resource.ts +1 -2
  156. package/src/lib/mysql/schemas/content/contribution-types.ts +1 -4
  157. package/src/lib/mysql/schemas/content/resource-progress.ts +3 -3
  158. package/src/lib/mysql/utils.ts +23 -0
  159. package/src/lib/pg.ts +298 -16
  160. package/src/lib/sqlite.ts +290 -15
  161. package/dist/chunk-BRGTUNY2.js +0 -78
@@ -1,13 +1,40 @@
1
1
  import type { AdapterSession, AdapterUser } from '@auth/core/adapters'
2
2
  import { addSeconds, isAfter } from 'date-fns'
3
- import { and, eq, sql } from 'drizzle-orm'
3
+ import { and, asc, eq, inArray, not, notInArray, or, sql } from 'drizzle-orm'
4
4
  import {
5
5
  mysqlTable as defaultMySqlTableFn,
6
6
  MySqlDatabase,
7
7
  MySqlTableFn,
8
8
  } from 'drizzle-orm/mysql-core'
9
+ import { v4 } from 'uuid'
10
+ import { z } from 'zod'
9
11
 
10
12
  import { type CourseBuilderAdapter } from '@coursebuilder/core/adapters'
13
+ import {
14
+ Coupon,
15
+ couponSchema,
16
+ MerchantCharge,
17
+ merchantChargeSchema,
18
+ MerchantCoupon,
19
+ merchantCouponSchema,
20
+ MerchantCustomer,
21
+ MerchantProduct,
22
+ Price,
23
+ Product,
24
+ Purchase,
25
+ purchaseSchema,
26
+ PurchaseUserTransfer,
27
+ PurchaseUserTransferState,
28
+ ResourceProgress,
29
+ UpgradableProduct,
30
+ upgradableProductSchema,
31
+ User,
32
+ userSchema,
33
+ } from '@coursebuilder/core/schemas'
34
+ import {
35
+ ContentResourceResourceSchema,
36
+ ContentResourceSchema,
37
+ } from '@coursebuilder/core/schemas/content-resource-schema'
11
38
  import { VideoResourceSchema } from '@coursebuilder/core/schemas/video-resource'
12
39
 
13
40
  import {
@@ -54,7 +81,14 @@ import { getMerchantSessionSchema } from './schemas/commerce/merchant-session.js
54
81
  import { getPriceSchema } from './schemas/commerce/price.js'
55
82
  import { getProductSchema } from './schemas/commerce/product.js'
56
83
  import { getPurchaseUserTransferSchema } from './schemas/commerce/purchase-user-transfer.js'
57
- import { getPurchaseSchema } from './schemas/commerce/purchase.js'
84
+ import {
85
+ getPurchaseRelationsSchema,
86
+ getPurchaseSchema,
87
+ } from './schemas/commerce/purchase.js'
88
+ import {
89
+ getUpgradableProductsRelationsSchema,
90
+ getUpgradableProductsSchema,
91
+ } from './schemas/commerce/upgradable-products.js'
58
92
  import { getCommunicationChannelSchema } from './schemas/communication/communication-channel.js'
59
93
  import { getCommunicationPreferenceTypesSchema } from './schemas/communication/communication-preference-types.js'
60
94
  import {
@@ -106,9 +140,10 @@ export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
106
140
  merchantPrice: getMerchantPriceSchema(mysqlTable),
107
141
  merchantProduct: getMerchantProductSchema(mysqlTable),
108
142
  merchantSession: getMerchantSessionSchema(mysqlTable),
109
- price: getPriceSchema(mysqlTable),
110
- product: getProductSchema(mysqlTable),
111
- purchase: getPurchaseSchema(mysqlTable),
143
+ prices: getPriceSchema(mysqlTable),
144
+ products: getProductSchema(mysqlTable),
145
+ purchases: getPurchaseSchema(mysqlTable),
146
+ purchaseRelations: getPurchaseRelationsSchema(mysqlTable),
112
147
  purchaseUserTransfer: getPurchaseUserTransferSchema(mysqlTable),
113
148
  communicationChannel: getCommunicationChannelSchema(mysqlTable),
114
149
  communicationPreferenceTypes:
@@ -127,6 +162,9 @@ export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
127
162
  contributionTypes: getContributionTypesSchema(mysqlTable),
128
163
  contributionTypesRelations: getContributionTypesRelationsSchema(mysqlTable),
129
164
  resourceProgress: getResourceProgressSchema(mysqlTable),
165
+ upgradableProducts: getUpgradableProductsSchema(mysqlTable),
166
+ upgradableProductsRelations:
167
+ getUpgradableProductsRelationsSchema(mysqlTable),
130
168
  } as const
131
169
  }
132
170
 
@@ -139,7 +177,7 @@ export type DefaultSchema = ReturnType<typeof createTables>
139
177
  export function mySqlDrizzleAdapter(
140
178
  client: InstanceType<typeof MySqlDatabase>,
141
179
  tableFn = defaultMySqlTableFn,
142
- ): CourseBuilderAdapter {
180
+ ): CourseBuilderAdapter<typeof MySqlDatabase> {
143
181
  const {
144
182
  users,
145
183
  accounts,
@@ -147,7 +185,7 @@ export function mySqlDrizzleAdapter(
147
185
  verificationTokens,
148
186
  contentResource,
149
187
  contentResourceResource,
150
- purchase,
188
+ purchases: purchaseTable,
151
189
  purchaseUserTransfer,
152
190
  coupon,
153
191
  merchantCoupon,
@@ -157,11 +195,551 @@ export function mySqlDrizzleAdapter(
157
195
  merchantCustomer,
158
196
  merchantSession,
159
197
  merchantProduct,
160
- price,
161
- product,
198
+ prices,
199
+ products,
200
+ upgradableProducts,
162
201
  } = createTables(tableFn)
163
202
 
164
203
  return {
204
+ client,
205
+ availableUpgradesForProduct(
206
+ purchases: any,
207
+ productId: string,
208
+ ): Promise<
209
+ {
210
+ upgradableTo: { id: string; name: string }
211
+ upgradableFrom: { id: string; name: string }
212
+ }[]
213
+ > {
214
+ throw new Error('Method not implemented.')
215
+ },
216
+ clearLessonProgressForUser(options: {
217
+ userId: string
218
+ lessons: { id: string; slug: string }[]
219
+ }): Promise<void> {
220
+ throw new Error('Method not implemented.')
221
+ },
222
+ completeLessonProgressForUser(options: {
223
+ userId: string
224
+ lessonId?: string
225
+ }): Promise<ResourceProgress | null> {
226
+ throw new Error('Method not implemented.')
227
+ },
228
+ couponForIdOrCode(options: {
229
+ code?: string
230
+ couponId?: string
231
+ }): Promise<(Coupon & { merchantCoupon: MerchantCoupon }) | null> {
232
+ throw new Error('Method not implemented.')
233
+ },
234
+ async createMerchantChargeAndPurchase(options): Promise<Purchase> {
235
+ const purchaseId = await client.transaction(async (trx) => {
236
+ try {
237
+ const {
238
+ userId,
239
+ stripeChargeId,
240
+ stripeCouponId,
241
+ merchantAccountId,
242
+ merchantProductId,
243
+ merchantCustomerId,
244
+ productId,
245
+ stripeChargeAmount,
246
+ quantity = 1,
247
+ checkoutSessionId,
248
+ appliedPPPStripeCouponId,
249
+ upgradedFromPurchaseId,
250
+ country,
251
+ usedCouponId,
252
+ } = options
253
+
254
+ const existingMerchantCharge = merchantChargeSchema.nullable().parse(
255
+ (await client.query.merchantCharge.findFirst({
256
+ where: eq(merchantCharge.identifier, stripeChargeId),
257
+ })) || null,
258
+ )
259
+
260
+ const existingPurchaseForCharge = existingMerchantCharge
261
+ ? await client.query.purchase.findFirst({
262
+ where: eq(
263
+ purchaseTable.merchantChargeId,
264
+ existingMerchantCharge.id,
265
+ ),
266
+ with: {
267
+ user: true,
268
+ product: true,
269
+ bulkCoupon: true,
270
+ },
271
+ })
272
+ : null
273
+
274
+ if (existingPurchaseForCharge) {
275
+ return purchaseSchema.parse(existingPurchaseForCharge)
276
+ }
277
+
278
+ const merchantChargeId = v4()
279
+ const purchaseId = v4()
280
+
281
+ const newMerchantCharge = trx.insert(merchantCharge).values({
282
+ id: merchantChargeId,
283
+ userId,
284
+ identifier: stripeChargeId,
285
+ merchantAccountId,
286
+ merchantProductId,
287
+ merchantCustomerId,
288
+ })
289
+
290
+ const existingPurchase = purchaseSchema.nullable().parse(
291
+ (await client.query.purchases.findFirst({
292
+ where: and(
293
+ eq(purchaseTable.productId, productId),
294
+ eq(purchaseTable.userId, userId),
295
+ inArray(purchaseTable.status, ['Valid', 'Restricted']),
296
+ ),
297
+ })) || null,
298
+ )
299
+
300
+ const existingBulkCoupon = couponSchema.nullable().parse(
301
+ await client
302
+ .select()
303
+ .from(coupon)
304
+ .leftJoin(
305
+ purchaseTable,
306
+ and(
307
+ eq(coupon.id, purchaseTable.bulkCouponId),
308
+ eq(purchaseTable.userId, userId),
309
+ ),
310
+ )
311
+ .then((res) => res[0] ?? null),
312
+ )
313
+
314
+ const isBulkPurchase =
315
+ quantity > 1 ||
316
+ Boolean(existingBulkCoupon) ||
317
+ options.bulk ||
318
+ Boolean(existingPurchase?.status === 'Valid')
319
+
320
+ let bulkCouponId: string | null = null
321
+ let couponToUpdate = null
322
+
323
+ if (isBulkPurchase) {
324
+ bulkCouponId =
325
+ existingBulkCoupon !== null ? existingBulkCoupon.id : v4()
326
+
327
+ if (existingBulkCoupon !== null) {
328
+ couponToUpdate = trx
329
+ .update(coupon)
330
+ .set({
331
+ maxUses: (existingBulkCoupon?.maxUses || 0) + quantity,
332
+ })
333
+ .where(eq(coupon.id, bulkCouponId))
334
+ } else {
335
+ const merchantCouponToUse = stripeCouponId
336
+ ? merchantCouponSchema.nullable().parse(
337
+ await client.query.merchantCoupon.findFirst({
338
+ where: eq(merchantCoupon.identifier, stripeCouponId),
339
+ }),
340
+ )
341
+ : null
342
+
343
+ couponToUpdate = await trx.insert(coupon).values({
344
+ id: bulkCouponId as string,
345
+ percentageDiscount: '1.0',
346
+ restrictedToProductId: productId,
347
+ maxUses: quantity,
348
+ status: 1,
349
+ ...(merchantCouponToUse
350
+ ? {
351
+ merchantCouponId: merchantCouponToUse.id,
352
+ }
353
+ : {}),
354
+ })
355
+ }
356
+ }
357
+
358
+ const merchantSessionId = v4()
359
+
360
+ const newMerchantSession = trx.insert(merchantSession).values({
361
+ id: merchantSessionId,
362
+ identifier: checkoutSessionId,
363
+ merchantAccountId,
364
+ })
365
+
366
+ const merchantCouponUsed = stripeCouponId
367
+ ? await client.query.merchantCoupon.findFirst({
368
+ where: eq(merchantCoupon.identifier, stripeCouponId),
369
+ })
370
+ : null
371
+
372
+ const pppMerchantCoupon = appliedPPPStripeCouponId
373
+ ? await client.query.merchantCoupon.findFirst({
374
+ where: and(
375
+ eq(merchantCoupon.identifier, appliedPPPStripeCouponId),
376
+ eq(merchantCoupon.type, 'ppp'),
377
+ ),
378
+ })
379
+ : null
380
+
381
+ const newPurchaseStatus =
382
+ merchantCouponUsed?.type === 'ppp' || pppMerchantCoupon
383
+ ? 'Restricted'
384
+ : 'Valid'
385
+
386
+ const newPurchase = trx.insert(purchaseTable).values({
387
+ id: purchaseId,
388
+ status: newPurchaseStatus,
389
+ userId,
390
+ productId,
391
+ merchantChargeId,
392
+ totalAmount: (stripeChargeAmount / 100).toFixed(),
393
+ bulkCouponId,
394
+ merchantSessionId,
395
+ country,
396
+ upgradedFromId: upgradedFromPurchaseId || null,
397
+ couponId: usedCouponId || null,
398
+ })
399
+
400
+ const oneWeekInMilliseconds = 1000 * 60 * 60 * 24 * 7
401
+
402
+ const newPurchaseUserTransfer = trx
403
+ .insert(purchaseUserTransfer)
404
+ .values({
405
+ id: v4(),
406
+ purchaseId: purchaseId as string,
407
+ expiresAt: existingPurchase
408
+ ? new Date()
409
+ : new Date(Date.now() + oneWeekInMilliseconds),
410
+ sourceUserId: userId,
411
+ })
412
+
413
+ await Promise.all([
414
+ newMerchantCharge,
415
+ newPurchase,
416
+ newPurchaseUserTransfer,
417
+ newMerchantSession,
418
+ ...(couponToUpdate ? [couponToUpdate] : []),
419
+ ])
420
+
421
+ return purchaseId
422
+ } catch (error) {
423
+ console.error(error)
424
+ trx.rollback()
425
+ throw error
426
+ }
427
+ })
428
+
429
+ return purchaseSchema.parse(
430
+ await client.query.purchases.findFirst({
431
+ where: eq(purchaseTable.id, purchaseId as string),
432
+ }),
433
+ )
434
+ },
435
+ findOrCreateMerchantCustomer(options: {
436
+ user: User
437
+ identifier: string
438
+ merchantAccountId: string
439
+ }): Promise<MerchantCustomer | null> {
440
+ throw new Error('Method not implemented.')
441
+ },
442
+ findOrCreateUser(
443
+ email: string,
444
+ name?: string | null,
445
+ ): Promise<{
446
+ user: User
447
+ isNewUser: boolean
448
+ }> {
449
+ throw new Error('Method not implemented.')
450
+ },
451
+ getCoupon(couponIdOrCode: string): Promise<Coupon | null> {
452
+ throw new Error('Method not implemented.')
453
+ },
454
+ getCouponWithBulkPurchases(couponId: string): Promise<
455
+ | (Coupon & {
456
+ bulkCouponPurchases: { bulkCouponId: string }[]
457
+ })
458
+ | null
459
+ > {
460
+ throw new Error('Method not implemented.')
461
+ },
462
+ getDefaultCoupon(productIds?: string[]): Promise<{
463
+ defaultMerchantCoupon: MerchantCoupon
464
+ defaultCoupon: Coupon
465
+ } | null> {
466
+ throw new Error('Method not implemented.')
467
+ },
468
+ getLessonProgressCountsByDate(): Promise<
469
+ {
470
+ count: number
471
+ completedAt: string
472
+ }[]
473
+ > {
474
+ throw new Error('Method not implemented.')
475
+ },
476
+ getLessonProgressForUser(userId: string): Promise<ResourceProgress[]> {
477
+ throw new Error('Method not implemented.')
478
+ },
479
+ getLessonProgresses(): Promise<ResourceProgress[]> {
480
+ throw new Error('Method not implemented.')
481
+ },
482
+ getMerchantCharge(
483
+ merchantChargeId: string,
484
+ ): Promise<MerchantCharge | null> {
485
+ throw new Error('Method not implemented.')
486
+ },
487
+ getMerchantCoupon(
488
+ merchantCouponId: string,
489
+ ): Promise<MerchantCoupon | null> {
490
+ throw new Error('Method not implemented.')
491
+ },
492
+ getMerchantProduct(
493
+ stripeProductId: string,
494
+ ): Promise<MerchantProduct | null> {
495
+ throw new Error('Method not implemented.')
496
+ },
497
+ getPrice(productId: string): Promise<Price | null> {
498
+ throw new Error('Method not implemented.')
499
+ },
500
+ getProduct(productId: string): Promise<Product | null> {
501
+ throw new Error('Method not implemented.')
502
+ },
503
+ async getPurchase(purchaseId: string): Promise<Purchase | null> {
504
+ const purchase = await client.query.purchases.findFirst({
505
+ where: eq(purchaseTable.id, purchaseId),
506
+ })
507
+
508
+ return purchase ? purchaseSchema.parse(purchase) : null
509
+ },
510
+ async createPurchase(options): Promise<Purchase> {
511
+ const newPurchaseId = options.id || `purchase-${v4()}`
512
+ await client.insert(purchaseTable).values({
513
+ state: 'Valid',
514
+ ...options,
515
+ id: newPurchaseId,
516
+ })
517
+
518
+ const newPurchase = await this.getPurchase(newPurchaseId)
519
+
520
+ return purchaseSchema.parse(newPurchase)
521
+ },
522
+ async getPurchaseDetails(
523
+ purchaseId: string,
524
+ userId: string,
525
+ ): Promise<{
526
+ purchase?: Purchase
527
+ existingPurchase?: Purchase
528
+ availableUpgrades: UpgradableProduct[]
529
+ }> {
530
+ const allPurchases = await this.getPurchasesForUser(userId)
531
+ const thePurchase = await client.query.purchases.findFirst({
532
+ where: and(
533
+ eq(purchaseTable.id, purchaseId),
534
+ eq(purchaseTable.userId, userId),
535
+ ),
536
+ with: {
537
+ user: true,
538
+ product: true,
539
+ bulkCoupon: true,
540
+ },
541
+ })
542
+
543
+ const parsedPurchase = purchaseSchema.safeParse(thePurchase)
544
+
545
+ if (!parsedPurchase.success) {
546
+ console.error('Error parsing purchase', parsedPurchase)
547
+ return {
548
+ availableUpgrades: [],
549
+ }
550
+ }
551
+
552
+ const purchaseCanUpgrade = ['Valid', 'Restricted'].includes(
553
+ parsedPurchase.data.state || '',
554
+ )
555
+
556
+ if (!purchaseCanUpgrade) {
557
+ return {
558
+ availableUpgrades: [],
559
+ }
560
+ }
561
+
562
+ const availableUpgrades = await client.query.upgradableProducts.findMany({
563
+ where: and(
564
+ eq(
565
+ upgradableProducts.upgradableFromId,
566
+ parsedPurchase.data.product?.id as string,
567
+ ),
568
+ not(
569
+ inArray(
570
+ upgradableProducts.upgradableToId,
571
+ allPurchases.map((p) => p.product?.id as string),
572
+ ),
573
+ ),
574
+ ),
575
+ with: {
576
+ upgradableTo: true,
577
+ upgradableFrom: true,
578
+ },
579
+ })
580
+
581
+ const existingPurchase = allPurchases.find(
582
+ (p) => p.product?.id === parsedPurchase.data.product?.id,
583
+ )
584
+
585
+ return Promise.resolve({
586
+ availableUpgrades: z
587
+ .array(upgradableProductSchema)
588
+ .parse(availableUpgrades),
589
+ existingPurchase,
590
+ purchase: parsedPurchase.data,
591
+ })
592
+ },
593
+ async getPurchaseForStripeCharge(
594
+ stripeChargeId: string,
595
+ ): Promise<Purchase | null> {
596
+ const purchase = purchaseSchema.safeParse(
597
+ await client
598
+ .select()
599
+ .from(purchaseTable)
600
+ .leftJoin(
601
+ merchantCharge,
602
+ and(
603
+ eq(merchantCharge.identifier, stripeChargeId),
604
+ eq(merchantCharge.id, purchaseTable.merchantChargeId),
605
+ ),
606
+ )
607
+ .then((res) => res[0] ?? null),
608
+ )
609
+
610
+ if (!purchase.success) {
611
+ return null
612
+ }
613
+
614
+ return purchase.data
615
+ },
616
+ getPurchaseUserTransferById(options: { id: string }): Promise<
617
+ | (PurchaseUserTransfer & {
618
+ sourceUser: User
619
+ targetUser: User | null
620
+ purchase: Purchase
621
+ })
622
+ | null
623
+ > {
624
+ throw new Error('Method not implemented.')
625
+ },
626
+ getPurchaseWithUser(purchaseId: string): Promise<
627
+ | (Purchase & {
628
+ user: User
629
+ })
630
+ | null
631
+ > {
632
+ throw new Error('Method not implemented.')
633
+ },
634
+ async getPurchasesForUser(userId?: string): Promise<Purchase[]> {
635
+ if (!userId) {
636
+ return []
637
+ }
638
+
639
+ const visiblePurchaseStates = ['Valid', 'Refunded', 'Restricted']
640
+
641
+ const userPurchases = await client.query.purchases.findMany({
642
+ where: and(
643
+ eq(purchaseTable.userId, userId),
644
+ inArray(purchaseTable.state, visiblePurchaseStates),
645
+ ),
646
+ with: {
647
+ user: true,
648
+ product: true,
649
+ bulkCoupon: true,
650
+ },
651
+ orderBy: asc(purchaseTable.createdAt),
652
+ })
653
+
654
+ const parsedPurchases = z.array(purchaseSchema).safeParse(userPurchases)
655
+
656
+ if (!parsedPurchases.success) {
657
+ console.error('Error parsing purchases', parsedPurchases)
658
+ return []
659
+ }
660
+
661
+ return parsedPurchases.data
662
+ },
663
+ async getUserById(userId: string): Promise<User | null> {
664
+ return userSchema.nullable().parse(
665
+ await client.query.users.findFirst({
666
+ where: eq(users.id, userId),
667
+ }),
668
+ )
669
+ },
670
+ pricesOfPurchasesTowardOneBundle(options: {
671
+ userId: string | undefined
672
+ bundleId: string
673
+ }): Promise<Price[]> {
674
+ throw new Error('Method not implemented.')
675
+ },
676
+ toggleLessonProgressForUser(options: {
677
+ userId: string
678
+ lessonId?: string
679
+ lessonSlug?: string
680
+ }): Promise<ResourceProgress | null> {
681
+ throw new Error('Method not implemented.')
682
+ },
683
+ transferPurchasesToNewUser(options: {
684
+ merchantCustomerId: string
685
+ userId: string
686
+ }): Promise<unknown> {
687
+ throw new Error('Method not implemented.')
688
+ },
689
+ updatePurchaseStatusForCharge(
690
+ chargeId: string,
691
+ status: 'Valid' | 'Refunded' | 'Disputed' | 'Banned' | 'Restricted',
692
+ ): Promise<Purchase | undefined> {
693
+ throw new Error('Method not implemented.')
694
+ },
695
+ updatePurchaseUserTransferTransferState(options: {
696
+ id: string
697
+ transferState: PurchaseUserTransferState
698
+ }): Promise<PurchaseUserTransfer | null> {
699
+ throw new Error('Method not implemented.')
700
+ },
701
+ addResourceToResource: async function (options) {
702
+ const { parentResourceId, childResourceId } = options
703
+
704
+ const parentResourceData = await client.query.contentResource.findFirst({
705
+ where: or(
706
+ eq(
707
+ sql`JSON_EXTRACT (${contentResource.fields}, "$.slug")`,
708
+ parentResourceId,
709
+ ),
710
+ eq(contentResource.id, parentResourceId),
711
+ ),
712
+ with: {
713
+ resources: true,
714
+ },
715
+ })
716
+
717
+ const parentResource = ContentResourceSchema.parse(parentResourceData)
718
+
719
+ await client.insert(contentResourceResource).values({
720
+ resourceOfId: parentResource.id,
721
+ resourceId: childResourceId,
722
+ position: parentResource.resources?.length || 0,
723
+ })
724
+
725
+ const resourceJoin = client.query.contentResourceResource.findFirst({
726
+ where: and(
727
+ eq(contentResourceResource.resourceOfId, parentResourceId),
728
+ eq(contentResourceResource.resourceId, childResourceId),
729
+ ),
730
+ with: {
731
+ resource: true,
732
+ },
733
+ })
734
+
735
+ const parsedResourceJoin =
736
+ ContentResourceResourceSchema.safeParse(resourceJoin)
737
+ if (!parsedResourceJoin.success) {
738
+ return null
739
+ }
740
+
741
+ return parsedResourceJoin.data
742
+ },
165
743
  async updateContentResourceFields(options) {
166
744
  if (!options.id) {
167
745
  throw new Error('No content resource id.')
@@ -178,11 +756,35 @@ export function mySqlDrizzleAdapter(
178
756
  .set({ fields: { ...currentResource.fields, ...options.fields } })
179
757
  .where(eq(contentResource.id, options.id))
180
758
 
181
- return await client
182
- .select()
183
- .from(contentResource)
184
- .where(eq(contentResource.id, options.id))
185
- .then((res) => res[0])
759
+ const resource = await client.query.contentResource.findFirst({
760
+ where: eq(contentResource.id, options.id),
761
+ with: {
762
+ resources: {
763
+ with: {
764
+ resource: {
765
+ with: {
766
+ resources: {
767
+ with: {
768
+ resource: true,
769
+ },
770
+ orderBy: asc(contentResourceResource.position),
771
+ },
772
+ },
773
+ },
774
+ },
775
+ orderBy: asc(contentResourceResource.position),
776
+ },
777
+ },
778
+ })
779
+
780
+ const parsedResource = ContentResourceSchema.safeParse(resource)
781
+
782
+ if (!parsedResource.success) {
783
+ console.error('Error parsing resource', resource)
784
+ return null
785
+ }
786
+
787
+ return parsedResource.data
186
788
  },
187
789
  async getVideoResource(id) {
188
790
  if (!id) {
@@ -214,7 +816,7 @@ export function mySqlDrizzleAdapter(
214
816
  })
215
817
  .catch((error) => {
216
818
  console.error(error)
217
- return error
819
+ throw error
218
820
  })
219
821
  },
220
822
  async createContentResource(data) {
@@ -222,31 +824,83 @@ export function mySqlDrizzleAdapter(
222
824
 
223
825
  await client.insert(contentResource).values({ ...data, id })
224
826
 
225
- return await client
226
- .select()
227
- .from(contentResource)
228
- .where(eq(contentResource.id, id))
229
- .then((res) => res[0])
827
+ const resource = await client.query.contentResource.findFirst({
828
+ where: eq(contentResource.id, id),
829
+ with: {
830
+ resources: {
831
+ with: {
832
+ resource: {
833
+ with: {
834
+ resources: {
835
+ with: {
836
+ resource: true,
837
+ },
838
+ orderBy: asc(contentResourceResource.position),
839
+ },
840
+ },
841
+ },
842
+ },
843
+ orderBy: asc(contentResourceResource.position),
844
+ },
845
+ },
846
+ })
847
+
848
+ const parsedResource = ContentResourceSchema.safeParse(resource)
849
+
850
+ if (!parsedResource.success) {
851
+ console.error('Error parsing resource', resource)
852
+ throw new Error('Error parsing resource')
853
+ }
854
+
855
+ return parsedResource.data
230
856
  },
231
857
  async getContentResource(data) {
232
- return (
233
- (await client
234
- .select()
235
- .from(contentResource)
236
- .where(eq(contentResource.id, data))
237
- .then((res) => res[0])) ?? null
238
- )
858
+ const resource = await client.query.contentResource.findFirst({
859
+ where: eq(contentResource.id, data),
860
+ with: {
861
+ resources: {
862
+ with: {
863
+ resource: {
864
+ with: {
865
+ resources: {
866
+ with: {
867
+ resource: true,
868
+ },
869
+ orderBy: asc(contentResourceResource.position),
870
+ },
871
+ },
872
+ },
873
+ },
874
+ orderBy: asc(contentResourceResource.position),
875
+ },
876
+ },
877
+ })
878
+
879
+ const parsedResource = ContentResourceSchema.safeParse(resource)
880
+
881
+ if (!parsedResource.success) {
882
+ console.error('Error parsing resource', resource)
883
+ return null
884
+ }
885
+
886
+ return parsedResource.data
239
887
  },
240
888
  async createUser(data) {
241
- const id = crypto.randomUUID()
889
+ console.log(data)
890
+ try {
891
+ const id = crypto.randomUUID()
242
892
 
243
- await client.insert(users).values({ ...data, id })
893
+ await client.insert(users).values({ ...data, id })
244
894
 
245
- return await client
246
- .select()
247
- .from(users)
248
- .where(eq(users.id, id))
249
- .then((res) => res[0] as AdapterUser)
895
+ return await client
896
+ .select()
897
+ .from(users)
898
+ .where(eq(users.id, id))
899
+ .then((res) => res[0] as AdapterUser)
900
+ } catch (error) {
901
+ console.error(error)
902
+ throw error
903
+ }
250
904
  },
251
905
  async getUser(data) {
252
906
  return (