-
-
Notifications
You must be signed in to change notification settings - Fork 331
total quota fix #1124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
total quota fix #1124
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2690,6 +2690,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 }; | ||
| concurrentSessions: { current: number; limit: number }; | ||
| }> | ||
| > { | ||
|
|
@@ -2713,7 +2714,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"; | ||
|
|
||
| // 计算各周期的时间范围 | ||
|
|
@@ -2732,15 +2735,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), | ||
| SessionTracker.getProviderSessionCount(providerId), | ||
| ]); | ||
|
|
||
| // 获取重置时间信息 | ||
| const resetDaily = await getResetInfoWithMode( | ||
|
|
@@ -2779,6 +2790,10 @@ export async function getProviderLimitUsage(providerId: number): Promise< | |
| limit: provider.limitMonthlyUsd, | ||
| resetAt: resetMonthly.resetAt!, | ||
| }, | ||
| limitTotalUsd: { | ||
| current: totalCost, | ||
| limit: provider.limitTotalUsd ?? null, | ||
| }, | ||
| concurrentSessions: { | ||
| current: concurrentSessions, | ||
| limit: provider.limitConcurrentSessions || 0, | ||
|
|
@@ -2800,6 +2815,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 }; | ||
| concurrentSessions: { current: number; limit: number }; | ||
| }; | ||
|
|
||
|
|
@@ -2820,6 +2836,7 @@ export async function getProviderLimitUsageBatch( | |
| limitDailyUsd?: number | null; | ||
| limitWeeklyUsd?: number | null; | ||
| limitMonthlyUsd?: number | null; | ||
| limitTotalUsd?: number | null; | ||
| limitConcurrentSessions?: number | null; | ||
|
Comment on lines
+2839
to
2840
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The limitTotalUsd?: number | null;
totalCostResetAt?: Date | null;
limitConcurrentSessions?: number | null; |
||
| }> | ||
| ): Promise<Map<number, ProviderLimitUsageData>> { | ||
|
|
@@ -2845,7 +2862,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); | ||
|
|
||
|
|
@@ -2871,7 +2890,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, | ||
|
|
@@ -2887,6 +2906,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), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment on lines
2906
to
+2909
|
||
| ]); | ||
|
|
||
| const sessionCount = sessionCountMap.get(provider.id) || 0; | ||
|
|
@@ -2926,6 +2946,10 @@ export async function getProviderLimitUsageBatch( | |
| limit: provider.limitMonthlyUsd ?? null, | ||
| resetAt: resetMonthly.resetAt!, | ||
| }, | ||
| limitTotalUsd: { | ||
| current: totalCost, | ||
| limit: provider.limitTotalUsd ?? null, | ||
| }, | ||
| concurrentSessions: { | ||
| current: sessionCount, | ||
| limit: provider.limitConcurrentSessions || 0, | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] [ERROR-NO-USER-FEEDBACK] Current-usage tooltip hidden when total limit is exactly Why this is a problem: The condition Suggested fix: |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -32,6 +32,7 @@ interface KeyQuota { | |||||||||||||||||||||||||||||||
| costDaily: { current: number; limit: number | null; resetAt?: Date }; | ||||||||||||||||||||||||||||||||
| costWeekly: { current: number; limit: number | null }; | ||||||||||||||||||||||||||||||||
| costMonthly: { current: number; limit: number | null }; | ||||||||||||||||||||||||||||||||
| costTotal: { current: number; limit: number | null }; | ||||||||||||||||||||||||||||||||
| concurrentSessions: { current: number; limit: number }; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -79,6 +80,9 @@ export function EditKeyQuotaDialog({ | |||||||||||||||||||||||||||||||
| const [limitMonthly, setLimitMonthly] = useState<string>( | ||||||||||||||||||||||||||||||||
| currentQuota?.costMonthly.limit?.toString() ?? "" | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| const [limitTotal, setLimitTotal] = useState<string>( | ||||||||||||||||||||||||||||||||
| currentQuota?.costTotal.limit?.toString() ?? "" | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| const [limitConcurrent, setLimitConcurrent] = useState<string>( | ||||||||||||||||||||||||||||||||
| currentQuota?.concurrentSessions.limit?.toString() ?? "0" | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
|
|
@@ -98,6 +102,7 @@ export function EditKeyQuotaDialog({ | |||||||||||||||||||||||||||||||
| dailyResetTime: resetTime, | ||||||||||||||||||||||||||||||||
| limitWeeklyUsd: limitWeekly ? parseFloat(limitWeekly) : null, | ||||||||||||||||||||||||||||||||
| limitMonthlyUsd: limitMonthly ? parseFloat(limitMonthly) : null, | ||||||||||||||||||||||||||||||||
| limitTotalUsd: limitTotal ? parseFloat(limitTotal) : null, | ||||||||||||||||||||||||||||||||
| limitConcurrentSessions: limitConcurrent ? parseInt(limitConcurrent, 10) : 0, | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -127,6 +132,7 @@ export function EditKeyQuotaDialog({ | |||||||||||||||||||||||||||||||
| dailyResetTime: resetTime, | ||||||||||||||||||||||||||||||||
| limitWeeklyUsd: null, | ||||||||||||||||||||||||||||||||
| limitMonthlyUsd: null, | ||||||||||||||||||||||||||||||||
| limitTotalUsd: null, | ||||||||||||||||||||||||||||||||
| limitConcurrentSessions: 0, | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -335,6 +341,32 @@ export function EditKeyQuotaDialog({ | |||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| {/* 总限额 */} | ||||||||||||||||||||||||||||||||
| <div className="grid gap-1.5"> | ||||||||||||||||||||||||||||||||
| <Label htmlFor="limitTotal" className="text-xs"> | ||||||||||||||||||||||||||||||||
| {t("limitTotalUsd.label")} | ||||||||||||||||||||||||||||||||
| </Label> | ||||||||||||||||||||||||||||||||
| <Input | ||||||||||||||||||||||||||||||||
| id="limitTotal" | ||||||||||||||||||||||||||||||||
| type="number" | ||||||||||||||||||||||||||||||||
| step="0.01" | ||||||||||||||||||||||||||||||||
| min="0" | ||||||||||||||||||||||||||||||||
| placeholder={t("limitTotalUsd.placeholder")} | ||||||||||||||||||||||||||||||||
| value={limitTotal} | ||||||||||||||||||||||||||||||||
| onChange={(e) => setLimitTotal(e.target.value)} | ||||||||||||||||||||||||||||||||
| className="h-9" | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| {currentQuota?.costTotal.limit && ( | ||||||||||||||||||||||||||||||||
| <p className="text-xs text-muted-foreground"> | ||||||||||||||||||||||||||||||||
| {t("limitTotalUsd.current", { | ||||||||||||||||||||||||||||||||
| currency: currencySymbol, | ||||||||||||||||||||||||||||||||
| current: Number(currentQuota.costTotal.current).toFixed(4), | ||||||||||||||||||||||||||||||||
| limit: Number(currentQuota.costTotal.limit).toFixed(2), | ||||||||||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||
|
Comment on lines
+353
to
+366
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The condition
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: src/app/[locale]/dashboard/quotas/keys/_components/edit-key-quota-dialog.tsx
Line: 351-364
Comment:
**Current-usage tooltip hidden when limit is exactly `0`**
The condition `currentQuota?.costTotal.limit && …` is falsy when `limit` is `0`. Because an admin can legitimately set a `limitTotalUsd` of `0` to hard-block a key, the tooltip won't appear in that case even though there is an active limit.
```suggestion
{currentQuota?.costTotal.limit !== null && currentQuota?.costTotal.limit !== undefined && (
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| {/* 并发限额 */} | ||||||||||||||||||||||||||||||||
| <div className="grid gap-1.5"> | ||||||||||||||||||||||||||||||||
| <Label htmlFor="limitConcurrent" className="text-xs"> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sumProviderTotalCostis invoked without the provider’s reset marker, so this path always sums lifetime ledger rows even afterresetProviderTotalUsageupdatesproviders.total_cost_reset_at. That makes quota usage shown by this action diverge from enforcement (RateLimitService.checkTotalCostLimit(..., { resetAt })), and providers can appear over the total cap immediately after an admin reset. Passprovider.totalCostResetAtto the total-cost aggregation here (and in the batch variant) to keep displayed usage consistent with runtime limit checks.Useful? React with 👍 / 👎.