You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

117 lines
3.9 KiB

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.<scheme>//<host>/.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,
};