diff --git a/apps/ui/src/App.tsx b/apps/ui/src/App.tsx index abd4099..6958567 100644 --- a/apps/ui/src/App.tsx +++ b/apps/ui/src/App.tsx @@ -15,10 +15,10 @@ import { } from "./core/constants"; import { filterSites, - sortSites, - summarizeSiteTests, type SiteSortState, type SiteTestResult, + sortSites, + summarizeSiteTests, } from "./core/sites"; import type { AdminData, @@ -464,10 +464,10 @@ const App = () => { event.preventDefault(); const form = event.currentTarget as HTMLFormElement; const formData = new FormData(form); - const payload = Object.fromEntries(formData.entries()) as Record< - string, - FormDataEntryValue - >; + const payload: Record = {}; + formData.forEach((value, key) => { + payload[key] = value; + }); try { const result = await apiFetch<{ token: string }>("/api/tokens", { method: "POST", diff --git a/apps/ui/src/core/sites.ts b/apps/ui/src/core/sites.ts index c4e30a5..52a0dfb 100644 --- a/apps/ui/src/core/sites.ts +++ b/apps/ui/src/core/sites.ts @@ -117,7 +117,11 @@ const getSortValue = (site: Site, key: SiteSortKey, today: string) => { case "tokens": return Number(site.call_tokens?.length ?? 0); case "checkin_enabled": - return site.site_type === "new-api" ? (site.checkin_enabled ? "已开启" : "已关闭") : "-"; + return site.site_type === "new-api" + ? site.checkin_enabled + ? "已开启" + : "已关闭" + : "-"; case "checkin": return getSiteCheckinLabel(site, today); default: diff --git a/apps/ui/src/features/DashboardView.tsx b/apps/ui/src/features/DashboardView.tsx index 614619e..519fb41 100644 --- a/apps/ui/src/features/DashboardView.tsx +++ b/apps/ui/src/features/DashboardView.tsx @@ -1,7 +1,7 @@ import type { DashboardData } from "../core/types"; type DashboardViewProps = { - dashboard: DashboardData | null; + dashboard: DashboardData | null; }; /** @@ -14,226 +14,220 @@ type DashboardViewProps = { * Dashboard JSX element. */ export const DashboardView = ({ dashboard }: DashboardViewProps) => { - if (!dashboard) { - return ( -
- 暂无数据 -
- ); - } - const errorRate = dashboard.summary.total_requests - ? Math.round( - (dashboard.summary.total_errors / dashboard.summary.total_requests) * - 100, - ) - : 0; - return ( -
-
-
- - 总请求 - -
- {dashboard.summary.total_requests} -
- - 最近窗口 - -
-
- - 总 Tokens - -
- {dashboard.summary.total_tokens} -
- - 累计消耗 - -
-
- - 错误率 - -
{errorRate}%
- - 平均延迟 {Math.round(dashboard.summary.avg_latency)}ms - -
-
-
-
-
-

- 按日趋势 -

-
-
- - - - - - - - - - {dashboard.byDay.map((row) => ( - - - - - - ))} - -
- 日期 - - 请求 - - Tokens -
- {row.day} - - {row.requests} - - {row.tokens} -
-
-
-
-
-

- 模型排行 -

-
-
- - - - - - - - - - {dashboard.byModel.map((row) => ( - - - - - - ))} - -
- 模型 - - 请求 - - Tokens -
- {row.model ?? "-"} - - {row.requests} - - {row.tokens} -
-
-
-
-
-
-
-

- 渠道贡献 -

-
-
- - - - - - - - - - {dashboard.byChannel.map((row) => ( - - - - - - ))} - -
- 渠道 - - 请求 - - Tokens -
- {row.channel_name ?? "-"} - - {row.requests} - - {row.tokens} -
-
-
-
-
-

- 令牌贡献 -

-
-
- - - - - - - - - - {dashboard.byToken.map((row) => ( - - - - - - ))} - -
- 令牌 - - 请求 - - Tokens -
- {row.token_name ?? "-"} - - {row.requests} - - {row.tokens} -
-
-
-
-
- ); + if (!dashboard) { + return ( +
+ 暂无数据 +
+ ); + } + const errorRate = dashboard.summary.total_requests + ? Math.round( + (dashboard.summary.total_errors / dashboard.summary.total_requests) * + 100, + ) + : 0; + return ( +
+
+
+ + 总请求 + +
+ {dashboard.summary.total_requests} +
+ + 最近窗口 + +
+
+ + 总 Tokens + +
+ {dashboard.summary.total_tokens} +
+ + 累计消耗 + +
+
+ + 错误率 + +
{errorRate}%
+ + 平均延迟 {Math.round(dashboard.summary.avg_latency)}ms + +
+
+
+
+
+

+ 按日趋势 +

+
+
+ + + + + + + + + + {dashboard.byDay.map((row) => ( + + + + + + ))} + +
+ 日期 + + 请求 + + Tokens +
+ {row.day} + + {row.requests} + + {row.tokens} +
+
+
+
+
+

+ 模型排行 +

+
+
+ + + + + + + + + + {dashboard.byModel.map((row) => ( + + + + + + ))} + +
+ 模型 + + 请求 + + Tokens +
+ {row.model ?? "-"} + + {row.requests} + + {row.tokens} +
+
+
+
+
+
+
+

+ 渠道贡献 +

+
+
+ + + + + + + + + + {dashboard.byChannel.map((row) => ( + + + + + + ))} + +
+ 渠道 + + 请求 + + Tokens +
+ {row.channel_name ?? "-"} + + {row.requests} + + {row.tokens} +
+
+
+
+
+

+ 令牌贡献 +

+
+
+ + + + + + + + + + {dashboard.byToken.map((row) => ( + + + + + + ))} + +
+ 令牌 + + 请求 + + Tokens +
+ {row.token_name ?? "-"} + + {row.requests} + + {row.tokens} +
+
+
+
+
+ ); }; diff --git a/apps/ui/src/features/ModelsView.tsx b/apps/ui/src/features/ModelsView.tsx index 9c626c6..18b0cae 100644 --- a/apps/ui/src/features/ModelsView.tsx +++ b/apps/ui/src/features/ModelsView.tsx @@ -1,7 +1,7 @@ import type { ModelItem } from "../core/types"; type ModelsViewProps = { - models: ModelItem[]; + models: ModelItem[]; }; /** @@ -14,43 +14,40 @@ type ModelsViewProps = { * Models JSX element. */ export const ModelsView = ({ models }: ModelsViewProps) => ( -
-
-

- 模型广场 -

- - {models.length} 个模型 - -
-
- - - - - - - - - {models.map((model) => ( - - - - - ))} - -
- 模型 - - 渠道 -
- {model.id} - - {model.channels.map((channel) => channel.name).join(" / ")} -
-
-
+
+
+

+ 模型广场 +

+ + {models.length} 个模型 + +
+
+ + + + + + + + + {models.map((model) => ( + + + + + ))} + +
+ 模型 + + 渠道 +
+ {model.id} + + {model.channels.map((channel) => channel.name).join(" / ")} +
+
+
); diff --git a/apps/ui/src/features/SitesView.tsx b/apps/ui/src/features/SitesView.tsx index ef6826e..963aa21 100644 --- a/apps/ui/src/features/SitesView.tsx +++ b/apps/ui/src/features/SitesView.tsx @@ -1,10 +1,10 @@ -import type { CheckinSummary, Site, SiteForm } from "../core/types"; import { getSiteCheckinLabel, getSiteTypeLabel, type SiteSortKey, type SiteSortState, } from "../core/sites"; +import type { CheckinSummary, Site, SiteForm } from "../core/types"; import { buildPageItems, formatDateTime, @@ -182,9 +182,7 @@ export const SitesView = ({ placeholder="站点名称或 URL" value={siteSearch} onInput={(event) => - onSearchChange( - (event.currentTarget as HTMLInputElement).value, - ) + onSearchChange((event.currentTarget as HTMLInputElement).value) } /> @@ -219,10 +217,6 @@ export const SitesView = ({ const systemReady = Boolean( site.system_token && site.system_userid, ); - const checkinEnabled = - site.site_type === "new-api" && Boolean(site.checkin_enabled); - const showCheckin = - site.site_type === "new-api" && checkinEnabled; const callTokenCount = site.call_tokens?.length ?? 0; return (
-
- 类型 - - {getSiteTypeLabel(site.site_type)} - -
+
+ 类型 + + {getSiteTypeLabel(site.site_type)} + +
权重 @@ -290,11 +284,13 @@ export const SitesView = ({

{getSiteCheckinLabel(site, today)}

- {message && showCheckin && ( -

- {message} -

- )} + {message && + site.site_type === "new-api" && + site.checkin_enabled && ( +

+ {message} +

+ )}
@@ -342,9 +338,7 @@ export const SitesView = ({ onClick={() => toggleSort(column.key)} > {column.label} - - {sortIndicator(column.key)} - + {sortIndicator(column.key)}
))} @@ -358,12 +352,6 @@ export const SitesView = ({
{pagedSites.map((site) => { const isActive = site.status === "active"; - const isToday = site.last_checkin_date === today; - const checkinEnabled = - site.site_type === "new-api" && - Boolean(site.checkin_enabled); - const showCheckin = - site.site_type === "new-api" && checkinEnabled; const callTokenCount = site.call_tokens?.length ?? 0; return (
0 ? `${callTokenCount} 个` : "-"}
- {site.site_type === "new-api" ? (site.checkin_enabled ? "已开启" : "已关闭") : "-"} + {site.site_type === "new-api" + ? site.checkin_enabled + ? "已开启" + : "已关闭" + : "-"}
{getSiteCheckinLabel(site, today)} @@ -802,8 +794,18 @@ export const SitesView = ({ }) } > - - + + )} toModelId(item)).filter((item) => item.length > 0); + return input + .map((item) => toModelId(item)) + .filter((item) => item.length > 0); } if (typeof input === "string") { return input diff --git a/apps/worker/src/services/checkin-runner.ts b/apps/worker/src/services/checkin-runner.ts index 10ceb1a..1095ed9 100644 --- a/apps/worker/src/services/checkin-runner.ts +++ b/apps/worker/src/services/checkin-runner.ts @@ -1,13 +1,13 @@ import type { D1Database } from "@cloudflare/workers-types"; +import { beijingDateString } from "../utils/time"; import { listChannels, updateChannelCheckinResult } from "./channel-repo"; import { - runCheckin, - summarizeCheckin, type CheckinResultItem, type CheckinSummary, + runCheckin, + summarizeCheckin, } from "./checkin"; import { parseSiteMetadata } from "./site-metadata"; -import { beijingDateString } from "../utils/time"; export type CheckinRunResult = { results: CheckinResultItem[]; diff --git a/apps/worker/src/services/checkin-scheduler.ts b/apps/worker/src/services/checkin-scheduler.ts index 647e454..28419fd 100644 --- a/apps/worker/src/services/checkin-scheduler.ts +++ b/apps/worker/src/services/checkin-scheduler.ts @@ -3,13 +3,13 @@ import type { DurableObjectState, } from "@cloudflare/workers-types"; import type { Bindings } from "../env"; -import { getCheckinScheduleTime } from "./settings"; -import { runCheckinAll } from "./checkin-runner"; import { beijingDateString, computeBeijingScheduleTime, computeNextBeijingRun, } from "../utils/time"; +import { runCheckinAll } from "./checkin-runner"; +import { getCheckinScheduleTime } from "./settings"; const SCHEDULER_NAME = "checkin-scheduler"; const LAST_RUN_DATE_KEY = "last_run_date"; @@ -30,10 +30,8 @@ export const shouldRunCheckin = ( return now.getTime() >= scheduledAt.getTime(); }; -export const shouldResetLastRun = ( - currentTime: string, - nextTime: string, -) => currentTime !== nextTime; +export const shouldResetLastRun = (currentTime: string, nextTime: string) => + currentTime !== nextTime; export const computeNextAlarmAt = ( now: Date, diff --git a/apps/worker/src/services/settings.ts b/apps/worker/src/services/settings.ts index 51d4b19..6742e44 100644 --- a/apps/worker/src/services/settings.ts +++ b/apps/worker/src/services/settings.ts @@ -112,9 +112,7 @@ export async function isAdminPasswordSet(db: D1Database): Promise { return Boolean(hash); } -export async function getCheckinScheduleTime( - db: D1Database, -): Promise { +export async function getCheckinScheduleTime(db: D1Database): Promise { const timeRaw = await readSetting(db, CHECKIN_SCHEDULE_TIME_KEY); return timeRaw && timeRaw.length > 0 ? timeRaw diff --git a/apps/worker/src/utils/time.ts b/apps/worker/src/utils/time.ts index ad8ccf8..bf4b608 100644 --- a/apps/worker/src/utils/time.ts +++ b/apps/worker/src/utils/time.ts @@ -28,7 +28,9 @@ export function beijingDateString(date: Date = new Date()): string { return `${year}-${month}-${day}`; } -export function parseScheduleTime(value: string): { hour: number; minute: number } | null { +export function parseScheduleTime( + value: string, +): { hour: number; minute: number } | null { if (!/^\d{2}:\d{2}$/.test(value)) { return null; } @@ -57,8 +59,13 @@ export function getBeijingDateParts(date: Date = new Date()) { }; } +const DEFAULT_SCHEDULE_PARTS = parseScheduleTime(DEFAULT_SCHEDULE_TIME) ?? { + hour: 0, + minute: 10, +}; + const resolveScheduleParts = (value: string) => - parseScheduleTime(value) ?? parseScheduleTime(DEFAULT_SCHEDULE_TIME)!; + parseScheduleTime(value) ?? DEFAULT_SCHEDULE_PARTS; export function computeBeijingScheduleTime( now: Date, @@ -71,10 +78,7 @@ export function computeBeijingScheduleTime( return new Date(utcMs); } -export function computeNextBeijingRun( - now: Date, - scheduleTime: string, -): Date { +export function computeNextBeijingRun(now: Date, scheduleTime: string): Date { const scheduled = computeBeijingScheduleTime(now, scheduleTime); if (now.getTime() < scheduled.getTime()) { return scheduled; diff --git a/helloagents/CHANGELOG.md b/helloagents/CHANGELOG.md index bf11fa1..35735fd 100644 --- a/helloagents/CHANGELOG.md +++ b/helloagents/CHANGELOG.md @@ -4,6 +4,15 @@ ### 微调 +- **[admin-ui]**: 令牌创建表单使用 FormData.forEach 以兼容缺少 entries 的类型定义 + - 类型: 微调(无方案包) + - 文件: apps/ui/src/App.tsx +- **[admin-ui]**: 清理站点列表未使用变量以通过 lint + - 类型: 微调(无方案包) + - 文件: apps/ui/src/features/SitesView.tsx +- **[worker]**: 时间工具函数移除非空断言以满足 lint + - 类型: 微调(无方案包) + - 文件: apps/worker/src/utils/time.ts - **[admin-ui]**: 站点管理新增一键测试并汇总结果提示 - 类型: 微调(无方案包) - 文件: apps/ui/src/App.tsx, apps/ui/src/features/SitesView.tsx, apps/ui/src/core/sites.ts, tests/ui/sites.test.ts