feat: Add manual OAuth authorization mode for WebUI (#131)

* feat: add manual OAuth flow support in WebUI

* fix: reset add account modal state on close

* feat: display custom API key in startup banner

* fix: move translations to separate files and optimize import API

* fix: remove orphaned model-manager.js and cleanup callback server on manual auth

---------

Co-authored-by: Badri Narayanan S <59133612+badrisnarayanan@users.noreply.github.com>
This commit is contained in:
董飞祥
2026-01-23 21:23:29 +08:00
committed by GitHub
parent 0fa945b069
commit 9992c4ab27
15 changed files with 624 additions and 16 deletions

View File

@@ -139,15 +139,19 @@ export function extractCodeFromInput(input) {
/**
* Start a local server to receive the OAuth callback
* Returns a promise that resolves with the authorization code
* Returns an object with a promise and an abort function
*
* @param {string} expectedState - Expected state parameter for CSRF protection
* @param {number} timeoutMs - Timeout in milliseconds (default 120000)
* @returns {Promise<string>} Authorization code from OAuth callback
* @returns {{promise: Promise<string>, abort: Function}} Object with promise and abort function
*/
export function startCallbackServer(expectedState, timeoutMs = 120000) {
return new Promise((resolve, reject) => {
const server = http.createServer((req, res) => {
let server = null;
let timeoutId = null;
let isAborted = false;
const promise = new Promise((resolve, reject) => {
server = http.createServer((req, res) => {
const url = new URL(req.url, `http://localhost:${OAUTH_CONFIG.callbackPort}`);
if (url.pathname !== '/oauth-callback') {
@@ -241,11 +245,28 @@ export function startCallbackServer(expectedState, timeoutMs = 120000) {
});
// Timeout after specified duration
setTimeout(() => {
server.close();
reject(new Error('OAuth callback timeout - no response received'));
timeoutId = setTimeout(() => {
if (!isAborted) {
server.close();
reject(new Error('OAuth callback timeout - no response received'));
}
}, timeoutMs);
});
// Abort function to clean up server when manual completion happens
const abort = () => {
if (isAborted) return;
isAborted = true;
if (timeoutId) {
clearTimeout(timeoutId);
}
if (server) {
server.close();
logger.info('[OAuth] Callback server aborted (manual completion)');
}
};
return { promise, abort };
}
/**