feat: 优化web

This commit is contained in:
2026-04-23 18:58:13 +08:00
commit 544a2f3428
160 changed files with 27327 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
'use client';
import { getOAuthClientId, getOAuthRedirectUri, getPublicApiOrigin } from '@/lib/env';
import { createPKCEPair } from '@/lib/oauth/pkce';
const VERIFIER_KEY = 'smart_oauth_pkce_verifier';
/** 跳转浏览器授权页(与 GET /oauth/authorize 一致);PKCE verifier 写入 sessionStorage,回调页取出换 token。 */
export async function redirectToAuthorize(): Promise<void> {
const { verifier, challenge } = await createPKCEPair();
if (typeof window !== 'undefined') {
sessionStorage.setItem(VERIFIER_KEY, verifier);
}
const params = new URLSearchParams({
response_type: 'code',
client_id: getOAuthClientId(),
redirect_uri: getOAuthRedirectUri(),
scope: 'openid',
code_challenge: challenge,
code_challenge_method: 'S256',
state:
typeof crypto !== 'undefined' && 'randomUUID' in crypto
? crypto.randomUUID()
: String(Date.now()),
});
const url = `${getPublicApiOrigin()}/oauth/authorize?${params.toString()}`;
window.location.href = url;
}
export function takeStoredPkceVerifier(): string | null {
if (typeof window === 'undefined') {
return null;
}
const v = sessionStorage.getItem(VERIFIER_KEY);
if (v) {
sessionStorage.removeItem(VERIFIER_KEY);
}
return v;
}
+19
View File
@@ -0,0 +1,19 @@
/** RFC 7636:生成 code_verifier 与 S256 code_challenge(与 Go VerifyPKCES256 一致)。 */
function base64Url(buf: ArrayBuffer): string {
const bytes = new Uint8Array(buf);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]!);
}
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
export async function createPKCEPair(): Promise<{ verifier: string; challenge: string }> {
const arr = new Uint8Array(32);
crypto.getRandomValues(arr);
const verifier = base64Url(arr.buffer);
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
const challenge = base64Url(hash);
return { verifier, challenge };
}