35 lines
1.9 KiB
TypeScript
35 lines
1.9 KiB
TypeScript
/**
|
|
* 在正式入口验证 GitHub 登录,不跟随跳转、不访问用户资料。
|
|
* Secret 名称齐全不代表运行时可用:必须实际签发 flow Cookie 并读取 D1 工作区。
|
|
*/
|
|
export async function verifyGithubDeployment(
|
|
hostname: string,
|
|
clientId: string,
|
|
fetcher: typeof fetch = fetch,
|
|
): Promise<void> {
|
|
const origin = `https://${hostname}`;
|
|
async function probe(path: string): Promise<Response> {
|
|
try {
|
|
return await fetcher(`${origin}${path}`, { redirect: 'manual', signal: AbortSignal.timeout(30_000) });
|
|
} catch {
|
|
// 不把网络异常的原始内容或带 OAuth state 的响应头写入部署日志。
|
|
throw new Error(`GitHub 登录入口验收无法连接 ${path},请检查正式域名的 DNS、证书及网络后重跑 Deploy。`);
|
|
}
|
|
}
|
|
const login = await probe('/auth/login');
|
|
const location = login.headers.get('Location');
|
|
const target = location && URL.canParse(location, origin) ? new URL(location, origin) : null;
|
|
if (login.status !== 302 || target?.origin !== 'https://github.com'
|
|
|| target.pathname !== '/login/oauth/authorize'
|
|
|| target.searchParams.get('client_id') !== clientId
|
|
|| target.searchParams.get('redirect_uri') !== `${origin}/auth/callback`
|
|
|| !login.headers.getSetCookie().some((value) => value.startsWith('__Host-edgessh-oauth='))) {
|
|
throw new Error(`GitHub 登录入口验收失败(/auth/login HTTP ${login.status})。请检查 Worker 配置与正式入口,修复后重跑 Deploy;不要更换 ENCRYPTION_KEY。`);
|
|
}
|
|
const account = await probe('/api/auth/me');
|
|
// 无 Cookie 的请求必须拒绝授权;500/503 表示数据库或认证状态仍不可用。
|
|
if (account.status !== 401) {
|
|
throw new Error(`GitHub 工作区验收失败(/api/auth/me HTTP ${account.status})。请检查 D1 迁移与认证方式是否部署完成,再重跑 Deploy。`);
|
|
}
|
|
}
|