80 lines
3.0 KiB
TypeScript
80 lines
3.0 KiB
TypeScript
import { apiFetch } from "@/core/api/fetch-client";
|
||
import { getBackendBaseURL } from "@/core/config";
|
||
|
||
import type { LoginByUsernameResponse } from "./index";
|
||
|
||
export interface AuthenticationOptions {
|
||
password_login_enabled: boolean;
|
||
registration_enabled: boolean;
|
||
sso_enabled: boolean;
|
||
single_session_enabled?: boolean;
|
||
needs_setup?: boolean;
|
||
}
|
||
|
||
function accountError(body: unknown, fallback: string): string {
|
||
const detail = (body as { detail?: unknown } | null)?.detail ?? body;
|
||
if (typeof detail === "string") return detail;
|
||
if (Array.isArray(detail)) {
|
||
return detail.map((item) => item?.msg).filter(Boolean).join(";") || fallback;
|
||
}
|
||
if (detail && typeof detail === "object") {
|
||
const value = detail as { message?: string; msg?: string };
|
||
return value.message || value.msg || fallback;
|
||
}
|
||
return fallback;
|
||
}
|
||
|
||
/** Bound unauthenticated requests so an unreachable gateway offers a retry. */
|
||
async function accountRequest<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||
const controller = new AbortController();
|
||
const timer = setTimeout(() => controller.abort(), 20_000);
|
||
try {
|
||
const response = await apiFetch(`${getBackendBaseURL()}/api/v1/auth${path}`, {
|
||
...init,
|
||
signal: controller.signal,
|
||
});
|
||
const body: unknown = await response.json().catch(() => null);
|
||
if (!response.ok) throw new Error(accountError(body, "请求失败,请稍后重试"));
|
||
return body as T;
|
||
} catch (error) {
|
||
if (controller.signal.aborted) throw new Error("连接超时,请检查网络后重试");
|
||
if (error instanceof TypeError) throw new Error("无法连接服务器,请检查网络后重试");
|
||
throw error;
|
||
} finally {
|
||
clearTimeout(timer);
|
||
}
|
||
}
|
||
|
||
export async function fetchAuthenticationOptions(): Promise<AuthenticationOptions> {
|
||
const options = await accountRequest<AuthenticationOptions>("/options", { cache: "no-store" });
|
||
if (!options || [options.password_login_enabled, options.registration_enabled, options.sso_enabled]
|
||
.some((value) => typeof value !== "boolean")) {
|
||
throw new Error("无法读取登录设置,请联系管理员或重试");
|
||
}
|
||
return options;
|
||
}
|
||
|
||
export function loginWithPassword(username: string, password: string): Promise<LoginByUsernameResponse> {
|
||
return accountRequest("/login/local", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||
body: new URLSearchParams({ username, password }),
|
||
});
|
||
}
|
||
|
||
export function registerAccount(username: string, password: string): Promise<LoginByUsernameResponse> {
|
||
return accountRequest("/register", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ username, password }),
|
||
});
|
||
}
|
||
|
||
export function initializeAdministrator(email: string, password: string): Promise<LoginByUsernameResponse> {
|
||
return accountRequest<LoginByUsernameResponse>("/initialize", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ email, password }),
|
||
});
|
||
}
|