feat(billing): integrate Stripe for subscription management and billing access

- Added billing-related fields to the User model in the database.
- Implemented functions for managing billing access, including trial periods and subscription statuses.
- Created new billing utility functions for Stripe integration.
- Updated onboarding page to include billing overview and workspace creation eligibility.
- Enhanced route access checks to require billing access for certain actions.
- Implemented cleanup scripts for expired billing workspaces and associated media.
- Updated header component to conditionally show app navigation based on billing access.
- Added new migrations for billing-related database changes.
This commit is contained in:
Yusuf İpek
2026-04-08 17:50:40 +03:00
parent 8db7a031e6
commit 6f22b0bf8b
45 changed files with 1859 additions and 218 deletions
+23
View File
@@ -1,6 +1,7 @@
import bcrypt from 'bcryptjs';
import type { ShareLink, SharePermission } from '@prisma/client';
import { db } from '@/lib/db';
import { hasBillingAccess } from '@/lib/billing';
export const MAX_SHARE_PASSWORD_LENGTH = 128;
@@ -45,6 +46,24 @@ export async function validateShareLinkAccess({
}: ValidateShareLinkParams): Promise<ShareLinkAccessResult> {
const link = await db.shareLink.findUnique({
where: { token },
include: {
project: {
select: {
workspace: {
select: {
owner: {
select: {
subscriptionStatus: true,
trialEndsAt: true,
stripeCurrentPeriodEnd: true,
billingAccessEndedAt: true,
},
},
},
},
},
},
},
});
if (!link) {
@@ -60,6 +79,10 @@ export async function validateShareLinkAccess({
return { hasAccess: false, canComment: false, canDownload: false, allowGuests: false, requiresPassword: false, link };
}
if (!link.project?.workspace.owner || !hasBillingAccess(link.project.workspace.owner)) {
return { hasAccess: false, canComment: false, canDownload: false, allowGuests: false, requiresPassword: false, link };
}
if (link.passwordHash && !passwordVerified) {
if (!presentedPassword) {
return { hasAccess: false, canComment: false, canDownload: false, allowGuests: false, requiresPassword: true, link };