@coursebuilder/adapter-drizzle 0.5.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.
- package/dist/{chunk-BM5UKWXI.js → chunk-7ISZNMPU.js} +2 -2
- package/dist/{chunk-TBM3AK3P.js → chunk-7X7TMG4A.js} +2 -2
- package/dist/{chunk-CQNKTFXE.js → chunk-DAP5KOAO.js} +2 -2
- package/dist/{chunk-JZQ666KH.js → chunk-KDOSK7KN.js} +2 -2
- package/dist/{chunk-O6JECJRY.js → chunk-QPXUGIXO.js} +2 -2
- package/dist/{chunk-FRA4ZCRA.js → chunk-UHH4OVKA.js} +4 -14
- package/dist/{chunk-QTGVGIRO.js → chunk-UK6EHYS5.js} +2 -2
- package/dist/{chunk-Z245DYMP.js → chunk-VUS77FG3.js} +493 -198
- package/dist/index.d.ts +2 -1
- package/dist/index.js +12 -12
- package/dist/lib/mysql/index.cjs +498 -202
- package/dist/lib/mysql/index.cjs.map +1 -1
- package/dist/lib/mysql/index.d.cts +8 -30
- package/dist/lib/mysql/index.d.ts +8 -30
- package/dist/lib/mysql/index.js +10 -8
- package/dist/lib/mysql/schemas/auth/accounts.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/accounts.js +1 -1
- package/dist/lib/mysql/schemas/auth/device-access-token.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/device-access-token.js +2 -2
- package/dist/lib/mysql/schemas/auth/device-verification.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/device-verification.js +2 -2
- package/dist/lib/mysql/schemas/auth/role-permissions.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/role-permissions.js +2 -2
- package/dist/lib/mysql/schemas/auth/roles.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/roles.js +1 -1
- package/dist/lib/mysql/schemas/auth/sessions.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/sessions.js +2 -2
- package/dist/lib/mysql/schemas/auth/user-permissions.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/user-permissions.js +1 -1
- package/dist/lib/mysql/schemas/auth/user-prefs.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/user-prefs.js +1 -1
- package/dist/lib/mysql/schemas/auth/user-roles.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/user-roles.js +1 -1
- package/dist/lib/mysql/schemas/auth/users.cjs.map +1 -1
- package/dist/lib/mysql/schemas/auth/users.js +1 -1
- package/dist/lib/mysql/schemas/commerce/coupon.cjs +3 -13
- package/dist/lib/mysql/schemas/commerce/coupon.cjs.map +1 -1
- package/dist/lib/mysql/schemas/commerce/coupon.d.cts +2 -14
- package/dist/lib/mysql/schemas/commerce/coupon.d.ts +2 -14
- package/dist/lib/mysql/schemas/commerce/coupon.js +1 -1
- package/dist/lib/mysql/schemas/commerce/price.cjs.map +1 -1
- package/dist/lib/mysql/schemas/commerce/price.js +1 -1
- package/dist/lib/mysql/schemas/commerce/product.cjs.map +1 -1
- package/dist/lib/mysql/schemas/commerce/product.js +1 -1
- package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs.map +1 -1
- package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.js +2 -2
- package/dist/lib/mysql/schemas/commerce/purchase.cjs +0 -4
- package/dist/lib/mysql/schemas/commerce/purchase.cjs.map +1 -1
- package/dist/lib/mysql/schemas/commerce/purchase.js +1 -1
- package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs.map +1 -1
- package/dist/lib/mysql/schemas/commerce/upgradable-products.js +2 -2
- package/dist/lib/mysql/schemas/communication/comment.cjs.map +1 -1
- package/dist/lib/mysql/schemas/communication/comment.js +1 -1
- package/dist/lib/mysql/schemas/communication/communication-preferences.cjs.map +1 -1
- package/dist/lib/mysql/schemas/communication/communication-preferences.js +1 -1
- package/dist/lib/mysql/schemas/content/content-contributions.cjs.map +1 -1
- package/dist/lib/mysql/schemas/content/content-contributions.js +1 -1
- package/dist/lib/mysql/schemas/content/content-resource-product.cjs.map +1 -1
- package/dist/lib/mysql/schemas/content/content-resource-product.js +1 -1
- package/dist/lib/mysql/schemas/content/content-resource-resource.cjs.map +1 -1
- package/dist/lib/mysql/schemas/content/content-resource-resource.js +1 -1
- package/dist/lib/mysql/schemas/content/content-resource.cjs.map +1 -1
- package/dist/lib/mysql/schemas/content/content-resource.js +1 -1
- package/dist/lib/utils.d.cts +1 -0
- package/dist/lib/utils.d.ts +1 -0
- package/package.json +4 -2
- package/src/index.ts +14 -4
- package/src/lib/mysql/index.ts +601 -182
- package/src/lib/mysql/schemas/commerce/coupon.ts +3 -9
package/src/lib/mysql/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
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,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
MySqlDatabase,
|
|
20
21
|
MySqlTableFn,
|
|
21
22
|
} from 'drizzle-orm/mysql-core'
|
|
23
|
+
import { customAlphabet } from 'nanoid'
|
|
22
24
|
import { v4 } from 'uuid'
|
|
23
25
|
import { z } from 'zod'
|
|
24
26
|
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
merchantPriceSchema,
|
|
35
37
|
MerchantProduct,
|
|
36
38
|
merchantProductSchema,
|
|
39
|
+
NewProduct,
|
|
37
40
|
Price,
|
|
38
41
|
priceSchema,
|
|
39
42
|
Product,
|
|
@@ -51,15 +54,16 @@ import {
|
|
|
51
54
|
userSchema,
|
|
52
55
|
} from '@coursebuilder/core/schemas'
|
|
53
56
|
import {
|
|
57
|
+
ContentResourceProductSchema,
|
|
54
58
|
ContentResourceResourceSchema,
|
|
55
59
|
ContentResourceSchema,
|
|
56
60
|
type ContentResource,
|
|
57
|
-
type ContentResourceResource,
|
|
58
61
|
} from '@coursebuilder/core/schemas/content-resource-schema'
|
|
59
62
|
import { merchantAccountSchema } from '@coursebuilder/core/schemas/merchant-account-schema'
|
|
60
63
|
import { merchantCustomerSchema } from '@coursebuilder/core/schemas/merchant-customer-schema'
|
|
61
64
|
import { type ModuleProgress } from '@coursebuilder/core/schemas/resource-progress-schema'
|
|
62
65
|
import { VideoResourceSchema } from '@coursebuilder/core/schemas/video-resource'
|
|
66
|
+
import { PaymentsProviderConfig } from '@coursebuilder/core/types'
|
|
63
67
|
import { logger } from '@coursebuilder/core/utils/logger'
|
|
64
68
|
import { validateCoupon } from '@coursebuilder/core/utils/validate-coupon'
|
|
65
69
|
|
|
@@ -172,6 +176,8 @@ import {
|
|
|
172
176
|
import { getLessonProgressSchema } from './schemas/content/lesson-progress.js'
|
|
173
177
|
import { getResourceProgressSchema } from './schemas/content/resource-progress.js'
|
|
174
178
|
|
|
179
|
+
export const guid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 5)
|
|
180
|
+
|
|
175
181
|
export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
|
|
176
182
|
return {
|
|
177
183
|
accounts: getAccountsSchema(mysqlTable),
|
|
@@ -254,6 +260,7 @@ export type DefaultSchema = ReturnType<typeof createTables>
|
|
|
254
260
|
export function mySqlDrizzleAdapter(
|
|
255
261
|
client: InstanceType<typeof MySqlDatabase>,
|
|
256
262
|
tableFn = defaultMySqlTableFn,
|
|
263
|
+
paymentProvider?: PaymentsProviderConfig,
|
|
257
264
|
): CourseBuilderAdapter<typeof MySqlDatabase> {
|
|
258
265
|
const {
|
|
259
266
|
users,
|
|
@@ -262,6 +269,7 @@ export function mySqlDrizzleAdapter(
|
|
|
262
269
|
verificationTokens,
|
|
263
270
|
contentResource,
|
|
264
271
|
contentResourceResource,
|
|
272
|
+
contentResourceProduct,
|
|
265
273
|
purchases: purchaseTable,
|
|
266
274
|
purchaseUserTransfer,
|
|
267
275
|
coupon,
|
|
@@ -304,7 +312,7 @@ export function mySqlDrizzleAdapter(
|
|
|
304
312
|
if (coupon && couponValidation.isRedeemable) {
|
|
305
313
|
// if the Coupon is the Bulk Coupon of a Bulk Purchase,
|
|
306
314
|
// then a bulk coupon is being redeemed
|
|
307
|
-
const bulkCouponRedemption = Boolean(coupon.
|
|
315
|
+
const bulkCouponRedemption = Boolean(coupon.maxUses > 1)
|
|
308
316
|
|
|
309
317
|
const { user } = await adapter.findOrCreateUser(email)
|
|
310
318
|
|
|
@@ -325,8 +333,18 @@ export function mySqlDrizzleAdapter(
|
|
|
325
333
|
productId,
|
|
326
334
|
})
|
|
327
335
|
|
|
328
|
-
if (existingPurchases.length > 0)
|
|
329
|
-
|
|
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
|
+
}
|
|
330
348
|
|
|
331
349
|
const purchaseId = `purchase-${v4()}`
|
|
332
350
|
|
|
@@ -410,7 +428,10 @@ export function mySqlDrizzleAdapter(
|
|
|
410
428
|
},
|
|
411
429
|
getMerchantPriceForProductId: async (productId) => {
|
|
412
430
|
const merchantPriceData = await client.query.merchantPrice.findFirst({
|
|
413
|
-
where:
|
|
431
|
+
where: and(
|
|
432
|
+
eq(merchantPrice.merchantProductId, productId),
|
|
433
|
+
eq(merchantPrice.status, 1),
|
|
434
|
+
),
|
|
414
435
|
})
|
|
415
436
|
|
|
416
437
|
const parsedMerchantPrice =
|
|
@@ -463,7 +484,7 @@ export function mySqlDrizzleAdapter(
|
|
|
463
484
|
)
|
|
464
485
|
|
|
465
486
|
if (previousPurchaseProductIds.length > 0) {
|
|
466
|
-
return
|
|
487
|
+
return client.query.upgradableProducts.findMany({
|
|
467
488
|
where: and(
|
|
468
489
|
eq(upgradableProducts.upgradableToId, productId),
|
|
469
490
|
inArray(
|
|
@@ -871,20 +892,21 @@ export function mySqlDrizzleAdapter(
|
|
|
871
892
|
},
|
|
872
893
|
async getCouponWithBulkPurchases(couponId: string): Promise<
|
|
873
894
|
| (Coupon & {
|
|
874
|
-
|
|
875
|
-
|
|
895
|
+
bulkPurchases?: Purchase[] | null
|
|
896
|
+
redeemedBulkCouponPurchases: { bulkCouponId?: string | null }[]
|
|
876
897
|
})
|
|
877
898
|
| null
|
|
878
899
|
> {
|
|
879
900
|
logger.debug('getCouponWithBulkPurchases', { couponId })
|
|
880
901
|
let couponData
|
|
902
|
+
let bulkCouponPurchases
|
|
881
903
|
try {
|
|
882
904
|
couponData =
|
|
883
905
|
(await client.query.coupon.findFirst({
|
|
884
906
|
where: eq(coupon.id, couponId),
|
|
885
907
|
with: {
|
|
886
|
-
|
|
887
|
-
|
|
908
|
+
bulkPurchases: true,
|
|
909
|
+
redeemedBulkCouponPurchases: true,
|
|
888
910
|
},
|
|
889
911
|
})) || null
|
|
890
912
|
} catch (e) {
|
|
@@ -892,6 +914,19 @@ export function mySqlDrizzleAdapter(
|
|
|
892
914
|
logger.error(e as Error)
|
|
893
915
|
}
|
|
894
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
|
+
|
|
895
930
|
if (!couponData) {
|
|
896
931
|
logger.debug('getCouponWithBulkPurchases', {
|
|
897
932
|
couponId,
|
|
@@ -900,20 +935,29 @@ export function mySqlDrizzleAdapter(
|
|
|
900
935
|
return null
|
|
901
936
|
}
|
|
902
937
|
|
|
938
|
+
const couponWithBulkPurchases = {
|
|
939
|
+
...couponData,
|
|
940
|
+
redeemedBulkCouponPurchases: bulkCouponPurchases || [],
|
|
941
|
+
}
|
|
942
|
+
|
|
903
943
|
const parsedCoupon = couponSchema
|
|
904
944
|
.merge(
|
|
905
945
|
z.object({
|
|
906
|
-
|
|
907
|
-
bulkPurchase: purchaseSchema.nullable().optional(),
|
|
946
|
+
redeemedBulkCouponPurchases: z.array(purchaseSchema),
|
|
908
947
|
}),
|
|
909
948
|
)
|
|
910
949
|
.nullable()
|
|
911
|
-
.safeParse(
|
|
950
|
+
.safeParse(couponWithBulkPurchases)
|
|
912
951
|
|
|
913
952
|
if (!parsedCoupon.success) {
|
|
914
|
-
console.error(
|
|
953
|
+
console.error(
|
|
954
|
+
'Error parsing coupon',
|
|
955
|
+
JSON.stringify(parsedCoupon.error),
|
|
956
|
+
couponData,
|
|
957
|
+
)
|
|
915
958
|
return null
|
|
916
959
|
}
|
|
960
|
+
|
|
917
961
|
return parsedCoupon.data
|
|
918
962
|
},
|
|
919
963
|
async getDefaultCoupon(productIds?: string[]): Promise<{
|
|
@@ -982,189 +1026,114 @@ export function mySqlDrizzleAdapter(
|
|
|
982
1026
|
async getModuleProgressForUser(
|
|
983
1027
|
userIdOrEmail: string,
|
|
984
1028
|
moduleIdOrSlug: string,
|
|
985
|
-
): Promise<ModuleProgress> {
|
|
986
|
-
|
|
987
|
-
const
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
eq(contentResource.id, moduleIdOrSlug),
|
|
992
|
-
eq(
|
|
993
|
-
sql`JSON_EXTRACT (${contentResource.fields}, "$.slug")`,
|
|
994
|
-
moduleIdOrSlug,
|
|
995
|
-
),
|
|
996
|
-
),
|
|
997
|
-
with: {
|
|
998
|
-
resources: {
|
|
999
|
-
with: {
|
|
1000
|
-
resource: {
|
|
1001
|
-
with: {
|
|
1002
|
-
resources: {
|
|
1003
|
-
with: {
|
|
1004
|
-
resource: {
|
|
1005
|
-
with: {
|
|
1006
|
-
resources: {
|
|
1007
|
-
with: {
|
|
1008
|
-
resource: true,
|
|
1009
|
-
},
|
|
1010
|
-
},
|
|
1011
|
-
},
|
|
1012
|
-
},
|
|
1013
|
-
},
|
|
1014
|
-
orderBy: asc(contentResourceResource.position),
|
|
1015
|
-
},
|
|
1016
|
-
},
|
|
1017
|
-
},
|
|
1018
|
-
},
|
|
1019
|
-
orderBy: asc(contentResourceResource.position),
|
|
1020
|
-
},
|
|
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,
|
|
1021
1035
|
},
|
|
1022
1036
|
})
|
|
1023
1037
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
function flattenResources(
|
|
1027
|
-
resources: ContentResourceResource[],
|
|
1028
|
-
): ContentResource[] {
|
|
1029
|
-
const result: ContentResource[] = []
|
|
1030
|
-
|
|
1031
|
-
function recurse(resources: ContentResourceResource[]) {
|
|
1032
|
-
for (const nestedResource of resources) {
|
|
1033
|
-
const resource = nestedResource.resource
|
|
1034
|
-
result.push(resource)
|
|
1035
|
-
if (resource.resources) {
|
|
1036
|
-
recurse(resource.resources)
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
recurse(resources)
|
|
1042
|
-
return result
|
|
1038
|
+
if (!user) {
|
|
1039
|
+
return null
|
|
1043
1040
|
}
|
|
1044
1041
|
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
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)),
|
|
1049
1050
|
})
|
|
1050
1051
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
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)
|
|
1057
1098
|
return {
|
|
1058
1099
|
completedLessons: [],
|
|
1059
1100
|
nextResource: null,
|
|
1060
1101
|
percentCompleted: 0,
|
|
1061
1102
|
completedLessonsCount: 0,
|
|
1062
|
-
totalLessonsCount
|
|
1103
|
+
totalLessonsCount,
|
|
1063
1104
|
}
|
|
1064
1105
|
}
|
|
1065
1106
|
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
role: true,
|
|
1082
|
-
},
|
|
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,
|
|
1083
1122
|
},
|
|
1084
|
-
},
|
|
1085
|
-
})
|
|
1086
|
-
.then(async (res) => {
|
|
1087
|
-
if (res) {
|
|
1088
|
-
return {
|
|
1089
|
-
...res,
|
|
1090
|
-
roles: res.roles.map((r) => r.role),
|
|
1091
|
-
}
|
|
1092
1123
|
}
|
|
1093
|
-
})
|
|
1094
|
-
|
|
1095
|
-
if (!user) {
|
|
1096
|
-
console.error('User not found', userIdOrEmail)
|
|
1097
|
-
return {
|
|
1098
|
-
completedLessons: [],
|
|
1099
|
-
nextResource: null,
|
|
1100
|
-
percentCompleted: 0,
|
|
1101
|
-
completedLessonsCount: 0,
|
|
1102
|
-
totalLessonsCount,
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
const parsedUser = userSchema.safeParse(user)
|
|
1107
|
-
|
|
1108
|
-
if (!parsedUser.success) {
|
|
1109
|
-
console.error('Error parsing user', parsedUser.error)
|
|
1110
|
-
return {
|
|
1111
|
-
completedLessons: [],
|
|
1112
|
-
nextResource: null,
|
|
1113
|
-
percentCompleted: 0,
|
|
1114
|
-
completedLessonsCount: 0,
|
|
1115
|
-
totalLessonsCount,
|
|
1116
1124
|
}
|
|
1117
1125
|
}
|
|
1118
1126
|
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
inArray(
|
|
1124
|
-
resourceProgress.resourceId,
|
|
1125
|
-
progressResources.map((r) => r.id),
|
|
1126
|
-
),
|
|
1127
|
-
),
|
|
1128
|
-
orderBy: asc(resourceProgress.completedAt),
|
|
1129
|
-
})
|
|
1130
|
-
|
|
1131
|
-
const nextResourceId = parsedResources.data
|
|
1132
|
-
.filter((r) => {
|
|
1133
|
-
return COUNTABLE_MODULE_RESOURCE_TYPES.includes(r.type)
|
|
1134
|
-
})
|
|
1135
|
-
.find((r) => !userProgress.find((p) => p.resourceId === r.id))?.id
|
|
1136
|
-
|
|
1137
|
-
const nextResource = await client.query.contentResource.findFirst({
|
|
1138
|
-
where: eq(contentResource.id, nextResourceId as string),
|
|
1139
|
-
})
|
|
1140
|
-
|
|
1141
|
-
const parsedNextResource = ContentResourceSchema.nullable()
|
|
1142
|
-
.optional()
|
|
1143
|
-
.default(null)
|
|
1144
|
-
.parse(nextResource)
|
|
1145
|
-
|
|
1146
|
-
const parsedProgress = z
|
|
1147
|
-
.array(resourceProgressSchema)
|
|
1148
|
-
.safeParse(userProgress)
|
|
1149
|
-
if (!parsedProgress.success) {
|
|
1150
|
-
console.error('Error parsing user progress', parsedProgress.error)
|
|
1151
|
-
return {
|
|
1152
|
-
completedLessons: [],
|
|
1153
|
-
nextResource: null,
|
|
1154
|
-
percentCompleted: 0,
|
|
1155
|
-
completedLessonsCount: 0,
|
|
1156
|
-
totalLessonsCount,
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
const percentCompleted = Math.round(
|
|
1160
|
-
(parsedProgress.data.length / parsedResources.data.length) * 100,
|
|
1161
|
-
)
|
|
1127
|
+
const percentCompleted =
|
|
1128
|
+
totalLessonsCount > 0
|
|
1129
|
+
? Math.ceil((completedLessonsCount / totalLessonsCount) * 100)
|
|
1130
|
+
: 0
|
|
1162
1131
|
|
|
1163
1132
|
return {
|
|
1164
|
-
completedLessons
|
|
1165
|
-
nextResource
|
|
1133
|
+
completedLessons,
|
|
1134
|
+
nextResource,
|
|
1166
1135
|
percentCompleted,
|
|
1167
|
-
completedLessonsCount
|
|
1136
|
+
completedLessonsCount,
|
|
1168
1137
|
totalLessonsCount,
|
|
1169
1138
|
}
|
|
1170
1139
|
},
|
|
@@ -1179,7 +1148,7 @@ export function mySqlDrizzleAdapter(
|
|
|
1179
1148
|
})
|
|
1180
1149
|
const parsed = merchantChargeSchema.safeParse(mCharge)
|
|
1181
1150
|
if (!parsed.success) {
|
|
1182
|
-
console.
|
|
1151
|
+
console.info('Error parsing merchantCharge', mCharge)
|
|
1183
1152
|
return null
|
|
1184
1153
|
}
|
|
1185
1154
|
return parsed.data
|
|
@@ -1258,12 +1227,356 @@ export function mySqlDrizzleAdapter(
|
|
|
1258
1227
|
}),
|
|
1259
1228
|
)
|
|
1260
1229
|
},
|
|
1261
|
-
async
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
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),
|
|
1265
|
+
}),
|
|
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'),
|
|
1265
1428
|
}),
|
|
1266
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
|
+
}
|
|
1267
1580
|
},
|
|
1268
1581
|
async getPurchaseCountForProduct(productId: string): Promise<number> {
|
|
1269
1582
|
return await client.query.purchases
|
|
@@ -1306,7 +1619,11 @@ export function mySqlDrizzleAdapter(
|
|
|
1306
1619
|
)
|
|
1307
1620
|
|
|
1308
1621
|
if (!chargeForPurchase) {
|
|
1309
|
-
logger.error(
|
|
1622
|
+
logger.error(
|
|
1623
|
+
new Error(
|
|
1624
|
+
`No charge found for purchase: Stripe Charge ID: ${stripeChargeId}`,
|
|
1625
|
+
),
|
|
1626
|
+
)
|
|
1310
1627
|
return null
|
|
1311
1628
|
}
|
|
1312
1629
|
|
|
@@ -1516,6 +1833,34 @@ export function mySqlDrizzleAdapter(
|
|
|
1516
1833
|
purchase: parsedPurchase.data,
|
|
1517
1834
|
}
|
|
1518
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
|
+
},
|
|
1519
1864
|
async getUserById(userId: string): Promise<User | null> {
|
|
1520
1865
|
const user = await client.query.users
|
|
1521
1866
|
.findFirst({
|
|
@@ -1638,11 +1983,56 @@ export function mySqlDrizzleAdapter(
|
|
|
1638
1983
|
}): Promise<unknown> {
|
|
1639
1984
|
throw new Error('Method not implemented.')
|
|
1640
1985
|
},
|
|
1641
|
-
updatePurchaseStatusForCharge(
|
|
1986
|
+
async updatePurchaseStatusForCharge(
|
|
1642
1987
|
chargeId: string,
|
|
1643
1988
|
status: 'Valid' | 'Refunded' | 'Disputed' | 'Banned' | 'Restricted',
|
|
1644
1989
|
): Promise<Purchase | undefined> {
|
|
1645
|
-
|
|
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
|
+
}
|
|
1646
2036
|
},
|
|
1647
2037
|
async updatePurchaseUserTransferTransferState(options: {
|
|
1648
2038
|
id: string
|
|
@@ -1706,6 +2096,35 @@ export function mySqlDrizzleAdapter(
|
|
|
1706
2096
|
|
|
1707
2097
|
return parsedResourceJoin.data
|
|
1708
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
|
+
},
|
|
1709
2128
|
async updateContentResourceFields(options) {
|
|
1710
2129
|
if (!options.id) {
|
|
1711
2130
|
throw new Error('No content resource id.')
|