- 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.
128 lines
4.0 KiB
TypeScript
128 lines
4.0 KiB
TypeScript
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;
|
|
}
|
|
}
|