feat(web,ui): per-provider git forge API base URL and detection URLs

Configure a default API base URL per git provider (github/gitlab/gitea)
in settings.json gitProviders, with GitHub Enterprise support (Octokit
baseUrl + device-flow web origin derived from the API base), and replace
the client-side custom-domains list with server-persisted detection URL
chips (SSH/HTTPS forms normalized to hosts). The configured API base host
auto-counts as a detection host. Settings round-trip through the existing
/api/config/settings sanitizer; the UI store hydrates from server settings
with a one-time localStorage migration.
This commit is contained in:
2026-08-17 09:55:11 +00:00
parent a87b3fd228
commit 0fc857959e
55 changed files with 1685 additions and 272 deletions
@@ -1,6 +1,5 @@
const DEVICE_CODE_URL = 'https://github.com/login/device/code';
const ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token';
const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
const DEFAULT_WEB_ORIGIN = 'https://github.com';
const encodeForm = (params) => {
const body = new URLSearchParams();
@@ -32,16 +31,19 @@ async function postForm(url, params) {
return payload;
}
export async function startDeviceFlow({ clientId, scope }) {
return postForm(DEVICE_CODE_URL, {
const deviceCodeUrl = (webOrigin) => `${(webOrigin || DEFAULT_WEB_ORIGIN).replace(/\/+$/, '')}/login/device/code`;
const accessTokenUrl = (webOrigin) => `${(webOrigin || DEFAULT_WEB_ORIGIN).replace(/\/+$/, '')}/login/oauth/access_token`;
export async function startDeviceFlow({ clientId, scope, webOrigin }) {
return postForm(deviceCodeUrl(webOrigin), {
client_id: clientId,
scope,
});
}
export async function exchangeDeviceCode({ clientId, deviceCode }) {
export async function exchangeDeviceCode({ clientId, deviceCode, webOrigin }) {
// GitHub returns 200 with {error: 'authorization_pending'|...} for non-success states.
const payload = await postForm(ACCESS_TOKEN_URL, {
const payload = await postForm(accessTokenUrl(webOrigin), {
client_id: clientId,
device_code: deviceCode,
grant_type: DEVICE_GRANT_TYPE,