feat(subscriptions): added polar as payment provider for subscriptions
* feature(dashboard): add polar / subscription * wip(payments): manage subscription * wip(payments): add free product, faq and some other improvements * fix(root): change node to bundler in tsconfig * wip(payments): display current subscription * feat(dashboard): schedule project for deletion * wip(payments): support custom products/subscriptions * wip(payments): fix polar scripts * wip(payments): add json package to dockerfiles
This commit is contained in:
committed by
GitHub
parent
86bf9dd064
commit
168ebc3430
@@ -1,11 +1,31 @@
|
||||
import { createLogger } from '@openpanel/logger';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { type Organization, PrismaClient } from '@prisma/client';
|
||||
import { readReplicas } from '@prisma/extension-read-replicas';
|
||||
|
||||
export * from '@prisma/client';
|
||||
|
||||
const logger = createLogger({ name: 'db' });
|
||||
|
||||
const isWillBeCanceled = (
|
||||
organization: Pick<
|
||||
Organization,
|
||||
'subscriptionStatus' | 'subscriptionCanceledAt' | 'subscriptionEndsAt'
|
||||
>,
|
||||
) =>
|
||||
organization.subscriptionStatus === 'active' &&
|
||||
organization.subscriptionCanceledAt &&
|
||||
organization.subscriptionEndsAt;
|
||||
|
||||
const isCanceled = (
|
||||
organization: Pick<
|
||||
Organization,
|
||||
'subscriptionStatus' | 'subscriptionCanceledAt'
|
||||
>,
|
||||
) =>
|
||||
organization.subscriptionStatus === 'canceled' &&
|
||||
organization.subscriptionCanceledAt &&
|
||||
organization.subscriptionCanceledAt < new Date();
|
||||
|
||||
const getPrismaClient = () => {
|
||||
const prisma = new PrismaClient({
|
||||
log: ['error'],
|
||||
@@ -32,15 +52,182 @@ const getPrismaClient = () => {
|
||||
return query(args);
|
||||
},
|
||||
},
|
||||
})
|
||||
.$extends({
|
||||
result: {
|
||||
organization: {
|
||||
subscriptionStatus: {
|
||||
needs: { subscriptionStatus: true, subscriptionCanceledAt: true },
|
||||
compute(org) {
|
||||
return org.subscriptionStatus || 'trialing';
|
||||
},
|
||||
},
|
||||
hasSubscription: {
|
||||
needs: { subscriptionStatus: true, subscriptionEndsAt: true },
|
||||
compute(org) {
|
||||
if (
|
||||
[null, 'canceled', 'trialing'].includes(org.subscriptionStatus)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
slug: {
|
||||
needs: { id: true },
|
||||
compute(org) {
|
||||
return org.id;
|
||||
},
|
||||
},
|
||||
subscriptionChartEndDate: {
|
||||
needs: {
|
||||
subscriptionEndsAt: true,
|
||||
subscriptionPeriodEventsCountExceededAt: true,
|
||||
},
|
||||
compute(org) {
|
||||
if (
|
||||
org.subscriptionEndsAt &&
|
||||
org.subscriptionPeriodEventsCountExceededAt
|
||||
) {
|
||||
return org.subscriptionEndsAt >
|
||||
org.subscriptionPeriodEventsCountExceededAt
|
||||
? org.subscriptionPeriodEventsCountExceededAt
|
||||
: org.subscriptionEndsAt;
|
||||
}
|
||||
|
||||
if (org.subscriptionEndsAt) {
|
||||
return org.subscriptionEndsAt;
|
||||
}
|
||||
|
||||
return new Date();
|
||||
},
|
||||
},
|
||||
isTrial: {
|
||||
needs: { subscriptionStatus: true, subscriptionEndsAt: true },
|
||||
compute(org) {
|
||||
const isSubscriptionInFuture =
|
||||
org.subscriptionEndsAt && org.subscriptionEndsAt > new Date();
|
||||
return (
|
||||
(org.subscriptionStatus === 'trialing' ||
|
||||
org.subscriptionStatus === null) &&
|
||||
isSubscriptionInFuture
|
||||
);
|
||||
},
|
||||
},
|
||||
isCanceled: {
|
||||
needs: { subscriptionStatus: true, subscriptionCanceledAt: true },
|
||||
compute(org) {
|
||||
return isCanceled(org);
|
||||
},
|
||||
},
|
||||
isWillBeCanceled: {
|
||||
needs: {
|
||||
subscriptionStatus: true,
|
||||
subscriptionCanceledAt: true,
|
||||
subscriptionEndsAt: true,
|
||||
},
|
||||
compute(org) {
|
||||
return isWillBeCanceled(org);
|
||||
},
|
||||
},
|
||||
isExpired: {
|
||||
needs: {
|
||||
subscriptionEndsAt: true,
|
||||
subscriptionStatus: true,
|
||||
subscriptionCanceledAt: true,
|
||||
},
|
||||
compute(org) {
|
||||
if (isCanceled(org)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isWillBeCanceled(org)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
org.subscriptionEndsAt && org.subscriptionEndsAt < new Date()
|
||||
);
|
||||
},
|
||||
},
|
||||
isExceeded: {
|
||||
needs: {
|
||||
subscriptionPeriodEventsCount: true,
|
||||
subscriptionPeriodEventsLimit: true,
|
||||
},
|
||||
compute(org) {
|
||||
return (
|
||||
org.subscriptionPeriodEventsCount >
|
||||
org.subscriptionPeriodEventsLimit
|
||||
);
|
||||
},
|
||||
},
|
||||
subscriptionCurrentPeriodStart: {
|
||||
needs: { subscriptionStartsAt: true, subscriptionInterval: true },
|
||||
compute(org) {
|
||||
if (!org.subscriptionStartsAt) return org.subscriptionStartsAt;
|
||||
|
||||
if (org.subscriptionInterval === 'year') {
|
||||
const startDay = org.subscriptionStartsAt.getUTCDate();
|
||||
const now = new Date();
|
||||
return new Date(
|
||||
Date.UTC(
|
||||
now.getUTCFullYear(),
|
||||
now.getUTCMonth(),
|
||||
startDay,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return org.subscriptionStartsAt;
|
||||
},
|
||||
},
|
||||
subscriptionCurrentPeriodEnd: {
|
||||
needs: {
|
||||
subscriptionStartsAt: true,
|
||||
subscriptionEndsAt: true,
|
||||
subscriptionInterval: true,
|
||||
},
|
||||
compute(org) {
|
||||
if (!org.subscriptionStartsAt) return org.subscriptionEndsAt;
|
||||
|
||||
if (org.subscriptionInterval === 'year') {
|
||||
const startDay = org.subscriptionStartsAt.getUTCDate();
|
||||
const now = new Date();
|
||||
return new Date(
|
||||
Date.UTC(
|
||||
now.getUTCFullYear(),
|
||||
now.getUTCMonth() + 1,
|
||||
startDay - 1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return org.subscriptionEndsAt;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return prisma;
|
||||
};
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: ReturnType<typeof getPrismaClient> | undefined;
|
||||
prisma: ReturnType<typeof getPrismaClient>;
|
||||
};
|
||||
|
||||
export const db = globalForPrisma.prisma ?? getPrismaClient();
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
globalForPrisma.prisma = db;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user