feat: Ollama Cloud quota tracker + proactive background OAuth refresh

Ollama: replace informational stub with real quota tracker hitting ollama.com/api/usage (session 5h + weekly 7d, 0..1 ratio) and /api/me plan label; bind handler to apiKey + add features.usageApikey so apikey connections work.

Token refresh: add backgroundTokenRefresh scheduler that refreshes OAuth connections within max(provider lead, 30min) of expiry, independent of inbound traffic (10s after boot, then every 5min, unref'd timers, DISABLE_BACKGROUND_TOKEN_REFRESH kill-switch, fail-open per tick/connection). Registered from custom-server.js (listening) and initializeApp.js. checkAndRefreshToken gains opt-in {force} for the scheduler; request path unchanged.
This commit is contained in:
B1nh M1nh
2026-08-02 09:34:27 +07:00
parent 6fcd27337a
commit f260a1817b
10 changed files with 710 additions and 18 deletions

View File

@@ -1,7 +1,42 @@
const http = require("http");
const path = require("path");
const { pathToFileURL } = require("url");
const origCreate = http.createServer.bind(http);
let backgroundRefreshStarted = false;
function startBackgroundTokenRefreshFromCustomServer() {
if (backgroundRefreshStarted) return;
backgroundRefreshStarted = true;
// Prefer source path (repo / standalone that still has src). Fail-open if missing
// — initializeApp also starts the same scheduler when the Next app boots.
const modPath = path.join(__dirname, "src", "sse", "services", "backgroundTokenRefresh.js");
import(pathToFileURL(modPath).href)
.then((m) => {
try {
m.startBackgroundTokenRefresh();
} catch (e) {
console.error("[BackgroundTokenRefresh] start failed:", e && e.message ? e.message : e);
}
const stop = () => {
try {
m.stopBackgroundTokenRefresh();
} catch {
/* ignore */
}
};
process.once("SIGINT", stop);
process.once("SIGTERM", stop);
})
.catch((e) => {
// Expected in published CLI standalone (src/ not on disk). App bootstrap covers it.
if (process.env.DEBUG_BACKGROUND_TOKEN_REFRESH) {
console.error("[BackgroundTokenRefresh] import failed:", e && e.message ? e.message : e);
}
});
}
// Wrap Next standalone HTTP server: derive client IP from the TCP socket
// (unspoofable) and strip client-supplied forwarding headers so downstream
// rate-limiting keys on the real peer address instead of attacker-controlled XFF.
@@ -26,7 +61,11 @@ http.createServer = (...args) => {
if (viaProxy) req.headers["x-9r-via-proxy"] = "1";
return handler(req, res);
};
return origCreate(...rest, wrapped);
const server = origCreate(...rest, wrapped);
server.once("listening", () => {
startBackgroundTokenRefreshFromCustomServer();
});
return server;
};
require("./server.js");