Initial import
This commit is contained in:
146
packages/db/src/account-store.ts
Normal file
146
packages/db/src/account-store.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { getApproximateQuotaBucket, type QuotaBucket } from "@nproxy/domain";
|
||||
import type { PrismaClient, SubscriptionStatus } from "@prisma/client";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { prisma as defaultPrisma } from "./prisma-client.js";
|
||||
|
||||
export interface UserAccountOverview {
|
||||
user: {
|
||||
id: string;
|
||||
email: string;
|
||||
isAdmin: boolean;
|
||||
createdAt: Date;
|
||||
};
|
||||
subscription: {
|
||||
id: string;
|
||||
status: SubscriptionStatus;
|
||||
renewsManually: boolean;
|
||||
activatedAt?: Date;
|
||||
currentPeriodStart?: Date;
|
||||
currentPeriodEnd?: Date;
|
||||
canceledAt?: Date;
|
||||
plan: {
|
||||
id: string;
|
||||
code: string;
|
||||
displayName: string;
|
||||
monthlyRequestLimit: number;
|
||||
monthlyPriceUsd: number;
|
||||
billingCurrency: string;
|
||||
isActive: boolean;
|
||||
};
|
||||
} | null;
|
||||
quota: {
|
||||
approximateBucket: QuotaBucket;
|
||||
usedSuccessfulRequests: number;
|
||||
monthlyRequestLimit: number;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export function createPrismaAccountStore(database: PrismaClient = defaultPrisma) {
|
||||
return {
|
||||
async getUserAccountOverview(userId: string): Promise<UserAccountOverview | null> {
|
||||
const user = await database.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const subscription = await database.subscription.findFirst({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
include: {
|
||||
plan: true,
|
||||
},
|
||||
orderBy: [
|
||||
{ currentPeriodEnd: "desc" },
|
||||
{ createdAt: "desc" },
|
||||
],
|
||||
});
|
||||
|
||||
const quota = subscription
|
||||
? await buildQuotaSnapshot(database, userId, {
|
||||
monthlyRequestLimit: subscription.plan.monthlyRequestLimit,
|
||||
cycleStart:
|
||||
subscription.currentPeriodStart ??
|
||||
subscription.activatedAt ??
|
||||
subscription.createdAt,
|
||||
})
|
||||
: null;
|
||||
|
||||
return {
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
isAdmin: user.isAdmin,
|
||||
createdAt: user.createdAt,
|
||||
},
|
||||
subscription: subscription
|
||||
? {
|
||||
id: subscription.id,
|
||||
status: subscription.status,
|
||||
renewsManually: subscription.renewsManually,
|
||||
...(subscription.activatedAt ? { activatedAt: subscription.activatedAt } : {}),
|
||||
...(subscription.currentPeriodStart
|
||||
? { currentPeriodStart: subscription.currentPeriodStart }
|
||||
: {}),
|
||||
...(subscription.currentPeriodEnd
|
||||
? { currentPeriodEnd: subscription.currentPeriodEnd }
|
||||
: {}),
|
||||
...(subscription.canceledAt ? { canceledAt: subscription.canceledAt } : {}),
|
||||
plan: {
|
||||
id: subscription.plan.id,
|
||||
code: subscription.plan.code,
|
||||
displayName: subscription.plan.displayName,
|
||||
monthlyRequestLimit: subscription.plan.monthlyRequestLimit,
|
||||
monthlyPriceUsd: decimalToNumber(subscription.plan.monthlyPriceUsd),
|
||||
billingCurrency: subscription.plan.billingCurrency,
|
||||
isActive: subscription.plan.isActive,
|
||||
},
|
||||
}
|
||||
: null,
|
||||
quota,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function decimalToNumber(value: Prisma.Decimal | { toNumber(): number }): number {
|
||||
return value.toNumber();
|
||||
}
|
||||
|
||||
async function buildQuotaSnapshot(
|
||||
database: PrismaClient,
|
||||
userId: string,
|
||||
input: {
|
||||
monthlyRequestLimit: number;
|
||||
cycleStart: Date;
|
||||
},
|
||||
): Promise<UserAccountOverview["quota"]> {
|
||||
const usageAggregation = await database.usageLedgerEntry.aggregate({
|
||||
where: {
|
||||
userId,
|
||||
entryType: "generation_success",
|
||||
createdAt: {
|
||||
gte: input.cycleStart,
|
||||
},
|
||||
},
|
||||
_sum: {
|
||||
deltaRequests: true,
|
||||
},
|
||||
});
|
||||
|
||||
const usedSuccessfulRequests = usageAggregation._sum.deltaRequests ?? 0;
|
||||
|
||||
return {
|
||||
approximateBucket: getApproximateQuotaBucket({
|
||||
used: usedSuccessfulRequests,
|
||||
limit: input.monthlyRequestLimit,
|
||||
}),
|
||||
usedSuccessfulRequests,
|
||||
monthlyRequestLimit: input.monthlyRequestLimit,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user