mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
162 lines
6.5 KiB
JavaScript
162 lines
6.5 KiB
JavaScript
// Ensure proxyFetch is loaded to patch globalThis.fetch
|
|
import "open-sse/index.js";
|
|
|
|
import { getProviderConnectionById } from "@/lib/localDb";
|
|
import { consumeCodexRateLimitResetCredit, getCodexRateLimitResetCredits } from "open-sse/services/usage.js";
|
|
import { resolveConnectionProxyConfig } from "@/lib/network/connectionProxy";
|
|
import { refreshAndUpdateCredentials } from "../route.js";
|
|
import { requireUsageDashboardUser } from "@/lib/auth/currentUser";
|
|
|
|
const AUTH_EXPIRED_PATTERNS = ["expired", "authentication", "unauthorized", "401", "re-authorize"];
|
|
|
|
function isAuthExpiredResult(result) {
|
|
const values = [result?.message, result?.code, result?.raw?.detail, result?.raw?.error]
|
|
.filter(Boolean)
|
|
.map((value) => String(value).toLowerCase());
|
|
return values.some((value) => AUTH_EXPIRED_PATTERNS.some((pattern) => value.includes(pattern)));
|
|
}
|
|
|
|
function isAuthExpiredError(error) {
|
|
return isAuthExpiredResult({ message: error?.message });
|
|
}
|
|
|
|
function getResponseForConsumeResult(result, redeemRequestId) {
|
|
if (result.ok) {
|
|
return Response.json({
|
|
code: result.code,
|
|
reset: true,
|
|
windows_reset: result.windowsReset,
|
|
redeemRequestId,
|
|
credit: result.raw?.credit || null,
|
|
});
|
|
}
|
|
|
|
if (result.noCredit) {
|
|
return Response.json({
|
|
code: "no_credit",
|
|
reset: false,
|
|
windows_reset: result.windowsReset,
|
|
message: "No Codex reset credits available.",
|
|
}, { status: 409 });
|
|
}
|
|
|
|
return Response.json({
|
|
code: result.code || "unknown_response",
|
|
reset: false,
|
|
windows_reset: result.windowsReset,
|
|
message: result.message || "Codex reset credit consume returned an unexpected response.",
|
|
}, { status: result.status >= 400 && result.status < 500 ? result.status : 502 });
|
|
}
|
|
|
|
async function getCodexConnection(connectionId, user) {
|
|
const connection = await getProviderConnectionById(connectionId, user.role === "admin" ? null : user.id);
|
|
if (!connection) {
|
|
return { response: Response.json({ error: "Connection not found" }, { status: 404 }) };
|
|
}
|
|
|
|
if (connection.provider !== "codex") {
|
|
return { response: Response.json({ error: "Codex reset credits are only available for Codex connections." }, { status: 400 }) };
|
|
}
|
|
|
|
const isOAuth = connection.authType === "oauth";
|
|
const isAccessToken = connection.authType === "access_token";
|
|
if (!isOAuth && !isAccessToken) {
|
|
return { response: Response.json({ error: "Codex reset credits require an OAuth or access-token connection." }, { status: 400 }) };
|
|
}
|
|
|
|
const proxyConfig = await resolveConnectionProxyConfig(connection.providerSpecificData);
|
|
const proxyOptions = {
|
|
connectionProxyEnabled: proxyConfig.connectionProxyEnabled === true,
|
|
connectionProxyUrl: proxyConfig.connectionProxyUrl || "",
|
|
connectionNoProxy: proxyConfig.connectionNoProxy || "",
|
|
vercelRelayUrl: proxyConfig.vercelRelayUrl || "",
|
|
strictProxy: false,
|
|
};
|
|
|
|
return { connection, isOAuth, proxyOptions };
|
|
}
|
|
|
|
async function refreshCodexConnection(connection, proxyOptions) {
|
|
try {
|
|
const result = await refreshAndUpdateCredentials(connection, false, proxyOptions);
|
|
return { connection: result.connection };
|
|
} catch (refreshError) {
|
|
console.error("[Codex Reset Credits API] Credential refresh failed:", refreshError);
|
|
return { response: Response.json({ error: `Credential refresh failed: ${refreshError.message}` }, { status: 401 }) };
|
|
}
|
|
}
|
|
|
|
export async function GET(_request, { params }) {
|
|
let connection;
|
|
try {
|
|
const { connectionId } = await params;
|
|
const user = await requireUsageDashboardUser();
|
|
const resolved = await getCodexConnection(connectionId, user);
|
|
if (resolved.response) return resolved.response;
|
|
({ connection } = resolved);
|
|
const { isOAuth, proxyOptions } = resolved;
|
|
|
|
if (isOAuth) {
|
|
const refreshed = await refreshCodexConnection(connection, proxyOptions);
|
|
if (refreshed.response) return refreshed.response;
|
|
connection = refreshed.connection;
|
|
}
|
|
|
|
let result;
|
|
try {
|
|
result = await getCodexRateLimitResetCredits(connection.accessToken, proxyOptions, connection.providerSpecificData);
|
|
} catch (fetchError) {
|
|
if (!isOAuth || !connection.refreshToken || !isAuthExpiredError(fetchError)) throw fetchError;
|
|
const retryResult = await refreshAndUpdateCredentials(connection, true, proxyOptions);
|
|
connection = retryResult.connection;
|
|
result = await getCodexRateLimitResetCredits(connection.accessToken, proxyOptions, connection.providerSpecificData);
|
|
}
|
|
|
|
return Response.json(result);
|
|
} catch (error) {
|
|
if (error?.message === "Unauthorized") return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
const provider = connection?.provider ?? "unknown";
|
|
console.warn(`[Codex Reset Credits] ${provider}: ${error.message}`);
|
|
return Response.json({ error: error.message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
export async function POST(request, { params }) {
|
|
let connection;
|
|
try {
|
|
const { connectionId } = await params;
|
|
const user = await requireUsageDashboardUser();
|
|
const resolved = await getCodexConnection(connectionId, user);
|
|
if (resolved.response) return resolved.response;
|
|
({ connection } = resolved);
|
|
const { isOAuth, proxyOptions } = resolved;
|
|
|
|
if (isOAuth) {
|
|
const refreshed = await refreshCodexConnection(connection, proxyOptions);
|
|
if (refreshed.response) return refreshed.response;
|
|
connection = refreshed.connection;
|
|
}
|
|
|
|
// Server-generated redeem id prevents client-controlled replay
|
|
const redeemRequestId = crypto.randomUUID();
|
|
let consumeResult = await consumeCodexRateLimitResetCredit(connection.accessToken, redeemRequestId, proxyOptions);
|
|
|
|
if (isOAuth && isAuthExpiredResult(consumeResult) && connection.refreshToken) {
|
|
try {
|
|
const retryResult = await refreshAndUpdateCredentials(connection, true, proxyOptions);
|
|
connection = retryResult.connection;
|
|
consumeResult = await consumeCodexRateLimitResetCredit(connection.accessToken, redeemRequestId, proxyOptions);
|
|
} catch (retryError) {
|
|
console.warn(`[Codex Reset Credits] force refresh failed: ${retryError.message}`);
|
|
}
|
|
}
|
|
|
|
return getResponseForConsumeResult(consumeResult, redeemRequestId);
|
|
} catch (error) {
|
|
if (error?.message === "Unauthorized") return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
const provider = connection?.provider ?? "unknown";
|
|
console.warn(`[Codex Reset Credits] ${provider}: ${error.message}`);
|
|
return Response.json({ error: error.message }, { status: 500 });
|
|
}
|
|
}
|