mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-06 17:37:47 +08:00
- User-Agent 改为 Electron 浏览器格式,新增 x-client-name/x-client-version/x-vscode-sessionid/x-goog-api-client header - 移除 MODEL_ALIAS_MAP 别名映射,改为仅剥离 -online 后缀 - thinking 自动注入关键字新增 gemini-3.1-pro - 新增定时 Antigravity UA 版本刷新任务(每 6 小时 + 启动时) - 前端 OAuth 设备授权新增 TOTP 验证码生成(otpauth),Region 改为按钮选择 - SSO OIDC 轮询日志 authorization_pending/slow_down 降级为 debug
80 lines
1.7 KiB
TypeScript
80 lines
1.7 KiB
TypeScript
import { ref, computed, onBeforeUnmount } from 'vue'
|
|
import { TOTP, Secret } from 'otpauth'
|
|
|
|
/**
|
|
* TOTP composable: 给定 secret 持续生成验证码并显示剩余秒数。
|
|
*/
|
|
export function useTotp() {
|
|
const secret = ref('')
|
|
const code = ref('')
|
|
const remaining = ref(0)
|
|
const error = ref('')
|
|
|
|
let timer: ReturnType<typeof setInterval> | null = null
|
|
let totp: TOTP | null = null
|
|
|
|
function parseSecret(raw: string): Secret | null {
|
|
const cleaned = raw.replace(/[\s-]/g, '')
|
|
if (!cleaned) return null
|
|
try {
|
|
return Secret.fromBase32(cleaned.toUpperCase())
|
|
} catch {
|
|
// 尝试 hex
|
|
}
|
|
try {
|
|
return Secret.fromHex(cleaned)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
function generate(): void {
|
|
if (!totp) {
|
|
code.value = ''
|
|
remaining.value = 0
|
|
return
|
|
}
|
|
code.value = totp.generate()
|
|
const epoch = Math.floor(Date.now() / 1000)
|
|
remaining.value = totp.period - (epoch % totp.period)
|
|
}
|
|
|
|
function start(rawSecret: string): void {
|
|
stop()
|
|
error.value = ''
|
|
secret.value = rawSecret
|
|
|
|
const parsed = parseSecret(rawSecret)
|
|
if (!parsed) {
|
|
error.value = rawSecret.trim() ? 'Secret 格式无效' : ''
|
|
return
|
|
}
|
|
|
|
totp = new TOTP({ secret: parsed, digits: 6, period: 30 })
|
|
generate()
|
|
timer = setInterval(generate, 1000)
|
|
}
|
|
|
|
function stop(): void {
|
|
if (timer) {
|
|
clearInterval(timer)
|
|
timer = null
|
|
}
|
|
totp = null
|
|
code.value = ''
|
|
remaining.value = 0
|
|
error.value = ''
|
|
}
|
|
|
|
onBeforeUnmount(stop)
|
|
|
|
return {
|
|
secret,
|
|
code: computed(() => code.value),
|
|
remaining: computed(() => remaining.value),
|
|
error: computed(() => error.value),
|
|
start,
|
|
stop,
|
|
}
|
|
}
|