feat: Add normalized types and utility functions for handling chat formats

- Introduced `NormalizedContentBlock`, `NormalizedUsage`, and related types for structured data handling.
- Implemented utility functions for creating and merging normalized usage statistics.
- Added a new module for shared transformers to manage chat formats.

feat: Implement app local state management

- Created functions to clear application installation state and manage local storage keys.
- Added constants for theme and user profile storage keys.

test: Add tests for BrandIcon component rendering

- Implemented tests to ensure BrandIcon does not force a colored background for recognized brands.

test: Enhance ModelAnalysisPanel tests

- Added tests for rendering token summaries with compact units in ModelAnalysisPanel.

test: Add tests for ModernSelect component

- Implemented tests to verify icon rendering for selected options in ModernSelect.

test: Add number formatting tests

- Created tests for `formatCompactTokenMetric` to validate token formatting logic.

feat: Implement account creation intent handling

- Added logic to handle session and API key creation intents in the Accounts page.

test: Add tests for dashboard performance card

- Implemented tests to verify rendering of performance metrics in the dashboard.

feat: Implement default connection segment resolution

- Added logic to determine initial connection segments based on platform types.

test: Add tests for settings factory reset functionality

- Implemented tests to verify the factory reset process and local state clearing.

test: Add tests for site creation redirect logic

- Implemented tests to verify redirection based on site capabilities during creation.

test: Add tests for token routes refresh decision action

- Implemented tests to verify refresh decision actions in TokenRoutes.

test: Add tests for token routes snapshot load

- Implemented tests to verify loading of cached snapshot data in TokenRoutes.
This commit is contained in:
cita
2026-03-08 12:08:57 +08:00
parent ed05b37626
commit 23922a5965
115 changed files with 18597 additions and 4411 deletions
+127
View File
@@ -0,0 +1,127 @@
import { eq } from 'drizzle-orm';
import { db, schema } from '../db/index.js';
import { formatUtcSqlDateTime } from './localTimeService.js';
export type SaveProxyVideoTaskInput = {
publicId?: string;
upstreamVideoId: string;
siteUrl: string;
tokenValue: string;
requestedModel: string;
actualModel: string;
channelId: number | null;
accountId: number | null;
statusSnapshot?: unknown;
upstreamResponseMeta?: unknown;
lastUpstreamStatus?: number | null;
lastPolledAt?: string | null;
};
export type ProxyVideoTaskRecord = {
publicId: string;
upstreamVideoId: string;
siteUrl: string;
tokenValue: string;
requestedModel: string | null;
actualModel: string | null;
channelId: number | null;
accountId: number | null;
statusSnapshot: unknown | null;
upstreamResponseMeta: unknown | null;
lastUpstreamStatus: number | null;
lastPolledAt: string | null;
};
function buildPublicVideoId(): string {
const timePart = Date.now().toString(36);
const randomPart = Math.random().toString(36).slice(2, 10);
return `vid_${timePart}_${randomPart}`;
}
export async function saveProxyVideoTask(input: SaveProxyVideoTaskInput): Promise<{
publicId: string;
upstreamVideoId: string;
}> {
const publicId = input.publicId?.trim() || buildPublicVideoId();
const now = formatUtcSqlDateTime(new Date());
await db.insert(schema.proxyVideoTasks).values({
publicId,
upstreamVideoId: input.upstreamVideoId,
siteUrl: input.siteUrl,
tokenValue: input.tokenValue,
requestedModel: input.requestedModel,
actualModel: input.actualModel,
channelId: input.channelId,
accountId: input.accountId,
statusSnapshot: input.statusSnapshot === undefined ? null : JSON.stringify(input.statusSnapshot),
upstreamResponseMeta: input.upstreamResponseMeta === undefined ? null : JSON.stringify(input.upstreamResponseMeta),
lastUpstreamStatus: input.lastUpstreamStatus ?? null,
lastPolledAt: input.lastPolledAt ?? (input.lastUpstreamStatus == null ? null : now),
createdAt: now,
updatedAt: now,
}).run();
return {
publicId,
upstreamVideoId: input.upstreamVideoId,
};
}
export async function getProxyVideoTaskByPublicId(publicId: string): Promise<ProxyVideoTaskRecord | null> {
const row = await db.select().from(schema.proxyVideoTasks)
.where(eq(schema.proxyVideoTasks.publicId, publicId))
.get();
if (!row) return null;
return {
publicId: row.publicId,
upstreamVideoId: row.upstreamVideoId,
siteUrl: row.siteUrl,
tokenValue: row.tokenValue,
requestedModel: row.requestedModel,
actualModel: row.actualModel,
channelId: row.channelId,
accountId: row.accountId,
statusSnapshot: parseJsonColumn(row.statusSnapshot),
upstreamResponseMeta: parseJsonColumn(row.upstreamResponseMeta),
lastUpstreamStatus: row.lastUpstreamStatus,
lastPolledAt: row.lastPolledAt,
};
}
export async function deleteProxyVideoTaskByPublicId(publicId: string): Promise<void> {
await db.delete(schema.proxyVideoTasks)
.where(eq(schema.proxyVideoTasks.publicId, publicId))
.run();
}
export async function refreshProxyVideoTaskSnapshot(
publicId: string,
input: {
statusSnapshot?: unknown;
upstreamResponseMeta?: unknown;
lastUpstreamStatus?: number | null;
lastPolledAt?: string | null;
},
): Promise<void> {
const now = formatUtcSqlDateTime(new Date());
await db.update(schema.proxyVideoTasks)
.set({
statusSnapshot: input.statusSnapshot === undefined ? null : JSON.stringify(input.statusSnapshot),
upstreamResponseMeta: input.upstreamResponseMeta === undefined ? null : JSON.stringify(input.upstreamResponseMeta),
lastUpstreamStatus: input.lastUpstreamStatus ?? null,
lastPolledAt: input.lastPolledAt ?? now,
updatedAt: now,
})
.where(eq(schema.proxyVideoTasks.publicId, publicId))
.run();
}
function parseJsonColumn(value: string | null | undefined): unknown | null {
if (typeof value !== 'string' || value.trim().length === 0) return null;
try {
return JSON.parse(value);
} catch {
return value;
}
}