Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions messages/en/quota.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@
"label": "Monthly Cost",
"resetAt": "Resets at"
},
"costTotal": {
"label": "Total Cost"
},
"concurrentSessions": {
"label": "Concurrent Sessions"
},
Expand Down Expand Up @@ -183,6 +186,7 @@
"costDaily": "Daily Quota",
"costWeekly": "Weekly Quota",
"costMonthly": "Monthly Quota",
"costTotal": "Total Quota",
"concurrentSessions": "Concurrent Limit",
"status": "Status",
"actions": "Actions"
Expand Down Expand Up @@ -235,6 +239,11 @@
"placeholder": "Unlimited",
"current": "Current usage: {currency}{current} / {currency}{limit}"
},
"limitTotalUsd": {
"label": "Total Quota (USD)",
"placeholder": "Unlimited",
"current": "Current usage: {currency}{current} / {currency}{limit}"
},
"concurrentSessions": {
"label": "Concurrent Session Quota",
"placeholder": "0 = Unlimited",
Expand Down
9 changes: 9 additions & 0 deletions messages/ja/quota.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@
"label": "月次コスト",
"resetAt": "リセット時刻"
},
"costTotal": {
"label": "総コスト"
},
"concurrentSessions": {
"label": "同時セッション"
},
Expand Down Expand Up @@ -160,6 +163,7 @@
"costDaily": "日次クォータ",
"costWeekly": "週次クォータ",
"costMonthly": "月次クォータ",
"costTotal": "総クォータ",
"concurrentSessions": "同時制限",
"status": "ステータス",
"actions": "アクション"
Expand Down Expand Up @@ -212,6 +216,11 @@
"placeholder": "無制限",
"current": "現在使用: {currency}{current} / {currency}{limit}"
},
"limitTotalUsd": {
"label": "総クォータ (USD)",
"placeholder": "無制限",
"current": "現在使用: {currency}{current} / {currency}{limit}"
},
"concurrentSessions": {
"label": "同時セッションクォータ",
"placeholder": "0 = 無制限",
Expand Down
9 changes: 9 additions & 0 deletions messages/ru/quota.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@
"label": "Ежемесячные расходы",
"resetAt": "Сброс в"
},
"costTotal": {
"label": "Общие расходы"
},
"concurrentSessions": {
"label": "Параллельные сессии"
},
Expand Down Expand Up @@ -183,6 +186,7 @@
"costDaily": "Дневная квота",
"costWeekly": "Еженедельная квота",
"costMonthly": "Ежемесячная квота",
"costTotal": "Общая квота",
"concurrentSessions": "Лимит параллельных",
"status": "Статус",
"actions": "Действия"
Expand Down Expand Up @@ -235,6 +239,11 @@
"placeholder": "Неограниченно",
"current": "Использовано: {currency}{current} из {currency}{limit}"
},
"limitTotalUsd": {
"label": "Общая квота (USD)",
"placeholder": "Неограниченно",
"current": "Использовано: {currency}{current} из {currency}{limit}"
},
"concurrentSessions": {
"label": "Квота параллельных сессий",
"placeholder": "0 = без ограничений",
Expand Down
9 changes: 9 additions & 0 deletions messages/zh-CN/quota.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@
"label": "月消费",
"resetAt": "重置于"
},
"costTotal": {
"label": "总消费"
},
"concurrentSessions": {
"label": "并发 Session"
},
Expand Down Expand Up @@ -183,6 +186,7 @@
"costDaily": "每日限额",
"costWeekly": "周限额",
"costMonthly": "月限额",
"costTotal": "总限额",
"concurrentSessions": "并发限制",
"status": "状态",
"actions": "操作"
Expand Down Expand Up @@ -235,6 +239,11 @@
"placeholder": "不限制",
"current": "当前已用: {currency}{current} / {currency}{limit}"
},
"limitTotalUsd": {
"label": "总限额(USD)",
"placeholder": "不限制",
"current": "当前已用: {currency}{current} / {currency}{limit}"
},
"concurrentSessions": {
"label": "并发 Session 限额",
"placeholder": "0 = 不限制",
Expand Down
9 changes: 9 additions & 0 deletions messages/zh-TW/quota.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@
"label": "月消費",
"resetAt": "重置於"
},
"costTotal": {
"label": "總消費"
},
"concurrentSessions": {
"label": "並發 Session"
},
Expand Down Expand Up @@ -158,6 +161,7 @@
"costDaily": "每日限額",
"costWeekly": "周限額",
"costMonthly": "月限額",
"costTotal": "總限額",
"concurrentSessions": "並發限制",
"status": "狀態",
"actions": "操作"
Expand Down Expand Up @@ -210,6 +214,11 @@
"placeholder": "不限制",
"current": "當前已用: {currency}{current} / {currency}{limit}"
},
"limitTotalUsd": {
"label": "總限額 (USD)",
"placeholder": "不限制",
"current": "當前已用: {currency}{current} / {currency}{limit}"
},
"concurrentSessions": {
"label": "並發 Session 限額",
"placeholder": "0 = 不限制",
Expand Down
2 changes: 2 additions & 0 deletions src/actions/key-quota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface KeyQuotaItem {
limit: number | null;
mode?: "fixed" | "rolling";
time?: string;
resetAt?: Date;
}

export interface KeyQuotaUsageResult {
Expand Down Expand Up @@ -164,6 +165,7 @@ export async function getKeyQuotaUsage(keyId: number): Promise<ActionResult<KeyQ
type: "limitTotal",
current: totalCost,
limit: parseNumericLimit(keyRow.limitTotalUsd),
resetAt: costResetAt ?? undefined,
},
{
type: "limitSessions",
Expand Down
3 changes: 2 additions & 1 deletion src/actions/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ export async function getKeyLimitUsage(keyId: number): Promise<
costDaily: { current: number; limit: number | null; resetAt?: Date };
costWeekly: { current: number; limit: number | null; resetAt?: Date };
costMonthly: { current: number; limit: number | null; resetAt?: Date };
costTotal: { current: number; limit: number | null };
costTotal: { current: number; limit: number | null; resetAt?: Date };
concurrentSessions: { current: number; limit: number };
}>
> {
Expand Down Expand Up @@ -958,6 +958,7 @@ export async function getKeyLimitUsage(keyId: number): Promise<
costTotal: {
current: totalCost,
limit: key.limitTotalUsd ?? null,
resetAt: costResetAt ?? undefined,
},
concurrentSessions: {
current: concurrentSessions,
Expand Down
61 changes: 49 additions & 12 deletions src/actions/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ export async function getProviders(): Promise<ProviderDisplay[]> {
limitWeeklyUsd: provider.limitWeeklyUsd,
limitMonthlyUsd: provider.limitMonthlyUsd,
limitTotalUsd: provider.limitTotalUsd,
totalCostResetAt: provider.totalCostResetAt,
limitConcurrentSessions: provider.limitConcurrentSessions,
maxRetryAttempts: provider.maxRetryAttempts,
circuitBreakerFailureThreshold: provider.circuitBreakerFailureThreshold,
Expand Down Expand Up @@ -1259,6 +1260,15 @@ export async function resetProviderTotalUsage(providerId: number): Promise<Actio
return { ok: false, error: "供应商不存在" };
}

try {
await publishProviderCacheInvalidation();
} catch (error) {
logger.warn("resetProviderTotalUsage:cache_invalidation_failed", {
providerId,
error: error instanceof Error ? error.message : String(error),
});
}

return { ok: true };
} catch (error) {
logger.error("重置供应商总用量失败:", error);
Expand Down Expand Up @@ -2690,6 +2700,7 @@ export async function getProviderLimitUsage(providerId: number): Promise<
costDaily: { current: number; limit: number | null; resetAt?: Date };
costWeekly: { current: number; limit: number | null; resetAt: Date };
costMonthly: { current: number; limit: number | null; resetAt: Date };
limitTotalUsd: { current: number; limit: number | null; resetAt?: Date };
concurrentSessions: { current: number; limit: number };
}>
> {
Expand All @@ -2713,7 +2724,9 @@ export async function getProviderLimitUsage(providerId: number): Promise<
getTimeRangeForPeriodWithMode,
} = await import("@/lib/rate-limit/time-utils");
const { RateLimitService } = await import("@/lib/rate-limit");
const { sumProviderCostInTimeRange } = await import("@/repository/statistics");
const { sumProviderCostInTimeRange, sumProviderTotalCost } = await import(
"@/repository/statistics"
);
const limit5hResetMode = provider.limit5hResetMode ?? "rolling";

// 计算各周期的时间范围
Expand All @@ -2732,15 +2745,23 @@ export async function getProviderLimitUsage(providerId: number): Promise<
]);

// 获取金额消费(直接查询数据库,确保配额显示与 DB 一致)
const [cost5h, costDaily, costWeekly, costMonthly, concurrentSessions] = await Promise.all([
limit5hResetMode === "fixed"
? RateLimitService.getCurrentCost(providerId, "provider", "5h", undefined, limit5hResetMode)
: sumProviderCostInTimeRange(providerId, range5h.startTime, range5h.endTime),
sumProviderCostInTimeRange(providerId, rangeDaily.startTime, rangeDaily.endTime),
sumProviderCostInTimeRange(providerId, rangeWeekly.startTime, rangeWeekly.endTime),
sumProviderCostInTimeRange(providerId, rangeMonthly.startTime, rangeMonthly.endTime),
SessionTracker.getProviderSessionCount(providerId),
]);
const [cost5h, costDaily, costWeekly, costMonthly, totalCost, concurrentSessions] =
await Promise.all([
limit5hResetMode === "fixed"
? RateLimitService.getCurrentCost(
providerId,
"provider",
"5h",
undefined,
limit5hResetMode
)
: sumProviderCostInTimeRange(providerId, range5h.startTime, range5h.endTime),
sumProviderCostInTimeRange(providerId, rangeDaily.startTime, rangeDaily.endTime),
sumProviderCostInTimeRange(providerId, rangeWeekly.startTime, rangeWeekly.endTime),
sumProviderCostInTimeRange(providerId, rangeMonthly.startTime, rangeMonthly.endTime),
sumProviderTotalCost(providerId, provider.totalCostResetAt),
SessionTracker.getProviderSessionCount(providerId),
]);

// 获取重置时间信息
const resetDaily = await getResetInfoWithMode(
Expand Down Expand Up @@ -2779,6 +2800,11 @@ export async function getProviderLimitUsage(providerId: number): Promise<
limit: provider.limitMonthlyUsd,
resetAt: resetMonthly.resetAt!,
},
limitTotalUsd: {
current: totalCost,
limit: provider.limitTotalUsd ?? null,
resetAt: provider.totalCostResetAt ?? undefined,
},
concurrentSessions: {
current: concurrentSessions,
limit: provider.limitConcurrentSessions || 0,
Expand All @@ -2800,6 +2826,7 @@ export type ProviderLimitUsageData = {
costDaily: { current: number; limit: number | null; resetAt?: Date };
costWeekly: { current: number; limit: number | null; resetAt: Date };
costMonthly: { current: number; limit: number | null; resetAt: Date };
limitTotalUsd: { current: number; limit: number | null; resetAt?: Date };
concurrentSessions: { current: number; limit: number };
};

Expand All @@ -2820,6 +2847,8 @@ export async function getProviderLimitUsageBatch(
limitDailyUsd?: number | null;
limitWeeklyUsd?: number | null;
limitMonthlyUsd?: number | null;
limitTotalUsd?: number | null;
totalCostResetAt?: Date | null;
limitConcurrentSessions?: number | null;
}>
): Promise<Map<number, ProviderLimitUsageData>> {
Expand All @@ -2845,7 +2874,9 @@ export async function getProviderLimitUsageBatch(
getTimeRangeForPeriodWithMode,
} = await import("@/lib/rate-limit/time-utils");
const { RateLimitService } = await import("@/lib/rate-limit");
const { sumProviderCostInTimeRange } = await import("@/repository/statistics");
const { sumProviderCostInTimeRange, sumProviderTotalCost } = await import(
"@/repository/statistics"
);

const providerIds = providers.map((p) => p.id);

Expand All @@ -2871,7 +2902,7 @@ export async function getProviderLimitUsageBatch(
);

// 并行查询该供应商的各周期消费(直接查询数据库)
const [cost5h, resetAt5h, costDaily, costWeekly, costMonthly] = await Promise.all([
const [cost5h, resetAt5h, costDaily, costWeekly, costMonthly, totalCost] = await Promise.all([
limit5hResetMode === "fixed"
? RateLimitService.getCurrentCost(
provider.id,
Expand All @@ -2887,6 +2918,7 @@ export async function getProviderLimitUsageBatch(
sumProviderCostInTimeRange(provider.id, rangeDaily.startTime, rangeDaily.endTime),
sumProviderCostInTimeRange(provider.id, rangeWeekly.startTime, rangeWeekly.endTime),
sumProviderCostInTimeRange(provider.id, rangeMonthly.startTime, rangeMonthly.endTime),
sumProviderTotalCost(provider.id, provider.totalCostResetAt ?? null),
]);

const sessionCount = sessionCountMap.get(provider.id) || 0;
Expand Down Expand Up @@ -2926,6 +2958,11 @@ export async function getProviderLimitUsageBatch(
limit: provider.limitMonthlyUsd ?? null,
resetAt: resetMonthly.resetAt!,
},
limitTotalUsd: {
current: totalCost,
limit: provider.limitTotalUsd ?? null,
resetAt: provider.totalCostResetAt ?? undefined,
},
concurrentSessions: {
current: sessionCount,
limit: provider.limitConcurrentSessions || 0,
Expand Down
7 changes: 4 additions & 3 deletions src/app/[locale]/dashboard/_components/dashboard-header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useTranslations } from "next-intl";
import { getTranslations } from "next-intl/server";
import { VersionUpdateNotifier } from "@/components/customs/version-update-notifier";
import { Button } from "@/components/ui/button";
import { LanguageSwitcher } from "@/components/ui/language-switcher";
Expand All @@ -11,10 +11,11 @@ import { UserMenu } from "./user-menu";

interface DashboardHeaderProps {
session: AuthSession | null;
locale: string;
}

export function DashboardHeader({ session }: DashboardHeaderProps) {
const t = useTranslations("dashboard.nav");
export async function DashboardHeader({ session, locale }: DashboardHeaderProps) {
const t = await getTranslations({ locale, namespace: "dashboard.nav" });
const isAdmin = session?.user.role === "admin";

const NAV_ITEMS: (DashboardNavItem & { adminOnly?: boolean })[] = [
Expand Down
10 changes: 8 additions & 2 deletions src/app/[locale]/dashboard/_components/dashboard-sections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,21 @@ export async function DashboardStatisticsSection() {
);
}

export async function DashboardLeaderboardSection({ isAdmin }: { isAdmin: boolean }) {
export async function DashboardLeaderboardSection({
isAdmin,
locale,
}: {
isAdmin: boolean;
locale: string;
}) {
const systemSettings = await getCachedSystemSettings();
const canViewLeaderboard = isAdmin || systemSettings.allowGlobalUsageView;

if (!canViewLeaderboard) {
return null;
}

const t = await getTranslations("dashboard");
const t = await getTranslations({ locale, namespace: "dashboard" });

return (
<div className="space-y-4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface LimitUsageData {
costDaily: { current: number; limit: number | null };
costWeekly: { current: number; limit: number | null };
costMonthly: { current: number; limit: number | null };
costTotal: { current: number; limit: number | null };
costTotal: { current: number; limit: number | null; resetAt?: Date };
concurrentSessions: { current: number; limit: number };
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/[locale]/dashboard/audit-logs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default async function AuditLogsPage({ params }: { params: Promise<{ loca
return redirect({ href: "/dashboard", locale });
}

const t = await getTranslations("auditLogs");
const t = await getTranslations({ locale, namespace: "auditLogs" });

return (
<div className="space-y-6">
Expand Down
Loading
Loading