102 lines
2.3 KiB
TypeScript
102 lines
2.3 KiB
TypeScript
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,
|
|
};
|
|
}
|