Conversation
*Total -- 1,193.12kb -> 874.91kb (26.67%) /public/readme/排行榜.png -- 158.35kb -> 110.57kb (30.17%) /public/readme/日志.png -- 265.04kb -> 193.59kb (26.96%) /public/readme/首页.png -- 365.05kb -> 269.36kb (26.21%) /public/readme/供应商管理.png -- 404.68kb -> 301.38kb (25.53%) Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
…time Add daily limit feature for keys and providers with a configurable daily reset time (HH:mm). Extend DB schema with limit_daily_usd and daily_reset_time, update rate-limit service to calculate and enforce daily limits using per-day windows with TTL based on reset time, extend time-utils to include daily period handling and next reset computations, and update transformers, repositories, and API handlers to propagate new fields. Also fix provider UI dialogs width for consistent layout.
…er-page-size-1e1f7f0)
[ImgBot] Optimize images
新增 `dailyResetMode` 配置项,允许用户选择每日限额的重置方式: - `fixed`: 在每天固定时间点重置(默认行为) - `rolling`: 从首次调用开始计算,24小时内滚动重置 该功能影响 Key 和 Provider 的限流逻辑,并更新了相关数据库字段、表单控件及 Redis 脚本。前端界面已添加对应选项和描述说明,确保用户可理解两种模式的区别。 同时扩展了 RateLimitService 中的时间范围和 TTL 计算方法以兼容新模式, 并引入两个新的 Lua 脚本用于处理 Redis 中的滚动窗口数据。
…er-page-size-fc62260)
移除了页面组件中重复声明的 configPath 变量,该变量在代码中已经正确定义, 重复声明可能导致潜在的逻辑错误或维护问题。此修复确保了配置路径的正确性和代码的简洁性。
- 在密钥和提供商表单中添加日重置模式选择器(固定窗口/滚动窗口) - 更新验证模式以包含新的daily_reset_mode字段 - 修复仓储层查询遗漏dailyResetMode字段的问题 - 改进日志记录以避免敏感数据泄露,仅在开发环境输出 - 为所有语言文件添加缺失的配置路径翻译key - 支持Linux系统的配置文件路径并修正JSON代码块语法 - 统一限流服务调用以正确处理不同模式的日限额计算 - 调整返回类型使其支持滚动模式下的可选重置时间 - 更新用户界面文案以统一占位符语法
…er-page-size-59a0326)
…et-time-fix-provider-page-size feat(rate-limit): add daily per-day limit with customizable reset time and fix provider page UI
- 将 `costDaily.resetAt` 字段设为可选,处理可能不存在重置时间的情况 - 在显示重置时间前添加条件检查,避免未定义值导致的渲染错误 - 更新相关组件中的类型定义,确保类型一致性 - 修复 `formatDateDistance` 函数调用,传入当前日期作为参考时间
- 新增统一的错误响应格式文档,详细说明各种错误场景 - 添加限流排查脚本,便于调试和诊断消费限额问题 - 在供应商选择器中增加详细的限流和熔断器过滤信息 - 重构错误响应构建方法,支持错误类型代码和详细上下文信息 - 为前端和CLI客户端提供结构化的错误数据,便于友好显示和问题定位
- 添加对selectionContext中filteredProviders的空值检查 - 通过可选链操作符安全访问可能为空的属性 - 重构条件逻辑以正确处理不同错误场景 - 避免在filteredProviders为undefined时抛出异常
- 在错误详情对话框中增强限流和熔断错误的可视化展示,区分 JSON 错误和纯文本错误 - 新增"被过滤的供应商"显示区域,在成功请求时展示因限流或熔断被排除的供应商 - 改进错误消息解析逻辑,为限流、熔断和混合不可用错误提供专门的UI样式 - 更新错误处理器,在供应商不可用时记录详细的错误信息到数据库 - 优化 ProviderSelector 的错误判断逻辑,更准确地区分不同类型的不可用状态 - 添加多语言支持,为所有语言版本增加 filteredProviders 翻译字段 - 增强错误信息展示的用户体验,提供更清晰的错误原因和供应商状态说明
- 重命名 0018_square_ozymandias.sql → 0021_square_ozymandias.sql - 重命名 0019_open_stephen_strange.sql → 0022_open_stephen_strange.sql - 重命名 0020_nosy_synch.sql → 0023_nosy_synch.sql - 更新 drizzle/meta/_journal.json 添加新的迁移条目 - 确保与上游 zsio/claude-code-hub 的迁移文件不冲突
- 合并 0021_square_ozymandias.sql, 0022_open_stephen_strange.sql, 0023_nosy_synch.sql 到 0021_daily_cost_limits.sql - 包含完整的每日成本限额功能:字段添加、约束设置、重置模式 - 更新 drizzle/meta/_journal.json 移除多余条目 - 提升迁移文件可维护性和执行效率
feat 新增每日消费限额功能,完善限额日志显示
代码审查报告 - PR #161 每日消费限额功能总体评价这是一个功能完整的新特性 PR,为 Key 和 Provider 新增了每日消费限额功能,支持两种重置模式(固定时间/滚动窗口)。代码质量较高,实现逻辑清晰。 ✅ 优点
|
- Fixes react/no-unescaped-entities error in api-test-button.tsx:574 - Replaced straight quotes with HTML entities (“ and ”) - Resolves CI build failure in PR Build Check workflow
🤖 自动修复 PR #161 的 CI 失败
代码审查总结这是一个功能完善的每日消费限额功能实现,整体质量很高。以下是详细的审查意见: ✅ 优点
|
| * 所有自然时间窗口使用配置的时区(Asia/Shanghai) | ||
| */ | ||
| export function getTimeRangeForPeriod(period: TimePeriod): TimeRange { | ||
| export function getTimeRangeForPeriod(period: TimePeriod, resetTime = "00:00"): TimeRange { |
There was a problem hiding this comment.
时区硬编码问题
当前时区固定为 Asia/Shanghai,可能不适合国际化部署场景。
建议:
- 考虑支持用户级别或系统级别的时区配置
- 在文档中明确说明时区行为和影响范围
- 对于全球部署,建议提供时区配置选项
示例:
// 支持用户级时区
export function getTimeRangeForPeriod(
period: TimePeriod,
resetTime = "00:00",
userTimezone?: string
): TimeRange {
const timezone = userTimezone || getEnvConfig().TZ;
// ...
}
src/drizzle/schema.ts
Outdated
| // 金额限流配置 | ||
| limit5hUsd: numeric('limit_5h_usd', { precision: 10, scale: 2 }), | ||
| limitDailyUsd: numeric('limit_daily_usd', { precision: 10, scale: 2 }), | ||
| dailyResetMode: varchar('daily_reset_mode', { length: 10 }) |
There was a problem hiding this comment.
类型安全性改进建议
当前 daily_reset_mode 使用 varchar,建议使用 Drizzle 的枚举类型增强类型安全:
// 在文件开头定义枚举
export const dailyResetModeEnum = pgEnum('daily_reset_mode', ['fixed', 'rolling']);
// 使用枚举替代 varchar
dailyResetMode: dailyResetModeEnum('daily_reset_mode').default('fixed').notNull()优点:
- 数据库层面约束,防止非法值插入
- TypeScript 类型推导更准确
- 迁移时自动验证数据合法性
- 更好的 IDE 自动补全支持
|
|
||
| -- Step 1: 添加基础字段 | ||
| ALTER TABLE "keys" ADD COLUMN "limit_daily_usd" numeric(10, 2);--> statement-breakpoint | ||
| ALTER TABLE "keys" ADD COLUMN "daily_reset_time" varchar(5) DEFAULT '00:00';--> statement-breakpoint |
There was a problem hiding this comment.
性能优化建议:添加部分索引
对于有限额的记录,建议添加部分索引提升查询性能:
-- 建议在下一次迁移中添加
CREATE INDEX idx_keys_daily_limit
ON keys(limit_daily_usd, daily_reset_mode)
WHERE limit_daily_usd IS NOT NULL;
CREATE INDEX idx_providers_daily_limit
ON providers(limit_daily_usd, daily_reset_mode)
WHERE limit_daily_usd IS NOT NULL;收益:
- 查询有限额的 keys/providers 时性能提升
- 索引只覆盖有限额的记录,空间占用小
- 支持复合查询(同时过滤限额值和重置模式)
Summary of ChangesHello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 此拉取请求引入了每日消费限额功能,允许用户和提供商设置每日最大消费金额,并可选择固定时间或24小时滚动窗口作为重置模式。这一功能通过更新数据库架构、前端界面和后端限流逻辑实现,旨在提供更精细的资源使用控制和更透明的错误反馈机制。 Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
| <TextField | ||
| label={t("dailyResetTime.label")} | ||
| placeholder={t("dailyResetTime.placeholder")} | ||
| description={t("dailyResetTime.description")} | ||
| type="time" | ||
| step={60} | ||
| {...form.getFieldProps("dailyResetTime")} | ||
| /> |
| <div className="flex-1"> | ||
| <span className="font-medium">{p.name}</span> | ||
| <span className="text-xs ml-2"> | ||
| ({p.reason === 'rate_limited' ? '供应商费用限制' : '熔断器打开'}) |
| {/* 每日重置时间 */} | ||
| <div className="grid gap-1.5"> | ||
| <Label htmlFor="dailyResetTime" className="text-xs"> | ||
| {t("dailyResetTime.label")} | ||
| </Label> | ||
| <Input | ||
| id="dailyResetTime" | ||
| type="time" | ||
| step={60} | ||
| value={resetTime} | ||
| onChange={(e) => setResetTime(e.target.value || "00:00")} | ||
| className="h-9" | ||
| /> | ||
| </div> |
- Add validation to ensure Key's daily limit does not exceed user's daily quota - Apply validation in both addKey and editKey functions - Add dailyResetMode parameter to editKey function signature - Addresses code review feedback from PR #161
- Add dailyResetMode field (fixed/rolling) to EditKeyFormProps - Import and implement Select component for reset mode selection - Conditionally display dailyResetTime input only for 'fixed' mode - Add i18n translations for dailyResetMode in en and zh-CN locales - Maintain consistency with add-key-form implementation - Addresses code review feedback from PR #161
- Add logs.details.reasons.rateLimited and circuitOpen keys to all locales - Replace hardcoded Chinese text with t() function in error-details-dialog - Support 5 languages: en, zh-CN, zh-TW, ja, ru - Improves maintainability and internationalization - Addresses code review feedback from PR #161
- Add dailyResetMode field to EditKeyQuotaDialogProps interface - Implement Select component for reset mode selection (fixed/rolling) - Conditionally display dailyResetTime input only for 'fixed' mode - Include dailyResetMode in both edit and clear quota operations - Add dynamic description text based on selected mode - Update KeyWithQuota interface to include dailyResetMode field - Addresses code review feedback from PR #161
- Add pgEnum definition for daily_reset_mode with values ['fixed', 'rolling']
- Replace varchar type with dailyResetModeEnum in keys and providers tables
- Maintain .default('fixed').notNull() constraints
- Generate migration 0022_simple_stardust.sql for type conversion
- Benefits: database-level constraint, better TypeScript inference, automatic validation
- Addresses code review feedback from PR #161
- Add idx_keys_daily_limit index on keys(limit_daily_usd, daily_reset_mode) - Add idx_providers_daily_limit index on providers(limit_daily_usd, daily_reset_mode) - Use partial indexes (WHERE limit_daily_usd IS NOT NULL) for efficiency - Benefits: faster queries for records with daily limits, smaller index size - Supports compound queries filtering by both limit and reset mode - Addresses code review feedback from PR #161
- Add Redis Key Architecture section to CLAUDE.md - Document fixed vs rolling window naming conventions - Explain why fixed mode needs suffix and rolling mode doesn't - Add detailed examples for all time periods (daily/5h/weekly/monthly) - Include debugging commands and troubleshooting guide - Add inline documentation in rate-limit/service.ts - Benefits: easier debugging, better maintainability, clearer design rationale - Addresses code review feedback from PR #161
代码审查报告感谢提交这个全面的每日消费限额功能!这是一个非常有价值的增强功能。经过详细审查,代码质量很高,架构设计合理。以下是详细的审查意见: ✅ 优点
🔍 需要关注的点1. 数据库迁移文件版本跳号建议:确认 2. 时间工具函数的边界情况在 function getCustomDailyResetTime(now: Date, resetTime: string, timezone: string): Date {
const { hours, minutes } = parseResetTime(resetTime);
const zonedNow = toZonedTime(now, timezone);
const zonedResetToday = buildZonedDate(zonedNow, hours, minutes);
const resetToday = fromZonedTime(zonedResetToday, timezone);
if (now >= resetToday) {
return resetToday; // ✅ 今天已过重置时间,返回今天的重置时间
}
return addDays(resetToday, -1); // ⚠️ 今天未到重置时间,返回昨天的重置时间
}问题:在跨越夏令时(DST)边界时, 建议: // 在时区时间上操作,然后转换回 UTC
const zonedYesterday = addDays(zonedResetToday, -1);
return fromZonedTime(zonedYesterday, timezone);3. Lua 脚本错误处理在
建议:在 PR 合并前进行集成测试,特别是:
4. UI 组件的数据获取性能在
🎯 测试建议
📝 代码风格建议
✨ 总结这是一个高质量的 PR,代码设计合理,文档详尽,考虑了向后兼容性和故障降级。主要关注点是时间处理的边界情况和缺失的测试覆盖。 建议的合并前检查清单:
整体来说,这个功能为系统增加了非常有价值的灵活性,允许用户根据实际需求选择不同的限额模式。代码质量和架构设计都很出色!👏 |
| return resetToday; | ||
| } | ||
|
|
||
| return addDays(resetToday, -1); |
There was a problem hiding this comment.
当前代码在 UTC 时间戳上执行 addDays(resetToday, -1),在跨越夏令时边界时可能产生意外结果。
建议修复:
// 在时区时间上操作日期,然后转换回 UTC
const zonedYesterday = addDays(zonedResetToday, -1);
return fromZonedTime(zonedYesterday, timezone);这样可以确保日期计算在正确的时区上下文中进行,避免 DST 切换导致的小时偏移问题。
| // 金额限流配置 | ||
| limit5hUsd: numeric('limit_5h_usd', { precision: 10, scale: 2 }), | ||
| limitDailyUsd: numeric('limit_daily_usd', { precision: 10, scale: 2 }), | ||
| dailyResetMode: dailyResetModeEnum('daily_reset_mode') |
There was a problem hiding this comment.
✅ 类型安全的 Enum 设计
使用 PostgreSQL Enum 而不是 VARCHAR 约束,提供了:
- 数据库层面的类型检查:无效值会被数据库拒绝
- 更好的性能:Enum 在数据库内部存储为整数
- 清晰的 API 契约:TypeScript 类型直接映射到数据库 Enum
这是最佳实践!🎯
注意:如果未来需要添加新的模式(如 weekly_rolling),需要执行 ALTER TYPE 迁移:
ALTER TYPE daily_reset_mode ADD VALUE 'weekly_rolling';| @@ -206,6 +206,171 @@ src/ | |||
| 4. **Redis Lua 脚本** - 原子性检查和递增(解决竞态条件) | |||
| 5. **Fail Open 策略** - Redis 不可用时降级,不影响服务 | |||
There was a problem hiding this comment.
📚 优秀的文档实践
这段 Redis Key 命名规范文档非常详尽,包含了:
- 核心设计原则和权衡
- 具体的 Key 命名模式和示例
- 数据结构对比表
- 调试和监控指南
- 常见问题排查
这对于后续的运维和故障排查至关重要。建议将这段文档也同步到项目的 Wiki 或专门的运维手册中,便于团队成员查阅。
The previous CI run (19575549792) started at 15:38:51 UTC but the format fix commit (f9272f3) was pushed at 15:39:17 UTC. This empty commit triggers a new CI run with the corrected code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Changed trailing period from ASCII `.` to Chinese `。` for consistency with the rest of the Chinese text in the documentation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
No description provided.