feat(quota): Moonshot Open Platform balance and TPD lock for custom nodes (#12590)

Validado em lote numa worktree combinada com os 9 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo, `check:provider-consistency` OK (273 entradas REGISTRY, **356** providers canônicos), `check-docs-counts-sync` exit 0 e **300/300** nos testes que a leva toca.

O crescimento de arquivo que os PRs empilham uns sobre os outros foi rebaselinado num único registro datado (`_rebaseline_2026_09_03_houminxi_batch`), com a decomposição por arquivo: `providers/page.tsx` +18 (import CSV do #12504 + busca do #12495 no mesmo painel), `accountFallback.ts` +6 (o #12566 sobre o rebaseline que o #12590 já registrou — os dois tocam `checkFallbackError`) e `chatCore.ts` +3 (invalidez de cache de quota no 429 do #12325). As violações restantes (`codex.ts`, `stream.ts`) foram medidas também no tip puro e são drift da base, não desta leva.
This commit is contained in:
Bob.Hou
2026-09-03 11:47:50 -04:00
committed by GitHub
parent a47d2e521e
commit 831ea040c3
121 changed files with 1626 additions and 247 deletions

View File

@@ -56,7 +56,7 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
if (isValidationFailure(validation)) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}
const { name, prefix, apiType, baseUrl, chatPath, modelsPath, customHeaders, iconUrl } =
const { name, prefix, apiType, baseUrl, chatPath, modelsPath, customHeaders, iconUrl, dailyQuotaResetTimezone, dailyQuotaResetHour } =
validation.data;
const node: any = await getProviderNodeById(id);
@@ -98,6 +98,9 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
// previously stored custom icon.
iconUrl: iconUrl?.trim() || null,
customHeaders: customHeaders || null,
dailyQuotaResetTimezone: dailyQuotaResetTimezone?.trim() || null,
dailyQuotaResetHour:
dailyQuotaResetHour === 0 || dailyQuotaResetHour != null ? dailyQuotaResetHour : null,
};
if (node.type === "openai-compatible") {
@@ -106,6 +109,21 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
const updated = await updateProviderNode(id, updates);
try {
const { registerMoonshotFetchersForNodes } = await import(
"@omniroute/open-sse/services/moonshotQuotaFetcher.ts"
);
registerMoonshotFetchersForNodes([
{
id: typeof updated?.id === "string" ? updated.id : id,
prefix: prefix.trim(),
baseUrl: sanitizedBaseUrl,
},
]);
} catch (error) {
console.warn("Moonshot fetcher re-register after node update skipped:", error);
}
const connections = await getProviderConnections({ provider: id });
await Promise.all(
connections.flatMap((connectionRaw) => {

View File

@@ -48,6 +48,27 @@ function sanitizeVibeProxyBaseUrl(baseUrl: string) {
return `${base}/v1`;
}
async function registerMoonshotFetchersForCreatedNode(node: {
id?: unknown;
prefix?: unknown;
baseUrl?: unknown;
}): Promise<void> {
try {
const { registerMoonshotFetchersForNodes } = await import(
"@omniroute/open-sse/services/moonshotQuotaFetcher.ts"
);
registerMoonshotFetchersForNodes([
{
id: typeof node.id === "string" ? node.id : null,
prefix: typeof node.prefix === "string" ? node.prefix : null,
baseUrl: typeof node.baseUrl === "string" ? node.baseUrl : null,
},
]);
} catch (error) {
console.warn("Moonshot fetcher register after node create skipped:", error);
}
}
function sanitizeAnthropicBaseUrl(baseUrl: string) {
return (baseUrl || "")
.trim()
@@ -126,6 +147,8 @@ export async function POST(request) {
modelsPath,
customHeaders,
iconUrl,
dailyQuotaResetTimezone,
dailyQuotaResetHour,
} = validation.data;
if (preset === "vibeproxy-openai") {
@@ -145,7 +168,11 @@ export async function POST(request) {
modelsPath: modelsPath || null,
iconUrl: iconUrl?.trim() || null,
customHeaders: customHeaders || null,
dailyQuotaResetTimezone: dailyQuotaResetTimezone?.trim() || null,
dailyQuotaResetHour:
dailyQuotaResetHour === 0 || dailyQuotaResetHour != null ? dailyQuotaResetHour : null,
});
await registerMoonshotFetchersForCreatedNode(node);
return NextResponse.json({ node }, { status: 201 });
}
@@ -170,7 +197,11 @@ export async function POST(request) {
modelsPath: modelsPath || null,
iconUrl: iconUrl?.trim() || null,
customHeaders: customHeaders || null,
dailyQuotaResetTimezone: dailyQuotaResetTimezone?.trim() || null,
dailyQuotaResetHour:
dailyQuotaResetHour === 0 || dailyQuotaResetHour != null ? dailyQuotaResetHour : null,
});
await registerMoonshotFetchersForCreatedNode(node);
return NextResponse.json({ node }, { status: 201 });
}
@@ -200,7 +231,11 @@ export async function POST(request) {
modelsPath: compatMode === "cc" ? null : modelsPath || null,
iconUrl: iconUrl?.trim() || null,
customHeaders: customHeaders || null,
dailyQuotaResetTimezone: dailyQuotaResetTimezone?.trim() || null,
dailyQuotaResetHour:
dailyQuotaResetHour === 0 || dailyQuotaResetHour != null ? dailyQuotaResetHour : null,
});
await registerMoonshotFetchersForCreatedNode(node);
return NextResponse.json({ node }, { status: 201 });
}