Files
CC-Switch/src/lib/utils/base64.ts
T
Thefool c49cf96a16 fix(grokbuild): complete proxy and deep-link integrations (#5677)
* fix(grokbuild): complete proxy integration

* fix(deeplink): preview GrokBuild configs safely

* test(app): stabilize provider integration suite

* fix(grokbuild): address review feedback

* fix(grokbuild): resolve remaining review findings

* fix(grokbuild): use native sessions and harden previews
2026-07-31 14:56:42 +08:00

67 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 把 URL-safe Base64RFC 4648 §5)归一到 `atob` 认识的标准字母表,
* 并还原被 URL 解析吃掉的 `+`(会变成空格)。
*
* 标准 Base64 的字母表里不存在 `-` 与 `_`,因此这两条替换不会误伤标准输入。
*
* ⚠️ 这里必须与后端 `deeplink/utils.rs::decode_base64_param` 的语义保持一致。
* 后端依次尝试 STANDARD / STANDARD_NO_PAD / URL_SAFE / URL_SAFE_NO_PAD 四种引擎;
* 前端若只认标准字母表,同一条链接就会出现「确认框显示不透明 Base64 或空列表、
* 后端却解码并保存了真实内容」的错位——即用户在看不见 payload 的情况下点了确认。
*/
function toStandardBase64Alphabet(value: string): string {
return value.replace(/ /g, "+").replace(/-/g, "+").replace(/_/g, "/");
}
function trimOuterLineBreaks(value: string): string {
return value.replace(/^[\r\n]+|[\r\n]+$/g, "");
}
/**
* Decode Base64 encoded UTF-8 string
*
* This function handles various Base64 edge cases that can occur when
* Base64 strings are passed through URLs:
* - Spaces (URL parsing may convert '+' to space)
* - Missing padding ('=' characters)
* - Standard and URL-safe alphabets (RFC 4648 §4 and §5)
*
* @param str - Base64 encoded string
* @returns Decoded UTF-8 string
*/
export function decodeBase64Utf8(str: string): string {
try {
// Keep spaces intact until they are restored to `+`. Using `trim()` here
// would discard a URL-decoded `+` at either edge and diverge from the
// backend decoder, which trims only CR/LF characters.
let cleaned = toStandardBase64Alphabet(trimOuterLineBreaks(str));
// Try to decode with standard Base64 first
try {
const binString = atob(cleaned);
const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0)!);
return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
} catch (e1) {
// If standard fails, try adding padding
const remainder = cleaned.length % 4;
if (remainder !== 0) {
cleaned += "=".repeat(4 - remainder);
}
const binString = atob(cleaned);
const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0)!);
return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
}
} catch (e) {
console.error("Base64 decode error:", e, "Input:", str);
// Last resort fallback using deprecated but sometimes working method
try {
return decodeURIComponent(
escape(atob(toStandardBase64Alphabet(trimOuterLineBreaks(str)))),
);
} catch {
// If all else fails, return original string
return str;
}
}
}