github-actions[bot]
Update from GitHub Actions
7fc5208
raw
history blame
7.81 kB
import { authApiToken, authMiddleware } from "../../utils/auth.js";
import { addCorsHeaders } from "../../utils/cors.js";
import { chromium } from 'playwright';
import { getVerificationCode } from '../../utils/emailVerification.js';
export const onRequest = async (context: RouteContext): Promise<Response> => {
const request = context.request;
const env: Env = context.env;
// 验证权限
const authResponse = await authMiddleware(request, env);
const apiResponse = await authApiToken(request, env);
if (authResponse && apiResponse) {
return addCorsHeaders(authResponse);
}
try {
const { email } = await request.json() as any;
if (!email) {
throw new Error("Email is required");
}
// 获取账户信息
const accountsStr = await env.KV.get("accounts");
const accounts: any[] = accountsStr ? JSON.parse(accountsStr) : [];
const account = accounts.find(a => a.email === email);
if (!account) {
throw new Error("Account not found");
}
const clientId = env.ENTRA_CLIENT_ID;
const clientSecret = env.ENTRA_CLIENT_SECRET;
const redirectUri = env.AUTH_REDIRECT_URI;
let browser;
let context;
try {
browser = await chromium.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-blink-features=AutomationControlled', // 禁用自动化特征
'--disable-infobars',
'--window-size=1920,1080'
],
executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH, // 使用系统 Chromium
});
context = await browser.newContext();
const page = await context.newPage();
const authUrl = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?` +
`client_id=${clientId}` +
`&response_type=code` +
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
`&response_mode=query` +
`&scope=offline_access%20IMAP.AccessAsUser.All%20User.Read%20Mail.ReadWrite.Shared%20Mail.Send%20Mail.Read` +
`&prompt=consent` + //强制显示权限确认窗口
`&state=${email}`;
console.log(authUrl)
//开始认证
await page.goto(authUrl);
await page.fill('input[type="email"]', account.email);
await page.click('input[type="submit"]');
try {
await page.waitForSelector('#idA_PWD_SwitchToPassword', { timeout: 3000 });
await page.click('#idA_PWD_SwitchToPassword');
} catch (error) {
console.log(`没有切换到密码登录,继续执行: ${error}`);
}
try {
const timestamp = Math.floor(Date.now() / 1000);
await page.waitForSelector('#idA_PWD_SwitchToCredPicker', { timeout: 3000 });
try {
//处理email验证
const proof = [
{
"suffix": "godgodgame.com",
"apiUrl": "http://159.138.99.139:89/api/latest-email",
"token": env.PROOF_GODGODGAME_TOKEN
},
{
"suffix": "igiven.com",
"apiUrl": "http://159.138.99.139:90/api/latest-email",
"token": env.PROOF_IGIVEN_TOKEN
}
]
const suffix = email.substring(email.indexOf('@') + 1);
const proofConfig = proof.find(p => p.suffix === suffix)!;
const verificationCode = await getVerificationCode(proofConfig.apiUrl, proofConfig.token!, account.proofEmail, timestamp);
await page.fill('input[type="tel"]', verificationCode);
await page.click('button[type="submit"]');
}
catch (error) {
throw new Error(`邮箱验证失败:${error}`)
}
await page.click('#idA_PWD_SwitchToCredPicker');
await page.click('#tileList > div:nth-child(2) Button');
} catch (error) {
console.log(`没有直接发邮件验证,继续执行: ${error}`);
}
//输入密码
await page.waitForURL("https://login.live.com/**")
await page.fill('input[type="password"]', account.password);
await page.click('button[type="submit"]');
//确认登录
await page.waitForURL('https://login.live.com/ppsecure/**')
await page.click('button[type="submit"]#acceptButton'); // 同意按钮
//同意授权
try {
// 等待同意授权页面,如果超时5秒则跳过
await page.waitForURL("https://account.live.com/Consent/**", { timeout: 5000 })
await page.click('button[type="submit"][data-testid="appConsentPrimaryButton"]');
} catch (error) {
// 如果超时或页面未出现,直接继续执行
console.log("Consent page not found or timeout, skipping...");
}
await page.waitForURL((url)=>{
console.log(url)
return url.href.startsWith(redirectUri);
})
} finally {
if (context) await context.close();
if (browser) await browser.close();
}
// 等待回调处理完成,检查 KV 中是否有对应的 code
let code = null;
const maxRetries = 30;
let retries = 0;
while (!code && retries < maxRetries) {
const codeKey = `code_${email}`;
code = await env.KV.get(codeKey);
if (!code) {
await new Promise(resolve => setTimeout(resolve, 1000));
retries++;
}
}
if (!code) {
throw new Error("Authorization timeout");
}
// 使用授权码获取刷新令牌
const tokenResponse = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
code: code,
redirect_uri: redirectUri,
grant_type: 'authorization_code'
})
});
const tokenData: any = await tokenResponse.json();
if (!tokenData.refresh_token) {
throw new Error("Failed to get refresh token");
}
if (!tokenData.expires_in) {
throw new Error("Missing expires_in in token response");
}
// 存储刷新令牌和时间戳
const tokenInfo = {
...tokenData,
timestamp: Date.now()
};
console.log(tokenInfo);
await env.KV.put(`refresh_token_${email}`, JSON.stringify(tokenInfo));
// 删除临时授权码
await env.KV.delete(`code_${email}`);
return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error: any) {
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500 }
);
}
};