feat: 更新SproutGate前后端代码
This commit is contained in:
@@ -12,6 +12,32 @@ export const API_BASE = (() => {
|
||||
/** `public/logo192.png`,含 Vite `base` 前缀,避免子路径部署时顶栏/开屏裂图 */
|
||||
export const LOGO_192_SRC = `${import.meta.env.BASE_URL}logo192.png`;
|
||||
|
||||
/** 全站随机背景(`GET /api/random?format=json`,见 https://randbg.api.smyhub.com) */
|
||||
export const RAND_BG_API_ORIGIN = "https://randbg.api.smyhub.com";
|
||||
|
||||
const SELF_SERVICE_ACCOUNT_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
/** 与自助注册规则一致:随机小写字母与数字(默认长度 10) */
|
||||
export function randomSelfServiceAccount(length = 10) {
|
||||
const n = Math.max(3, Math.min(32, Number(length) || 10));
|
||||
const buf = new Uint8Array(n);
|
||||
crypto.getRandomValues(buf);
|
||||
let s = "";
|
||||
for (let i = 0; i < n; i++) {
|
||||
s += SELF_SERVICE_ACCOUNT_ALPHABET[buf[i] % SELF_SERVICE_ACCOUNT_ALPHABET.length];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** 输入时规范为自助注册允许的字符(小写、数字),超长截断 */
|
||||
export function normalizeSelfServiceAccountInput(raw, maxLen = 32) {
|
||||
const m = Math.max(3, Math.min(32, maxLen));
|
||||
return String(raw || "")
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]/g, "")
|
||||
.slice(0, m);
|
||||
}
|
||||
|
||||
/** 浏览器侧 IP/地理(与后端写入「最后访问」字段配合使用) */
|
||||
export const CLIENT_GEO_LOOKUP_URL = "https://cf-ip-geo.smyhub.com/api";
|
||||
|
||||
@@ -130,15 +156,35 @@ export function getPublicAccountFromPathname(pathname) {
|
||||
export function getAuthFlowFromSearch(search) {
|
||||
const params = new URLSearchParams(search);
|
||||
const redirectUri = (params.get("redirect_uri") || params.get("return_url") || "").trim();
|
||||
const scopeRaw = (params.get("scope") || "").trim();
|
||||
const scopes = scopeRaw
|
||||
? scopeRaw.split(/[\s+]+/).map((s) => s.trim()).filter(Boolean)
|
||||
: [];
|
||||
return {
|
||||
redirectUri,
|
||||
state: (params.get("state") || "").trim(),
|
||||
prompt: (params.get("prompt") || "").trim(),
|
||||
clientId: (params.get("client_id") || "").trim(),
|
||||
clientName: (params.get("client_name") || "").trim()
|
||||
clientName: (params.get("client_name") || "").trim(),
|
||||
scopes,
|
||||
scopeRaw
|
||||
};
|
||||
}
|
||||
|
||||
/** 授权页展示用:回跳地址可读标签(主机 + 路径) */
|
||||
export function formatOAuthRedirectLabel(redirectUri) {
|
||||
if (!redirectUri || typeof redirectUri !== "string") return "—";
|
||||
const t = redirectUri.trim();
|
||||
if (!t) return "—";
|
||||
try {
|
||||
const u = new URL(t, typeof window !== "undefined" ? window.location.href : "https://example.com");
|
||||
const path = u.pathname === "/" ? "" : u.pathname;
|
||||
return `${u.host}${path}` || u.host;
|
||||
} catch {
|
||||
return t.length > 64 ? `${t.slice(0, 61)}…` : t;
|
||||
}
|
||||
}
|
||||
|
||||
const AUTH_CLIENT_ID_KEY = "sproutgate_auth_client_id";
|
||||
const AUTH_CLIENT_NAME_KEY = "sproutgate_auth_client_name";
|
||||
|
||||
@@ -185,3 +231,15 @@ export function buildAuthCallbackUrl(redirectUri, payload) {
|
||||
url.hash = hashParams.toString();
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/** 用户拒绝授权:回跳到应用地址,在 URL hash 中携带 OAuth 风格 error(与成功回跳一致使用 fragment) */
|
||||
export function buildAuthDenyCallbackUrl(redirectUri, state = "") {
|
||||
if (!redirectUri || typeof redirectUri !== "string") return "";
|
||||
const url = new URL(redirectUri, window.location.href);
|
||||
const hashParams = new URLSearchParams();
|
||||
hashParams.set("error", "access_denied");
|
||||
hashParams.set("error_description", "resource_owner_denied");
|
||||
if (state) hashParams.set("state", String(state));
|
||||
url.hash = hashParams.toString();
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user