Skip to content

Commit 68f8cc1

Browse files
committed
Remove unnecessary content type from purchases logic
1 parent b2618ae commit 68f8cc1

File tree

12 files changed

+85
-87
lines changed

12 files changed

+85
-87
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
Warnings:
3+
4+
- You are about to drop the column `content_type` on the `email_notifications` table. All the data in the column will be lost.
5+
- You are about to drop the column `content_type` on the `purchases` table. All the data in the column will be lost.
6+
- A unique constraint covering the columns `[user_id,content_slug,email_type]` on the table `email_notifications` will be added. If there are existing duplicate values, this will fail.
7+
- A unique constraint covering the columns `[email,content_slug,email_type]` on the table `email_notifications` will be added. If there are existing duplicate values, this will fail.
8+
- A unique constraint covering the columns `[user_id,content_slug]` on the table `purchases` will be added. If there are existing duplicate values, this will fail.
9+
- A unique constraint covering the columns `[email,content_slug]` on the table `purchases` will be added. If there are existing duplicate values, this will fail.
10+
11+
*/
12+
-- DropIndex
13+
DROP INDEX "email_notifications_email_content_type_content_slug_email_t_key";
14+
15+
-- DropIndex
16+
DROP INDEX "email_notifications_user_id_content_type_content_slug_email_key";
17+
18+
-- DropIndex
19+
DROP INDEX "purchases_email_content_type_content_slug_key";
20+
21+
-- DropIndex
22+
DROP INDEX "purchases_user_id_content_type_content_slug_key";
23+
24+
-- AlterTable
25+
ALTER TABLE "email_notifications" DROP COLUMN "content_type";
26+
27+
-- AlterTable
28+
ALTER TABLE "purchases" DROP COLUMN "content_type";
29+
30+
-- CreateIndex
31+
CREATE UNIQUE INDEX "email_notifications_user_id_content_slug_email_type_key" ON "email_notifications"("user_id", "content_slug", "email_type");
32+
33+
-- CreateIndex
34+
CREATE UNIQUE INDEX "email_notifications_email_content_slug_email_type_key" ON "email_notifications"("email", "content_slug", "email_type");
35+
36+
-- CreateIndex
37+
CREATE UNIQUE INDEX "purchases_user_id_content_slug_key" ON "purchases"("user_id", "content_slug");
38+
39+
-- CreateIndex
40+
CREATE UNIQUE INDEX "purchases_email_content_slug_key" ON "purchases"("email", "content_slug");

prisma/schema.prisma

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,31 +68,29 @@ model VerificationToken {
6868
model Purchase {
6969
id String @id @default(cuid())
7070
userId String? @map("user_id")
71-
contentType String @map("content_type")
7271
contentSlug String @map("content_slug")
7372
purchaseDate DateTime @default(now()) @map("purchase_date")
7473
stripePaymentId String? @map("stripe_payment_id")
7574
amount Decimal?
7675
email String?
7776
user User? @relation(fields: [userId], references: [id])
7877
79-
@@unique([userId, contentType, contentSlug])
80-
@@unique([email, contentType, contentSlug])
78+
@@unique([userId, contentSlug])
79+
@@unique([email, contentSlug])
8180
@@map("purchases")
8281
}
8382

8483
model EmailNotification {
8584
id String @id @default(cuid())
8685
userId String? @map("user_id")
8786
email String?
88-
contentType String @map("content_type")
8987
contentSlug String @map("content_slug")
9088
emailType String @map("email_type")
9189
sentAt DateTime @default(now()) @map("sent_at")
9290
user User? @relation(fields: [userId], references: [id])
9391
94-
@@unique([userId, contentType, contentSlug, emailType])
95-
@@unique([email, contentType, contentSlug, emailType])
92+
@@unique([userId, contentSlug, emailType])
93+
@@unique([email, contentSlug, emailType])
9694
@@map("email_notifications")
9795
}
9896

scripts/grant-free-access.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ const grantAccess = async (email, productSlug) => {
165165
// Create a new purchase record
166166
const purchaseData = {
167167
email,
168-
contentType: 'article', // Assuming all products are articles for now
168+
169169
contentSlug: productSlug,
170170
stripePaymentId,
171171
amount: 0, // Free access

src/app/api/check-purchase/route.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@ export async function GET(req: Request) {
1515
// Get parameters from URL
1616
const { searchParams } = new URL(req.url)
1717
const slug = searchParams.get('slug')
18-
const type = searchParams.get('type') || 'blog' // Default to blog if not specified
1918
const email = searchParams.get('email') // Allow checking by email parameter
2019

2120
// Add detailed logging of all parameters
2221
console.log('CHECK-PURCHASE DEBUG - Request parameters:', {
2322
slug,
24-
type,
2523
email,
2624
session: session ? {
2725
id: session.user?.id,
@@ -34,10 +32,6 @@ export async function GET(req: Request) {
3432
return NextResponse.json({ error: 'Missing slug parameter' }, { status: 400 })
3533
}
3634

37-
// Convert type to contentType format used in the database
38-
const contentType = type === 'blog' ? 'article' : type
39-
console.log('CHECK-PURCHASE DEBUG - Converted content type:', { originalType: type, convertedType: contentType })
40-
4135
// Extract the base slug without path components
4236
// This allows products like 'rag-pipeline-tutorial' to be found regardless of path
4337
const baseSlug = slug.split('/').pop() || slug
@@ -60,9 +54,7 @@ export async function GET(req: Request) {
6054
id: session.user.id,
6155
email: session.user.email,
6256
slug,
63-
baseSlug,
64-
type,
65-
contentType
57+
baseSlug
6658
})
6759

6860
// First, check if the user ID is valid (not empty string)
@@ -72,7 +64,6 @@ export async function GET(req: Request) {
7264
const purchase = await prisma.purchase.findFirst({
7365
where: {
7466
userId: session.user.id,
75-
contentType,
7667
...slugCondition
7768
}
7869
})
@@ -92,16 +83,13 @@ export async function GET(req: Request) {
9283
console.log('CHECK-PURCHASE DEBUG - Checking purchase for email:', {
9384
email: userEmail,
9485
slug,
95-
baseSlug,
96-
type,
97-
contentType
86+
baseSlug
9887
})
9988

10089
// Check for purchases with exact email match
10190
const purchase = await prisma.purchase.findFirst({
10291
where: {
10392
email: userEmail,
104-
contentType,
10593
...slugCondition
10694
}
10795
})
@@ -116,7 +104,6 @@ export async function GET(req: Request) {
116104
const purchaseCaseInsensitive = await prisma.purchase.findFirst({
117105
where: {
118106
email: { equals: userEmail, mode: 'insensitive' },
119-
contentType,
120107
...slugCondition
121108
}
122109
})

src/app/api/checkout-sessions/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ export async function GET(req: Request) {
127127

128128
console.log('Found manually provisioned purchase:', purchase);
129129

130-
// Determine the content type directory
131-
let contentDir = purchase.contentType === 'course' ? 'learn/courses' : purchase.contentType;
130+
// Default to 'blog' content directory since we no longer store contentType
131+
const contentDir = 'blog';
132132

133133
// Load the content metadata using the new function
134134
const content = await getContentItemByDirectorySlug(contentDir, purchase.contentSlug);

src/app/api/webhooks/stripe/route.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,22 +95,20 @@ export async function POST(req: Request) {
9595
logger.error('WEBHOOK: FATAL - Could not load canonical content or type missing', { directorySlug, loadedContent });
9696
return NextResponse.json({ error: 'Could not load canonical content or type missing' }, { status: 500 });
9797
}
98-
const canonicalType = loadedContent.type;
99-
logger.info('WEBHOOK: Using canonical content type for purchase record:', canonicalType);
98+
logger.info('WEBHOOK: Using canonical content for purchase record');
99+
100100
// 2. Record the purchase
101101
logger.info('WEBHOOK: Recording purchase')
102102
try {
103-
const contentType = canonicalType;
104103
const contentSlug = directorySlug;
105-
logger.debug('WEBHOOK: Purchase upsert details:', { userId: user?.id, contentType, contentSlug, email, sessionId: session.id })
104+
logger.debug('WEBHOOK: Purchase upsert details:', { userId: user?.id, contentSlug, email, sessionId: session.id })
106105
let result;
107106
if (user?.id) {
108107
logger.debug('WEBHOOK: Upserting by userId')
109108
result = await prisma.purchase.upsert({
110109
where: {
111-
userId_contentType_contentSlug: {
110+
userId_contentSlug: {
112111
userId: user.id,
113-
contentType,
114112
contentSlug
115113
}
116114
},
@@ -122,7 +120,6 @@ export async function POST(req: Request) {
122120
},
123121
create: {
124122
userId: user.id,
125-
contentType,
126123
contentSlug,
127124
purchaseDate: new Date(),
128125
stripePaymentId: session.id,
@@ -134,9 +131,8 @@ export async function POST(req: Request) {
134131
logger.debug('WEBHOOK: Upserting by email')
135132
result = await prisma.purchase.upsert({
136133
where: {
137-
email_contentType_contentSlug: {
134+
email_contentSlug: {
138135
email,
139-
contentType,
140136
contentSlug
141137
}
142138
},
@@ -148,7 +144,6 @@ export async function POST(req: Request) {
148144
},
149145
create: {
150146
userId: null,
151-
contentType,
152147
contentSlug,
153148
purchaseDate: new Date(),
154149
stripePaymentId: session.id,
@@ -189,7 +184,6 @@ export async function POST(req: Request) {
189184
const existingNotification = await prisma.emailNotification.findFirst({
190185
where: {
191186
email,
192-
contentType: type,
193187
contentSlug: directorySlug,
194188
emailType: 'purchase_confirmation'
195189
}
@@ -235,7 +229,6 @@ export async function POST(req: Request) {
235229
await prisma.emailNotification.create({
236230
data: {
237231
userId: user?.id || null,
238-
contentType: type,
239232
contentSlug: directorySlug,
240233
emailType: 'purchase_confirmation',
241234
email

src/app/blog/[slug]/page.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,15 @@ export default async function Page({ params }: PageProps) {
6666
let hasPurchased = false;
6767
if (content?.commerce?.isPaid) {
6868
logger.debug(`Checking purchase status for paid content (${slug})`);
69-
// Always use the canonical type and directorySlug from the content object
69+
// Use the directorySlug from the content object
7070
const directorySlug = content.directorySlug || slug;
71-
const contentType = content.type || CONTENT_TYPE;
7271
// First try with user ID
73-
hasPurchased = await hasUserPurchased(session?.user?.id, contentType, directorySlug);
72+
hasPurchased = await hasUserPurchased(session?.user?.id, directorySlug);
7473
logger.debug(`Purchase check by user ID (${session?.user?.id || 'N/A'}): ${hasPurchased}`);
7574
// If not found by user ID, try with email as a fallback
7675
if (!hasPurchased && session?.user?.email) {
7776
logger.debug(`Not found by user ID, trying email: ${session.user.email}`);
78-
hasPurchased = await hasUserPurchased(session.user.email, contentType, directorySlug);
77+
hasPurchased = await hasUserPurchased(session.user.email, directorySlug);
7978
logger.debug(`Purchase check by email (${session.user.email}): ${hasPurchased}`);
8079
}
8180
} else {

src/app/learn/[course]/[segment]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ async function getSegmentContent(course: string, segment: string) {
104104
async function userPurchasedCourse(userEmail: string, courseId: number): Promise<boolean> {
105105
try {
106106
// Use the hasUserPurchased function to check if the user has purchased the course
107-
return await hasUserPurchased('course', courseId.toString(), userEmail);
107+
return await hasUserPurchased(userEmail, courseId.toString());
108108
} catch (error) {
109109
console.error('Error checking if user purchased course:', error);
110110
return false;

src/app/learn/courses/[slug]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export default async function CourseSlugPage({ params }: PageProps) {
5151
const userId = session?.user.id
5252

5353
// Check if user has purchased access
54-
const userHasPurchased = await hasUserPurchased(userId, CONTENT_TYPE, slug)
54+
const userHasPurchased = await hasUserPurchased(userId, slug)
5555

5656
let isSubscribed = false
5757
if (content?.commerce?.requiresEmail) {

src/app/videos/[slug]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default async function VideoSlugPage({ params }: PageProps) {
4848
const session = await auth()
4949

5050
// If no user is logged in or hasn't purchased, show a preview with paywall
51-
if (!session?.user || !(await hasUserPurchased(session.user.id, CONTENT_TYPE, slug))) {
51+
if (!session?.user || !(await hasUserPurchased(session.user.id, slug))) {
5252
const defaultText = getDefaultPaywallText(CONTENT_TYPE)
5353

5454
// Show a limited preview with a paywall

0 commit comments

Comments
 (0)