feat: login in xui
This commit is contained in:
3
.env.example
Normal file
3
.env.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
XUI_HOST=
|
||||||
|
XUI_USER=
|
||||||
|
XUI_PASSWORD=
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -32,6 +32,7 @@ yarn-error.log*
|
|||||||
|
|
||||||
# env files (can opt-in for committing if needed)
|
# env files (can opt-in for committing if needed)
|
||||||
.env*
|
.env*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
@@ -41,3 +42,6 @@ yarn-error.log*
|
|||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
certificates
|
certificates
|
||||||
|
|
||||||
|
# my
|
||||||
|
repomix-output.*
|
||||||
2
src/app/api/_lib/index.ts
Normal file
2
src/app/api/_lib/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { authFetch } from "./login";
|
||||||
|
export { authFetch };
|
||||||
97
src/app/api/_lib/login.ts
Normal file
97
src/app/api/_lib/login.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
const cookieJar: Map<string, string> = new Map();
|
||||||
|
|
||||||
|
let loginInProgress: Promise<void> | null = null;
|
||||||
|
|
||||||
|
interface Cookie {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseSetCookie = (header: string): Cookie | null => {
|
||||||
|
const parts = header.split(";");
|
||||||
|
const nameValue = parts[0].trim();
|
||||||
|
const equalIndex = nameValue.indexOf("=");
|
||||||
|
if (equalIndex === -1) return null;
|
||||||
|
const name = nameValue.substring(0, equalIndex).trim();
|
||||||
|
const value = nameValue.substring(equalIndex + 1).trim();
|
||||||
|
return { name, value };
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateCookieJar = (setCookieHeaders: string[]) => {
|
||||||
|
setCookieHeaders.forEach((header) => {
|
||||||
|
const cookie = parseSetCookie(header);
|
||||||
|
if (cookie) cookieJar.set(cookie.name, cookie.value);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const login = async (): Promise<void> => {
|
||||||
|
const username = process.env.XUI_USER || "";
|
||||||
|
const password = process.env.XUI_PASSWORD || "";
|
||||||
|
const details = {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
const encodedData = new URLSearchParams(details).toString();
|
||||||
|
const response = await fetch(process.env.XUI_HOST + "/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
||||||
|
},
|
||||||
|
body: encodedData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const setCookieHeaders = response.headers.getSetCookie();
|
||||||
|
updateCookieJar(setCookieHeaders);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const authFetch = async (url: string, options: RequestInit = {}) => {
|
||||||
|
if (cookieJar.size === 0) {
|
||||||
|
if (!loginInProgress) {
|
||||||
|
loginInProgress = login().finally(() => {
|
||||||
|
loginInProgress = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await loginInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cookieHeader = Array.from(cookieJar.entries())
|
||||||
|
.map(([name, value]) => `${name}=${value}`)
|
||||||
|
.join("; ");
|
||||||
|
const headers = {
|
||||||
|
...options.headers,
|
||||||
|
Cookie: cookieHeader,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(url, { ...options, headers });
|
||||||
|
|
||||||
|
const setCookieHeaders = response.headers.getSetCookie();
|
||||||
|
if (setCookieHeaders.length > 0) updateCookieJar(setCookieHeaders);
|
||||||
|
|
||||||
|
if (response.headers.get("Content-Type")?.startsWith("text/html")) {
|
||||||
|
if (!loginInProgress) {
|
||||||
|
loginInProgress = login().finally(() => {
|
||||||
|
loginInProgress = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await loginInProgress;
|
||||||
|
|
||||||
|
const newCookieHeader = Array.from(cookieJar.entries())
|
||||||
|
.map(([name, value]) => `${name}=${value}`)
|
||||||
|
.join("; ");
|
||||||
|
const newHeaders = {
|
||||||
|
...options.headers,
|
||||||
|
Cookie: newCookieHeader,
|
||||||
|
};
|
||||||
|
|
||||||
|
const retryResponse = await fetch(url, { ...options, headers: newHeaders });
|
||||||
|
const retrySetCookieHeaders = retryResponse.headers.getSetCookie();
|
||||||
|
if (retrySetCookieHeaders.length > 0) updateCookieJar(retrySetCookieHeaders);
|
||||||
|
return retryResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
8
src/app/api/get_url/route.ts
Normal file
8
src/app/api/get_url/route.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { authFetch } from "../_lib/login";
|
||||||
|
|
||||||
|
//TODO: its just for testing. Make it normal
|
||||||
|
export async function GET() {
|
||||||
|
const res = await authFetch(process.env.XUI_HOST + "/panel/api/inbounds/list");
|
||||||
|
const data = await res.json();
|
||||||
|
return Response.json(data);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user