|
|
@@ -6,6 +6,7 @@ import axios, {
|
|
|
|
|
|
import { clearUser } from "@/lib/auth-types";
|
|
|
import { getApiBaseUrl } from "@/lib/env";
|
|
|
+import { SESSION_EXPIRED_GO_LOGIN } from "@/lib/session-expired-events";
|
|
|
|
|
|
/**
|
|
|
* 业务后端接口约定:
|
|
|
@@ -72,71 +73,12 @@ export class ApiError extends Error {
|
|
|
|
|
|
let authTimeoutDialogShown = false;
|
|
|
|
|
|
-/** 局域网 IP(无 NEXT_PUBLIC_SITE_URL 时仍用相对路径,避免误跳到 :80)。 */
|
|
|
-function isPrivateLanIpv4Hostname(hostname: string): boolean {
|
|
|
- const parts = hostname.split(".");
|
|
|
- if (
|
|
|
- parts.length !== 4 ||
|
|
|
- !parts.every((p) => /^\d+$/.test(p)) ||
|
|
|
- parts[0] === undefined ||
|
|
|
- parts[1] === undefined
|
|
|
- ) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- const a = Number(parts[0]);
|
|
|
- const b = Number(parts[1]);
|
|
|
- if (a === 10) return true;
|
|
|
- if (a === 172 && b >= 16 && b <= 31) return true;
|
|
|
- if (a === 192 && b === 168) return true;
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 会话失效后整页跳转登录(必须用绝对 URL,禁止相对 `/auth/login`):
|
|
|
- * 相对路径会保留当前 origin 的 `:4000`,国际化再补 `/zh` 就会变成 `host:4000/zh/...`。
|
|
|
- * - 配置了 `NEXT_PUBLIC_SITE_URL` 时一律用它(不再做 hostname 白名单,避免 www/裸域/直连端口不一致时误回退相对路径)。
|
|
|
- * - 本机回环、内网 IP 且无 SITE_URL:仍用相对路径。
|
|
|
- * - 其它:用 `协议//主机名`(不含端口)为基址拼 `/auth/login`。
|
|
|
- */
|
|
|
-function loginHrefAfterSessionExpired(): string {
|
|
|
- const path = "/auth/login";
|
|
|
- if (typeof window === "undefined") return path;
|
|
|
-
|
|
|
- const { hostname, protocol } = window.location;
|
|
|
- if (
|
|
|
- hostname === "localhost" ||
|
|
|
- hostname === "127.0.0.1" ||
|
|
|
- hostname === "[::1]"
|
|
|
- ) {
|
|
|
- return path;
|
|
|
- }
|
|
|
-
|
|
|
- const site = process.env.NEXT_PUBLIC_SITE_URL?.trim().replace(/\/$/, "");
|
|
|
- if (site) {
|
|
|
- try {
|
|
|
- return new URL(path, `${site}/`).href;
|
|
|
- } catch {
|
|
|
- /* fall through */
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (isPrivateLanIpv4Hostname(hostname)) {
|
|
|
- return path;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- return new URL(path, `${protocol}//${hostname}/`).href;
|
|
|
- } catch {
|
|
|
- return path;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
function notifySessionTimeoutAndRedirect() {
|
|
|
if (typeof window === "undefined") return;
|
|
|
if (authTimeoutDialogShown) return;
|
|
|
authTimeoutDialogShown = true;
|
|
|
window.alert("登录超时,请重新登录");
|
|
|
- window.location.replace(loginHrefAfterSessionExpired());
|
|
|
+ window.dispatchEvent(new Event(SESSION_EXPIRED_GO_LOGIN));
|
|
|
}
|
|
|
|
|
|
function isPlainObject(v: unknown): v is Record<string, unknown> {
|