You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
4.8 KiB
183 lines
4.8 KiB
import axios, { type InternalAxiosRequestConfig } from "axios";
|
|
import { isLocale } from "../translations/config";
|
|
import { authBridge } from "./auth-bridge";
|
|
import { getClientCookie } from "./cookies";
|
|
|
|
const PROXY_PATH_PARAM = "__proxyPath";
|
|
function isAbsoluteUrl(url: string) {
|
|
return /^[a-z][a-z\d+\-.]*:\/\//i.test(url);
|
|
}
|
|
|
|
function shouldUseProxy() {
|
|
if (process.env.NEXT_PUBLIC_DISABLE_PROXY === "true") {
|
|
return false;
|
|
}
|
|
|
|
if (typeof window === "undefined") {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function withProxyPathParam(
|
|
params: InternalAxiosRequestConfig["params"],
|
|
proxyPath: string,
|
|
) {
|
|
if (params instanceof URLSearchParams) {
|
|
const nextParams = new URLSearchParams(params);
|
|
nextParams.set(PROXY_PATH_PARAM, proxyPath);
|
|
return nextParams;
|
|
}
|
|
|
|
if (typeof params === "string") {
|
|
const nextParams = new URLSearchParams(params);
|
|
nextParams.set(PROXY_PATH_PARAM, proxyPath);
|
|
return nextParams;
|
|
}
|
|
|
|
return {
|
|
...(params && typeof params === "object" ? params : {}),
|
|
[PROXY_PATH_PARAM]: proxyPath,
|
|
};
|
|
}
|
|
|
|
export function getApiRequestUrl(path: string) {
|
|
if (!isAbsoluteUrl(path) && shouldUseProxy()) {
|
|
const searchParams = new URLSearchParams({
|
|
[PROXY_PATH_PARAM]: path,
|
|
});
|
|
|
|
return `/api/proxy?${searchParams.toString()}`;
|
|
}
|
|
|
|
return `${process.env.NEXT_PUBLIC_API_BASE_URL}${path}`;
|
|
}
|
|
|
|
export function resolveMediaUrl(url: string | null | undefined): string | null {
|
|
if (!url || typeof url !== "string") {
|
|
return null;
|
|
}
|
|
const trimmed = url.trim();
|
|
if (!trimmed) {
|
|
return null;
|
|
}
|
|
|
|
// Blob and data URLs are generated in the current browser session and work immediately
|
|
if (trimmed.startsWith("blob:") || trimmed.startsWith("data:")) {
|
|
return trimmed;
|
|
}
|
|
|
|
if (shouldUseProxy()) {
|
|
if (isAbsoluteUrl(trimmed)) {
|
|
try {
|
|
const parsed = new URL(trimmed);
|
|
const apiBase = process.env.NEXT_PUBLIC_API_BASE_URL;
|
|
let isBackendHost = false;
|
|
|
|
if (
|
|
parsed.hostname === "127.0.0.1" ||
|
|
parsed.hostname === "localhost" ||
|
|
parsed.port === "8000" ||
|
|
parsed.port === "8001"
|
|
) {
|
|
isBackendHost = true;
|
|
} else if (apiBase) {
|
|
try {
|
|
const apiParsed = new URL(apiBase);
|
|
if (parsed.host === apiParsed.host) {
|
|
isBackendHost = true;
|
|
}
|
|
} catch {
|
|
// Ignore URL parsing error
|
|
}
|
|
}
|
|
|
|
if (
|
|
isBackendHost ||
|
|
parsed.pathname.startsWith("/static/") ||
|
|
parsed.pathname.startsWith("/media/")
|
|
) {
|
|
const proxyPath = `${parsed.pathname}${parsed.search}`;
|
|
const searchParams = new URLSearchParams({
|
|
[PROXY_PATH_PARAM]: proxyPath,
|
|
});
|
|
return `/api/proxy?${searchParams.toString()}`;
|
|
}
|
|
} catch {
|
|
// Fall through
|
|
}
|
|
} else {
|
|
const normalizedPath = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
const searchParams = new URLSearchParams({
|
|
[PROXY_PATH_PARAM]: normalizedPath,
|
|
});
|
|
return `/api/proxy?${searchParams.toString()}`;
|
|
}
|
|
}
|
|
|
|
if (isAbsoluteUrl(trimmed)) {
|
|
return trimmed;
|
|
}
|
|
|
|
const base = process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
const normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
return `${base}${normalized}`;
|
|
}
|
|
|
|
export const http = axios.create({
|
|
baseURL: shouldUseProxy()
|
|
? "/api/proxy"
|
|
: process.env.NEXT_PUBLIC_API_BASE_URL,
|
|
headers: {
|
|
Accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
},
|
|
withCredentials: true,
|
|
});
|
|
|
|
http.interceptors.request.use((config) => {
|
|
if (shouldUseProxy() && config.url && !isAbsoluteUrl(config.url)) {
|
|
config.params = withProxyPathParam(config.params, config.url);
|
|
config.url = "";
|
|
}
|
|
|
|
const stripNoToken = (v: string | null) => (v && v !== "NO_TOKEN" ? v : null);
|
|
|
|
const token =
|
|
authBridge.getToken() ??
|
|
stripNoToken(getClientCookie("HABIB_TOKEN")) ??
|
|
stripNoToken(getClientCookie("habib_token"));
|
|
|
|
if (token) {
|
|
config.headers.Authorization = `Token ${token}`;
|
|
}
|
|
|
|
const pathSegment =
|
|
typeof window !== "undefined"
|
|
? window.location.pathname.split("/")[1]
|
|
: undefined;
|
|
const docLang =
|
|
typeof document !== "undefined" ? document.documentElement.lang : undefined;
|
|
const cookieLanguage =
|
|
getClientCookie("HABIB_LANGUAGE") ??
|
|
getClientCookie("habib_language") ??
|
|
undefined;
|
|
const lang = isLocale(pathSegment)
|
|
? pathSegment
|
|
: isLocale(cookieLanguage)
|
|
? cookieLanguage
|
|
: isLocale(docLang)
|
|
? docLang
|
|
: "en";
|
|
|
|
config.headers["Accept-Language"] = lang;
|
|
config.headers["X-User-Language"] = lang;
|
|
|
|
return config;
|
|
});
|
|
|
|
http.interceptors.response.use(
|
|
(response) => response,
|
|
(error) => Promise.reject(error),
|
|
);
|