feat(project): init
This commit is contained in:
101
lib/quota.ts
Normal file
101
lib/quota.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import "server-only";
|
||||
|
||||
import { toPublicUser } from "@/lib/auth";
|
||||
import {
|
||||
getSettingsSnapshot,
|
||||
getUsageForUser,
|
||||
listUserSummaries,
|
||||
} from "@/lib/db";
|
||||
import { getCurrentPeriodKey } from "@/lib/quota-core";
|
||||
import type { AdminSnapshot, QuotaSnapshot, UserRecord } from "@/lib/types";
|
||||
|
||||
type QuotaUser = Pick<UserRecord, "id" | "role" | "monthlyQuota">;
|
||||
|
||||
export function getQuotaSnapshotForUser(
|
||||
user: QuotaUser | null | undefined,
|
||||
): QuotaSnapshot {
|
||||
const periodKey = getCurrentPeriodKey();
|
||||
|
||||
if (!user || user.role === "admin") {
|
||||
return {
|
||||
periodKey,
|
||||
limit: null,
|
||||
used: user ? getUsageForUser(user.id, periodKey) : 0,
|
||||
remaining: null,
|
||||
isUnlimited: true,
|
||||
};
|
||||
}
|
||||
|
||||
const settings = getSettingsSnapshot();
|
||||
const limit =
|
||||
user.monthlyQuota === null
|
||||
? settings.defaultMonthlyQuota
|
||||
: user.monthlyQuota;
|
||||
const used = getUsageForUser(user.id, periodKey);
|
||||
|
||||
return {
|
||||
periodKey,
|
||||
limit,
|
||||
used,
|
||||
remaining: Math.max(limit - used, 0),
|
||||
isUnlimited: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function assertCanGenerate(user: QuotaUser): QuotaSnapshot {
|
||||
const quotaSnapshot = getQuotaSnapshotForUser(user);
|
||||
const remaining = quotaSnapshot.remaining ?? 0;
|
||||
|
||||
if (!quotaSnapshot.isUnlimited && remaining <= 0) {
|
||||
const error = new Error(
|
||||
"本月额度已用完,请联系管理员调整额度。",
|
||||
) as Error & {
|
||||
statusCode?: number;
|
||||
};
|
||||
error.statusCode = 403;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return quotaSnapshot;
|
||||
}
|
||||
|
||||
export function buildAdminQuotaSummaries(): AdminSnapshot {
|
||||
const settings = getSettingsSnapshot();
|
||||
const periodKey = getCurrentPeriodKey();
|
||||
const users = listUserSummaries(periodKey).map((user) => {
|
||||
const publicUser = toPublicUser(user);
|
||||
|
||||
if (!publicUser) {
|
||||
throw new Error("Failed to sanitize user record.");
|
||||
}
|
||||
|
||||
if (user.role === "admin") {
|
||||
return {
|
||||
...publicUser,
|
||||
usedThisPeriod: user.usedThisPeriod,
|
||||
limit: null,
|
||||
remaining: null,
|
||||
isUnlimited: true,
|
||||
};
|
||||
}
|
||||
|
||||
const limit =
|
||||
user.monthlyQuota === null
|
||||
? settings.defaultMonthlyQuota
|
||||
: user.monthlyQuota;
|
||||
|
||||
return {
|
||||
...publicUser,
|
||||
usedThisPeriod: user.usedThisPeriod,
|
||||
limit,
|
||||
remaining: Math.max(limit - user.usedThisPeriod, 0),
|
||||
isUnlimited: false,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
settings,
|
||||
periodKey,
|
||||
users,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user