From c75dd3e78bb0f55392a9cc90d49fd2dc17a23625 Mon Sep 17 00:00:00 2001 From: divocat Date: Sun, 5 Oct 2025 16:09:26 +0300 Subject: [PATCH] feat: add base clash api methods --- fe-app-podkop/src/clash/index.ts | 2 + .../src/clash/methods/createBaseApiRequest.ts | 28 ++++++++ fe-app-podkop/src/clash/methods/getConfig.ts | 13 ++++ .../src/clash/methods/getGroupDelay.ts | 19 +++++ fe-app-podkop/src/clash/methods/getProxies.ts | 13 ++++ fe-app-podkop/src/clash/methods/getVersion.ts | 13 ++++ fe-app-podkop/src/clash/methods/index.ts | 5 ++ fe-app-podkop/src/clash/types.ts | 53 ++++++++++++++ fe-app-podkop/src/main.ts | 1 + .../luci-static/resources/view/podkop/main.js | 71 +++++++++++++++++++ .../resources/view/podkop/podkop.js | 15 ++++ 11 files changed, 233 insertions(+) create mode 100644 fe-app-podkop/src/clash/index.ts create mode 100644 fe-app-podkop/src/clash/methods/createBaseApiRequest.ts create mode 100644 fe-app-podkop/src/clash/methods/getConfig.ts create mode 100644 fe-app-podkop/src/clash/methods/getGroupDelay.ts create mode 100644 fe-app-podkop/src/clash/methods/getProxies.ts create mode 100644 fe-app-podkop/src/clash/methods/getVersion.ts create mode 100644 fe-app-podkop/src/clash/methods/index.ts create mode 100644 fe-app-podkop/src/clash/types.ts diff --git a/fe-app-podkop/src/clash/index.ts b/fe-app-podkop/src/clash/index.ts new file mode 100644 index 0000000..c3b7574 --- /dev/null +++ b/fe-app-podkop/src/clash/index.ts @@ -0,0 +1,2 @@ +export * from './types'; +export * from './methods'; diff --git a/fe-app-podkop/src/clash/methods/createBaseApiRequest.ts b/fe-app-podkop/src/clash/methods/createBaseApiRequest.ts new file mode 100644 index 0000000..b63516a --- /dev/null +++ b/fe-app-podkop/src/clash/methods/createBaseApiRequest.ts @@ -0,0 +1,28 @@ +import { IBaseApiResponse } from '../types'; + +export async function createBaseApiRequest( + fetchFn: () => Promise, +): Promise> { + try { + const response = await fetchFn(); + + if (!response.ok) { + return { + success: false as const, + message: `HTTP error ${response.status}: ${response.statusText}`, + }; + } + + const data: T = await response.json(); + + return { + success: true as const, + data, + }; + } catch (e) { + return { + success: false as const, + message: e instanceof Error ? e.message : 'Unknown error', + }; + } +} diff --git a/fe-app-podkop/src/clash/methods/getConfig.ts b/fe-app-podkop/src/clash/methods/getConfig.ts new file mode 100644 index 0000000..a782ba1 --- /dev/null +++ b/fe-app-podkop/src/clash/methods/getConfig.ts @@ -0,0 +1,13 @@ +import { ClashAPI, IBaseApiResponse } from '../types'; +import { createBaseApiRequest } from './createBaseApiRequest'; + +export async function getClashConfig(): Promise< + IBaseApiResponse +> { + return createBaseApiRequest(() => + fetch('http://192.168.160.129:9090/configs', { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }), + ); +} diff --git a/fe-app-podkop/src/clash/methods/getGroupDelay.ts b/fe-app-podkop/src/clash/methods/getGroupDelay.ts new file mode 100644 index 0000000..bbad3f2 --- /dev/null +++ b/fe-app-podkop/src/clash/methods/getGroupDelay.ts @@ -0,0 +1,19 @@ +import { ClashAPI, IBaseApiResponse } from '../types'; +import { createBaseApiRequest } from './createBaseApiRequest'; + +export async function getClashGroupDelay( + group: string, + url = 'https://www.gstatic.com/generate_204', + timeout = 2000, +): Promise> { + const endpoint = `http://192.168.160.129:9090/group/${group}/delay?url=${encodeURIComponent( + url, + )}&timeout=${timeout}`; + + return createBaseApiRequest(() => + fetch(endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }), + ); +} diff --git a/fe-app-podkop/src/clash/methods/getProxies.ts b/fe-app-podkop/src/clash/methods/getProxies.ts new file mode 100644 index 0000000..c431b2e --- /dev/null +++ b/fe-app-podkop/src/clash/methods/getProxies.ts @@ -0,0 +1,13 @@ +import { ClashAPI, IBaseApiResponse } from '../types'; +import { createBaseApiRequest } from './createBaseApiRequest'; + +export async function getClashProxies(): Promise< + IBaseApiResponse +> { + return createBaseApiRequest(() => + fetch('http://192.168.160.129:9090/proxies', { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }), + ); +} diff --git a/fe-app-podkop/src/clash/methods/getVersion.ts b/fe-app-podkop/src/clash/methods/getVersion.ts new file mode 100644 index 0000000..0f99ede --- /dev/null +++ b/fe-app-podkop/src/clash/methods/getVersion.ts @@ -0,0 +1,13 @@ +import { ClashAPI, IBaseApiResponse } from '../types'; +import { createBaseApiRequest } from './createBaseApiRequest'; + +export async function getClashVersion(): Promise< + IBaseApiResponse +> { + return createBaseApiRequest(() => + fetch('http://192.168.160.129:9090/version', { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }), + ); +} diff --git a/fe-app-podkop/src/clash/methods/index.ts b/fe-app-podkop/src/clash/methods/index.ts new file mode 100644 index 0000000..bce1a71 --- /dev/null +++ b/fe-app-podkop/src/clash/methods/index.ts @@ -0,0 +1,5 @@ +export * from './createBaseApiRequest'; +export * from './getConfig'; +export * from './getGroupDelay'; +export * from './getProxies'; +export * from './getVersion'; diff --git a/fe-app-podkop/src/clash/types.ts b/fe-app-podkop/src/clash/types.ts new file mode 100644 index 0000000..a54a55f --- /dev/null +++ b/fe-app-podkop/src/clash/types.ts @@ -0,0 +1,53 @@ +export type IBaseApiResponse = + | { + success: true; + data: T; + } + | { + success: false; + message: string; + }; + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace ClashAPI { + export interface Version { + meta: boolean; + premium: boolean; + version: string; + } + + export interface Config { + port: number; + 'socks-port': number; + 'redir-port': number; + 'tproxy-port': number; + 'mixed-port': number; + 'allow-lan': boolean; + 'bind-address': string; + mode: 'Rule' | 'Global' | 'Direct'; + 'mode-list': string[]; + 'log-level': 'debug' | 'info' | 'warn' | 'error'; + ipv6: boolean; + tun: null | Record; + } + + export interface ProxyHistoryEntry { + time: string; + delay: number; + } + + export interface ProxyBase { + type: string; + name: string; + udp: boolean; + history: ProxyHistoryEntry[]; + now?: string; + all?: string[]; + } + + export interface Proxies { + proxies: Record; + } + + export type Delays = Record; +} diff --git a/fe-app-podkop/src/main.ts b/fe-app-podkop/src/main.ts index f3656c5..34b2c09 100644 --- a/fe-app-podkop/src/main.ts +++ b/fe-app-podkop/src/main.ts @@ -4,4 +4,5 @@ export * from './validators'; export * from './helpers'; +export * from './clash'; export * from './constants'; diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js index ec52014..cc1c318 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js @@ -556,6 +556,72 @@ function maskIP(ip = "") { const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; return ip.replace(ipv4Regex, (_match, _p1, _p2, _p3, p4) => `XX.XX.XX.${p4}`); } + +// src/clash/methods/createBaseApiRequest.ts +async function createBaseApiRequest(fetchFn) { + try { + const response = await fetchFn(); + if (!response.ok) { + return { + success: false, + message: `HTTP error ${response.status}: ${response.statusText}` + }; + } + const data = await response.json(); + return { + success: true, + data + }; + } catch (e) { + return { + success: false, + message: e instanceof Error ? e.message : "Unknown error" + }; + } +} + +// src/clash/methods/getConfig.ts +async function getClashConfig() { + return createBaseApiRequest( + () => fetch("http://192.168.160.129:9090/configs", { + method: "GET", + headers: { "Content-Type": "application/json" } + }) + ); +} + +// src/clash/methods/getGroupDelay.ts +async function getClashGroupDelay(group, url = "https://www.gstatic.com/generate_204", timeout = 2e3) { + const endpoint = `http://192.168.160.129:9090/group/${group}/delay?url=${encodeURIComponent( + url + )}&timeout=${timeout}`; + return createBaseApiRequest( + () => fetch(endpoint, { + method: "GET", + headers: { "Content-Type": "application/json" } + }) + ); +} + +// src/clash/methods/getProxies.ts +async function getClashProxies() { + return createBaseApiRequest( + () => fetch("http://192.168.160.129:9090/proxies", { + method: "GET", + headers: { "Content-Type": "application/json" } + }) + ); +} + +// src/clash/methods/getVersion.ts +async function getClashVersion() { + return createBaseApiRequest( + () => fetch("http://192.168.160.129:9090/version", { + method: "GET", + headers: { "Content-Type": "application/json" } + }) + ); +} return baseclass.extend({ ALLOWED_WITH_RUSSIA_INSIDE, BOOTSTRAP_DNS_SERVER_OPTIONS, @@ -576,8 +642,13 @@ return baseclass.extend({ UPDATE_INTERVAL_OPTIONS, bulkValidate, copyToClipboard, + createBaseApiRequest, executeShellCommand, getBaseUrl, + getClashConfig, + getClashGroupDelay, + getClashProxies, + getClashVersion, injectGlobalStyles, maskIP, parseValueList, diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js index 0b1e6c6..7607ab2 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js @@ -12,6 +12,21 @@ const EntryNode = { async render() { main.injectGlobalStyles(); + main.getClashVersion() + .then(result => console.log('getClashVersion - then', result)) + .catch(err => console.log('getClashVersion - err', err)) + .finally(() => console.log('getClashVersion - finish')); + + main.getClashConfig() + .then(result => console.log('getClashConfig - then', result)) + .catch(err => console.log('getClashConfig - err', err)) + .finally(() => console.log('getClashConfig - finish')); + + main.getClashProxies() + .then(result => console.log('getClashProxies - then', result)) + .catch(err => console.log('getClashProxies - err', err)) + .finally(() => console.log('getClashProxies - finish')); + const podkopFormMap = new form.Map('podkop', '', null, ['main', 'extra']); // Main Section