const { URL } = require('url'); const FALLBACK_DEFAULT_STUDENT_GIT = 'https://git.educoder.net/pu428f3pz/rnsuwx89z320260422155438.git'; /** 与 server 默认学员库一致(可被 DEFAULT_STUDENT_GIT_URL 覆盖) */ function getDefaultPublicStudentGitUrl() { const v = process.env.DEFAULT_STUDENT_GIT_URL; return (v && String(v).trim()) || FALLBACK_DEFAULT_STUDENT_GIT; } function stripGitCredentials(href) { try { const u = new URL(String(href).trim()); u.username = ''; u.password = ''; return u.href; } catch { return String(href).trim(); } } /** 用于判断「是否为同一远程」:忽略凭据、主机小写、去掉末尾 /、可选去掉 .git */ function normalizeComparableGitRemote(href) { try { const u = new URL(String(href).trim()); u.username = ''; u.password = ''; u.hostname = u.hostname.toLowerCase(); let p = u.pathname.replace(/\/+$/, '') || '/'; if (p.toLowerCase().endsWith('.git')) p = p.slice(0, -4); u.pathname = p || '/'; return `${u.protocol}//${u.hostname}${u.pathname === '/' ? '' : u.pathname}`; } catch { return stripGitCredentials(href); } } function sameGitRemoteIgnoringAuth(a, b) { return normalizeComparableGitRemote(a) === normalizeComparableGitRemote(b); } function hasDefaultGitPasswordAuth() { const user = String(process.env.DEFAULT_STUDENT_GIT_USER || '').trim(); const pass = String(process.env.DEFAULT_STUDENT_GIT_PASSWORD || '').trim(); return Boolean(user && pass); } function shouldApplyDefaultGitAuth(publicGitUrl) { if (!hasDefaultGitPasswordAuth()) return false; const pub = String(publicGitUrl || '').trim(); if (!pub) return false; const canonicalDefault = getDefaultPublicStudentGitUrl(); if (!sameGitRemoteIgnoringAuth(pub, canonicalDefault)) return false; try { const u = new URL(pub); if (u.protocol !== 'https:' && u.protocol !== 'http:') return false; if (u.username || u.password) return false; return true; } catch { return false; } } /** * 供 git clone/pull 使用:当目标与默认学员库为同一远程、且配置了 * DEFAULT_STUDENT_GIT_USER + DEFAULT_STUDENT_GIT_PASSWORD 时,生成带 userinfo 的 URL。 * 注意:部分 Git/Credential Manager 会忽略 URL 内嵌密码,此时应配合 buildGitHttpAuthConfigArgs 使用。 */ function resolveCloneUrlForGit(publicGitUrl) { const pub = String(publicGitUrl || '').trim(); if (!pub) return pub; if (!shouldApplyDefaultGitAuth(pub)) return pub; try { const u = new URL(pub); const user = String(process.env.DEFAULT_STUDENT_GIT_USER || '').trim(); const pass = String(process.env.DEFAULT_STUDENT_GIT_PASSWORD || '').trim(); u.username = user; u.password = pass; return u.href; } catch { return pub; } } /** * 通过 git -c http.///.extraHeader=Authorization: Basic … 传 HTTPS 凭据, * 避免新版 Git / 凭证管理器丢弃 URL 中的 user:password。 * @returns {string[]} 形如 ['-c', 'http.https://host/.extraHeader=Authorization: Basic xxx'] */ function buildGitHttpAuthConfigArgs(publicGitUrl) { if (!shouldApplyDefaultGitAuth(publicGitUrl)) return []; try { const u = new URL(String(publicGitUrl).trim()); const user = String(process.env.DEFAULT_STUDENT_GIT_USER || '').trim(); const pass = String(process.env.DEFAULT_STUDENT_GIT_PASSWORD || '').trim(); const schemeHost = `${u.protocol}//${u.hostname}`; const token = Buffer.from(`${user}:${pass}`, 'utf8').toString('base64'); const headerVal = `Authorization: Basic ${token}`; const configKey = `http.${schemeHost}/.extraHeader`; return ['-c', `${configKey}=${headerVal}`]; } catch { return []; } } module.exports = { getDefaultPublicStudentGitUrl, stripGitCredentials, normalizeComparableGitRemote, sameGitRemoteIgnoringAuth, resolveCloneUrlForGit, buildGitHttpAuthConfigArgs, shouldApplyDefaultGitAuth, hasDefaultGitPasswordAuth, };