Add market watch and match hub workflows

这个提交包含在:
cryptocommuniums-afk
2026-04-07 11:00:03 +08:00
父节点 495da60212
当前提交 32ffad1545
修改 39 个文件,包含 6974 行新增330 行删除

查看文件

@@ -39,10 +39,12 @@ export function getSessionCookieOptions(
// ? hostname
// : undefined;
const secure = isSecureRequest(req);
return {
httpOnly: true,
path: "/",
sameSite: "none",
secure: isSecureRequest(req),
sameSite: secure ? "none" : "lax",
secure,
};
}

29
server/_core/fetch.test.ts 普通文件
查看文件

@@ -0,0 +1,29 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { fetchWithTimeout } from "./fetch";
describe("fetchWithTimeout", () => {
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it("retries timeout-like errors for allowed methods", async () => {
const fetchMock = vi.fn()
.mockRejectedValueOnce(new Error("Request timed out after 100ms"))
.mockResolvedValueOnce(new Response("ok", { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
const response = await fetchWithTimeout("https://example.com", {
method: "POST",
}, {
timeoutMs: 100,
retries: 1,
retryMethods: ["POST"],
baseDelayMs: 1,
});
expect(response.ok).toBe(true);
expect(fetchMock).toHaveBeenCalledTimes(2);
});
});

查看文件

@@ -25,7 +25,12 @@ function shouldRetryError(method: string, error: unknown, options: FetchRetryOpt
}
if (error instanceof Error) {
return error.name === "AbortError" || error.name === "TimeoutError" || error.message.includes("fetch");
return (
error.name === "AbortError" ||
error.name === "TimeoutError" ||
error.message.startsWith("Request timed out after ") ||
error.message.includes("fetch")
);
}
return false;

查看文件

@@ -9,7 +9,7 @@ import { appRouter } from "../routers";
import { createContext } from "./context";
import { registerMediaProxy } from "./mediaProxy";
import { serveStatic } from "./static";
import { createBackgroundTask, getAdminUserId, hasRecentBackgroundTaskOfType, seedAchievementDefinitions, seedAppSettings, seedTutorials, seedVisionReferenceImages } from "../db";
import { createBackgroundTask, getAdminUserId, getAppSettingValue, hasRecentBackgroundTaskOfType, seedAchievementDefinitions, seedAppSettings, seedTutorials, seedVisionReferenceImages } from "../db";
import { nanoid } from "nanoid";
import { syncTutorialImages } from "../tutorialImages";
@@ -64,6 +64,32 @@ async function scheduleDailyNtrpRefresh() {
});
}
async function scheduleMarketWatchRefresh() {
const intervalMinutes = Math.max(5, await getAppSettingValue("market_watch_refresh_interval_minutes", 30));
const since = new Date(Date.now() - intervalMinutes * 60_000);
const exists = await hasRecentBackgroundTaskOfType("market_watch_refresh", since);
if (exists) {
return;
}
const adminUserId = await getAdminUserId();
if (!adminUserId) {
return;
}
const taskId = nanoid();
await createBackgroundTask({
id: taskId,
userId: adminUserId,
type: "market_watch_refresh",
title: "全网球拍行情刷新",
message: "系统已自动创建球拍行情刷新任务",
payload: { scope: "all_users", trigger: "scheduler" },
progress: 0,
maxAttempts: 3,
});
}
function isPortAvailable(port: number): Promise<boolean> {
return new Promise(resolve => {
const server = net.createServer();
@@ -129,6 +155,9 @@ async function startServer() {
void scheduleDailyNtrpRefresh().catch((error) => {
console.error("[scheduler] failed to schedule NTRP refresh", error);
});
void scheduleMarketWatchRefresh().catch((error) => {
console.error("[scheduler] failed to schedule market refresh", error);
});
}, 60_000);
}

查看文件

@@ -70,6 +70,8 @@ export type InvokeParams = {
output_schema?: OutputSchema;
responseFormat?: ResponseFormat;
response_format?: ResponseFormat;
timeoutMs?: number;
retryCount?: number;
};
export type ToolCall = {
@@ -286,6 +288,8 @@ export async function invokeLLM(params: InvokeParams): Promise<InvokeResult> {
output_schema,
responseFormat,
response_format,
timeoutMs,
retryCount,
} = params;
const payload: Record<string, unknown> = {
@@ -332,8 +336,8 @@ export async function invokeLLM(params: InvokeParams): Promise<InvokeResult> {
},
body: JSON.stringify(payload),
}, {
timeoutMs: ENV.llmTimeoutMs,
retries: ENV.llmRetryCount,
timeoutMs: timeoutMs ?? ENV.llmTimeoutMs,
retries: retryCount ?? ENV.llmRetryCount,
retryMethods: ["POST"],
});