* esm * wip * wip * wip * wip * wip * wip * subscription notice * wip * wip * wip * fix envs * fix: update docker build * fix * esm/types * delete dashboard :D * add patches to dockerfiles * update packages + catalogs + ts * wip * remove native libs * ts * improvements * fix redirects and fetching session * try fix favicon * fixes * fix * order and resize reportds within a dashboard * improvements * wip * added userjot to dashboard * fix * add op * wip * different cache key * improve date picker * fix table * event details loading * redo onboarding completely * fix login * fix * fix * extend session, billing and improve bars * fix * reduce price on 10M
275 lines
7.7 KiB
TypeScript
275 lines
7.7 KiB
TypeScript
import { createLogger } from '@openpanel/logger';
|
|
import { readReplicas } from '@prisma/extension-read-replicas';
|
|
import { type Organization, PrismaClient } from './generated/prisma/client';
|
|
|
|
export * from './generated/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'],
|
|
})
|
|
.$extends(
|
|
readReplicas({
|
|
url: process.env.DATABASE_URL_REPLICA ?? process.env.DATABASE_URL!,
|
|
}),
|
|
)
|
|
.$extends({
|
|
query: {
|
|
async $allOperations({ operation, model, args, query }) {
|
|
if (
|
|
operation === 'create' ||
|
|
operation === 'update' ||
|
|
operation === 'delete'
|
|
) {
|
|
logger.info('Prisma operation', {
|
|
operation,
|
|
args,
|
|
model,
|
|
});
|
|
}
|
|
return query(args);
|
|
},
|
|
},
|
|
})
|
|
.$extends({
|
|
result: {
|
|
organization: {
|
|
subscriptionStatus: {
|
|
needs: { subscriptionStatus: true, subscriptionCanceledAt: true },
|
|
compute(org) {
|
|
if (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return 'active';
|
|
}
|
|
|
|
return org.subscriptionStatus || 'trialing';
|
|
},
|
|
},
|
|
hasSubscription: {
|
|
needs: { subscriptionStatus: true, subscriptionEndsAt: true },
|
|
compute(org) {
|
|
if (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return false;
|
|
}
|
|
|
|
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 (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return null;
|
|
}
|
|
|
|
if (
|
|
org.subscriptionEndsAt &&
|
|
org.subscriptionPeriodEventsCountExceededAt
|
|
) {
|
|
return org.subscriptionEndsAt >
|
|
org.subscriptionPeriodEventsCountExceededAt
|
|
? org.subscriptionPeriodEventsCountExceededAt
|
|
: org.subscriptionEndsAt;
|
|
}
|
|
|
|
if (org.subscriptionEndsAt) {
|
|
return org.subscriptionEndsAt;
|
|
}
|
|
|
|
// Hedge against edge cases :D
|
|
return new Date(Date.now() + 1000 * 60 * 60 * 24);
|
|
},
|
|
},
|
|
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) {
|
|
if (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return false;
|
|
}
|
|
|
|
return isCanceled(org);
|
|
},
|
|
},
|
|
isWillBeCanceled: {
|
|
needs: {
|
|
subscriptionStatus: true,
|
|
subscriptionCanceledAt: true,
|
|
subscriptionEndsAt: true,
|
|
},
|
|
compute(org) {
|
|
if (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return false;
|
|
}
|
|
|
|
return isWillBeCanceled(org);
|
|
},
|
|
},
|
|
isExpired: {
|
|
needs: {
|
|
subscriptionEndsAt: true,
|
|
subscriptionStatus: true,
|
|
subscriptionCanceledAt: true,
|
|
},
|
|
compute(org) {
|
|
if (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return false;
|
|
}
|
|
|
|
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) {
|
|
if (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return false;
|
|
}
|
|
|
|
return (
|
|
org.subscriptionPeriodEventsCount >
|
|
org.subscriptionPeriodEventsLimit
|
|
);
|
|
},
|
|
},
|
|
subscriptionCurrentPeriodStart: {
|
|
needs: { subscriptionStartsAt: true, subscriptionInterval: true },
|
|
compute(org) {
|
|
if (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return null;
|
|
}
|
|
|
|
if (!org.subscriptionStartsAt) {
|
|
return null;
|
|
}
|
|
|
|
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 (process.env.VITE_SELF_HOSTED === 'true') {
|
|
return null;
|
|
}
|
|
|
|
if (!org.subscriptionStartsAt) {
|
|
return null;
|
|
}
|
|
|
|
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>;
|
|
};
|
|
|
|
export const db = globalForPrisma.prisma ?? getPrismaClient();
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
globalForPrisma.prisma = db;
|
|
}
|