Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4dad830
fix: 清理 usage-doc / big-screen i18n 硬编码 + 修复 Next.js params Promise 报错
YangQing-Lin Jan 13, 2026
84c8ce6
feat(leaderboard): add user tag and group filters for user ranking (#…
ding113 Jan 13, 2026
58c262b
feat(leaderboard): add tag/group suggestions dropdown for better UX
ding113 Jan 13, 2026
6a9e51d
docs: update Privnode offer details in README files
ding113 Jan 14, 2026
c9f3a3a
fix: 修复 1M 上下文标头兼容问题
ding113 Jan 18, 2026
ac92b09
fix: resolve container name conflicts in multi-user environments (#625)
SaladDay Jan 18, 2026
472cfd4
feat(dashboard): improve user management, statistics reset, and i18n …
miraserver Jan 19, 2026
745df9b
feat(my-usage): cache statistics and timezone fixes (#623)
miraserver Jan 19, 2026
1cd0877
fix: Set HOSTNAME environment variable in Dockerfile (#622)
yllhwa Jan 19, 2026
09901db
fix: inherit 1M flag from client
ding113 Jan 19, 2026
24aa513
fix(my-usage): make date range DST-safe
ding113 Jan 19, 2026
2305736
test(api): cover leaderboard comma-list parsing
ding113 Jan 19, 2026
e37b8d3
fix(auth): avoid hardcoded server error fallback
ding113 Jan 19, 2026
b72edd7
fix(actions): localize key quota permission errors
ding113 Jan 19, 2026
af4a08d
docs: clarify context 1M inherit behavior
ding113 Jan 19, 2026
62e0c69
test(settings): fix model multi-select messages loader
ding113 Jan 19, 2026
8884820
test(dashboard): provide QueryClientProvider in edit key form test
ding113 Jan 19, 2026
f625975
chore(scripts): avoid hardcoded docker container names
ding113 Jan 19, 2026
17fad8b
fix(i18n): replace fullwidth parentheses in ja dashboard
ding113 Jan 19, 2026
dab9b50
fix(ui): add a11y label and clean up dialog styles
ding113 Jan 19, 2026
4279202
chore: ignore tmp scratch directory
ding113 Jan 19, 2026
d9e85ac
feat: Dashboard Logs:秒级时间筛选 + Session ID 精确筛选/联想/展示(含回归修复) (#611)
YangQing-Lin Jan 19, 2026
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
/coverage-my-usage
/coverage-proxy-guard-pipeline
/coverage-thinking-signature-rectifier
/coverage-logs-sessionid-time-filter
/coverage-usage-logs-sessionid-search

# next.js
/.next/
Expand Down Expand Up @@ -84,3 +86,6 @@ TRANSLATIONS_CHECKLIST.md
# docs-site submodule build artifacts
docs-site/.next/
docs-site/node_modules/

# local scratch
tmp/
3 changes: 1 addition & 2 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ Cubence offers special discount coupons for users of CCH: when purchasing with t
</a>
</td>
<td>
<b>💎 Special Offer</b>: <a href="https://privnode.com/with-cch?utm_source=refLink">Privnode</a> is an affordable AI API aggregation platform providing one-stop relay services for mainstream models like Claude and Codex, serving developers and teams with reliable stability and competitive pricing.<br/>
Use code <code>WITHCCH</code> for <b>15% off</b> → <a href="https://privnode.com/with-cch?utm_source=refLink">Visit Now</a>
<a href="https://privnode.com/with-cch?utm_source=refLink">Privnode</a> is an affordable AI API aggregation platform providing one-stop relay services for mainstream models like Claude and Codex, serving developers and teams with reliable stability and competitive pricing. → <a href="https://privnode.com/with-cch?utm_source=refLink">Visit Now</a>
</td>
</tr>
</table>
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ Cubence 为 CCH 的使用用户提供了特别的优惠折扣:在购买时使
</a>
</td>
<td>
<b>💎 特别优惠</b>:<a href="https://privnode.com/with-cch?utm_source=refLink">Privnode</a> 是一家平价的 AI API 聚合平台,为 Claude、Codex 等主流模型提供一站式中转服务,以良好的稳定性和较高的性价比,服务于开发者与团队的实际需求。<br/>
使用优惠码 <code>WITHCCH</code> 可获得 <b>15% 折扣</b> → <a href="https://privnode.com/with-cch?utm_source=refLink">立即访问</a>
<a href="https://privnode.com/with-cch?utm_source=refLink">Privnode</a> 是一家平价的 AI API 聚合平台,为 Claude、Codex 等主流模型提供一站式中转服务,以良好的稳定性和较高的性价比,服务于开发者与团队的实际需求。 → <a href="https://privnode.com/with-cch?utm_source=refLink">立即访问</a>
</td>
</tr>
</table>
Expand Down
1 change: 1 addition & 0 deletions deploy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ FROM node:trixie-slim AS runner
ENV NODE_ENV=production
ENV PORT=3000
ENV HOST=0.0.0.0
ENV HOSTNAME=0.0.0.0
WORKDIR /app

# 安装 PostgreSQL 18 客户端工具(用于数据库备份/恢复功能)和 curl(用于健康检查)
Expand Down
5 changes: 2 additions & 3 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
name: ${COMPOSE_PROJECT_NAME:-claude-code-hub}

services:
postgres:
image: postgres:18
container_name: claude-code-hub-db
restart: unless-stopped
# 不对外暴露数据库端口,仅允许容器内部网络访问
# 如需调试,可取消注释下行(仅绑定本机):
Expand Down Expand Up @@ -31,7 +32,6 @@ services:

redis:
image: redis:7-alpine
container_name: claude-code-hub-redis
restart: unless-stopped
volumes:
# 持久化 Redis 数据到本地 ./data/redis 目录
Expand All @@ -47,7 +47,6 @@ services:

app:
image: ghcr.io/ding113/claude-code-hub:latest
container_name: claude-code-hub-app
depends_on:
postgres:
condition: service_healthy
Expand Down
119 changes: 119 additions & 0 deletions docs/dashboard-logs-callchain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Dashboard Logs(Usage Logs)入口与调用链盘点

本文用于锁定 `/dashboard/logs` 的真实入口与关键调用链边界,避免后续需求实现与验收口径跑偏。

## 1) 路由入口(Server)

- 路由:`/dashboard/logs`
- 入口页面:`src/app/[locale]/dashboard/logs/page.tsx`
- 登录态校验:`getSession()`(未登录重定向到 `/login`)
- 数据区块入口:`UsageLogsDataSection`(`src/app/[locale]/dashboard/logs/_components/usage-logs-sections.tsx`)

## 2) 真实渲染链路(Client)

当前页面实际使用“虚拟列表”链路:

- 虚拟列表入口:`UsageLogsViewVirtualized`(`src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx`)
- URL -> filters 解析:`parseLogsUrlFilters()`(`src/app/[locale]/dashboard/logs/_utils/logs-query.ts`)
- filters -> URL 回填:`buildLogsUrlQuery()`(`src/app/[locale]/dashboard/logs/_utils/logs-query.ts`)
- Filters 面板:`UsageLogsFilters`
- 列表:`VirtualizedLogsTable`
- 统计面板:`UsageLogsStatsPanel`

仓库内仍存在“非虚拟表格”实现(目前不被路由引用,属于历史/备用路径):

- `UsageLogsView`(`src/app/[locale]/dashboard/logs/_components/usage-logs-view.tsx`)
- `UsageLogsTable`(`src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx`)

## 3) 过滤器 / URL / 时间语义

- URL 参数解析/构建(统一入口):`src/app/[locale]/dashboard/logs/_utils/logs-query.ts`
- `sessionId`:字符串(trim 后空值不落盘)
- `startTime/endTime`:毫秒时间戳
- 秒级时间工具:`src/app/[locale]/dashboard/logs/_utils/time-range.ts`
- UI endTime 为“包含式”秒;对后端转换为“排他上界”(`endExclusive = endInclusive + 1s`)
- 后端查询语义保持:`created_at >= startTime` 且 `created_at < endTime`

## 4) 数据获取链路(Actions -> Repository)

### 列表(无限滚动)

- Action:`src/actions/usage-logs.ts#getUsageLogsBatch`
- Repo:`src/repository/usage-logs.ts#findUsageLogsBatch`

### 统计(折叠面板按需加载)

- Action:`src/actions/usage-logs.ts#getUsageLogsStats`
- Repo:`src/repository/usage-logs.ts#findUsageLogsStats`

### 导出 CSV

- Action:`src/actions/usage-logs.ts#exportUsageLogs`
- Repo:`src/repository/usage-logs.ts#findUsageLogsWithDetails`
- CSV 生成:`src/actions/usage-logs.ts#generateCsv`

### Session ID 联想(候选查询)

- Action:`src/actions/usage-logs.ts#getUsageLogSessionIdSuggestions`
- Repo:`src/repository/usage-logs.ts#findUsageLogSessionIdSuggestions`

#### 匹配语义与边界(2026-01-15 更新)

- **前端约束**:
- 最小长度:`SESSION_ID_SUGGESTION_MIN_LEN`(`src/lib/constants/usage-logs.constants.ts`)
- 最大长度截断:`SESSION_ID_SUGGESTION_MAX_LEN`(`src/actions/usage-logs.ts` 内对输入 trim 后截断)
- 每次返回数量:`SESSION_ID_SUGGESTION_LIMIT`
- **后端匹配**:
- 语义:仅支持「字面量前缀匹配」(`term%`),不再支持包含匹配(`%term%`)
- 安全:输入中的 `%` / `_` / `\\` 会被统一转义,避免被当作 LIKE 通配符
- SQL(核心条件):`session_id LIKE '<escapedTerm>%' ESCAPE '\\'`
- 转义实现:`src/repository/_shared/like.ts#escapeLike`
- **行为变更示例**:
- 之前:输入 `abc` 可能命中 `xxxabcxxx`(包含匹配)
- 之后:仅命中 `abc...`(前缀匹配)
- 之前:输入 `%` / `_` 可主动触发通配
- 之后:`%` / `_` 按字面量处理(例如输入 `%a` 只匹配以 `%a` 开头的 session_id)

#### 索引与迁移(前缀匹配性能)

- 已有索引:`idx_message_request_session_id`(`message_request.session_id`,partial: `deleted_at IS NULL`)
- 新增索引(前缀匹配):`idx_message_request_session_id_prefix`
- opclass:`varchar_pattern_ops`
- partial:`deleted_at IS NULL AND (blocked_by IS NULL OR blocked_by <> 'warmup')`
- 迁移文件:`drizzle/0055_neat_stepford_cuckoos.sql`

## 5) 本需求相关影响面(文件/符号清单)

**前端(logs 页面内聚)**:

- URL/过滤器:`src/app/[locale]/dashboard/logs/_utils/logs-query.ts`
- 秒级时间:`src/app/[locale]/dashboard/logs/_utils/time-range.ts`
- 过滤器 UI:`src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx`
- 虚拟列表:`src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx`
- 非虚拟表格:`src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx`
- 统计面板:`src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx`

**后端(Actions/Repo)**:

- Actions:`src/actions/usage-logs.ts`
- `getUsageLogsBatch/getUsageLogsStats/exportUsageLogs/getUsageLogSessionIdSuggestions`
- Repo:`src/repository/usage-logs.ts`
- `findUsageLogsBatch/findUsageLogsWithDetails/findUsageLogsStats/findUsageLogSessionIdSuggestions`

**i18n(用户可见文案)**:

- `messages/*/dashboard.json`(`dashboard.logs.filters.*` / `dashboard.logs.columns.*`)

## 6) 边界说明(在范围内 / 不在范围内)

在范围内(本次需求直接相关):

- `sessionId` 精确筛选 + URL 回填 + UI 展示(列/复制/tooltip)
- 秒级时间输入与 `endExclusive` 语义对齐(`< endTime`)
- Session ID 联想(最小成本:minLen + debounce + limit)

不在范围内(需另开 issue/评审确认后再做):

- 针对联想查询的索引/物化/离线表(优化类工程)
- 大规模改动数据库 schema 或重建索引策略(例如 CONCURRENTLY/离线重建)
- Logs 页面其它过滤项语义调整(非本需求验收口径)
26 changes: 26 additions & 0 deletions docs/error-session-id-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Error Session ID Guide

When reporting an API error, include the CCH session id so maintainers can locate the exact request.

## Where to find it

1. **Preferred**: response header `x-cch-session-id`
2. **Fallback**: `error.message` suffix `cch_session_id: <id>`

If the response does not include a session id, the server could not determine it for that request.

## Example (curl)

```bash
curl -i -sS \\
-H "Authorization: Bearer <your-key>" \\
-H "Content-Type: application/json" \\
-d '{"model":"gpt-4.1-mini","messages":[{"role":"user","content":"hi"}]}' \\
http://localhost:13500/v1/chat/completions
```

In the response:

- Check header: `x-cch-session-id: ...`
- If missing, check JSON: `{"error":{"message":"... (cch_session_id: ...)"} }`

1 change: 1 addition & 0 deletions drizzle/0055_neat_stepford_cuckoos.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE INDEX IF NOT EXISTS "idx_message_request_session_id_prefix" ON "message_request" USING btree ("session_id" varchar_pattern_ops) WHERE "message_request"."deleted_at" IS NULL AND ("message_request"."blocked_by" IS NULL OR "message_request"."blocked_by" <> 'warmup');
Loading
Loading