mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Add: API request lib
This commit is contained in:
parent
fc4c58ae92
commit
b60ebfe75f
1221
package-lock.json
generated
1221
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@ -26,19 +26,20 @@
|
||||
"start-dev": "webpack-dev-server --config=configs/webpack/dev.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.1.0",
|
||||
"@babel/core": "^7.1.0",
|
||||
"@babel/cli": "^7.1.2",
|
||||
"@babel/core": "^7.1.2",
|
||||
"@babel/preset-env": "^7.1.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@types/node": "^10.10.3",
|
||||
"@types/react": "^16.4.14",
|
||||
"@types/react-dom": "^16.0.7",
|
||||
"@types/node": "^10.11.4",
|
||||
"@types/react": "^16.4.15",
|
||||
"@types/react-dom": "^16.0.8",
|
||||
"@types/react-i18next": "^7.8.2",
|
||||
"@types/react-router-dom": "^4.3.1",
|
||||
"@types/react-sortable-hoc": "^0.6.4",
|
||||
"@types/yaml": "^1.0.0",
|
||||
"autoprefixer": "^9.1.5",
|
||||
"awesome-typescript-loader": "^5.2.1",
|
||||
"babel-loader": "^8.0.2",
|
||||
"babel-loader": "^8.0.4",
|
||||
"css-loader": "^1.0.0",
|
||||
"file-loader": "^2.0.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
@ -49,28 +50,29 @@
|
||||
"react-hot-loader": "^4.3.11",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^0.23.0",
|
||||
"stylelint": "^9.5.0",
|
||||
"stylelint": "^9.6.0",
|
||||
"stylelint-config-standard": "^18.2.0",
|
||||
"stylelint-webpack-plugin": "^0.10.5",
|
||||
"tslint": "^5.11.0",
|
||||
"tslint-config-standard": "^8.0.1",
|
||||
"tslint-loader": "^3.6.0",
|
||||
"uglifyjs-webpack-plugin": "^2.0.1",
|
||||
"webpack": "^4.19.1",
|
||||
"webpack-cli": "^3.1.1",
|
||||
"webpack-dev-middleware": "^3.3.0",
|
||||
"webpack-dev-server": "^3.1.8",
|
||||
"webpack": "^4.20.2",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-middleware": "^3.4.0",
|
||||
"webpack-dev-server": "^3.1.9",
|
||||
"webpack-merge": "^4.1.4",
|
||||
"webpack-pwa-manifest": "^3.7.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"classnames": "^2.2.6",
|
||||
"dayjs": "^1.7.5",
|
||||
"dayjs": "^1.7.7",
|
||||
"i18next": "^11.9.0",
|
||||
"i18next-browser-languagedetector": "^2.2.3",
|
||||
"immer": "^1.7.2",
|
||||
"ini": "^1.3.5",
|
||||
"mobx": "^5.1.2",
|
||||
"mobx": "^5.5.0",
|
||||
"mobx-react": "^5.2.8",
|
||||
"mobx-react-router": "^4.0.5",
|
||||
"node-sass": "^4.9.3",
|
||||
@ -79,6 +81,7 @@
|
||||
"react-i18next": "^7.12.0",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-sortable-hoc": "^0.8.3",
|
||||
"typescript": "^3.0.3"
|
||||
"typescript": "^3.1.1",
|
||||
"yaml": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export function getLocalStorageItem (key: string) {
|
||||
return window.localStorage.getItem(key)
|
||||
export function getLocalStorageItem (key: string, defaultValue = '') {
|
||||
return window.localStorage.getItem(key) || defaultValue
|
||||
}
|
||||
|
||||
export function setLocalStorageItem (key: string, value: string) {
|
||||
@ -22,3 +22,5 @@ export async function to <T, E = Error> (promise: any): Promise<[T, E]> {
|
||||
return [null as T, e]
|
||||
}
|
||||
}
|
||||
|
||||
export type Partial<T> = { [P in keyof T]?: T[P] }
|
||||
|
@ -1,36 +0,0 @@
|
||||
const sectionExpr = /^\[(.*)\]/
|
||||
const lineBreak = /\r?\n/g
|
||||
|
||||
const isSectionLine = (line: string) => sectionExpr.test(line)
|
||||
const formatSection = (text: string) =>
|
||||
text.split(lineBreak)
|
||||
.map(t => t.trim())
|
||||
.filter(t => t && t[0] !== ';')
|
||||
.map(t => t.split('=', 2))
|
||||
.filter(pair => pair.length === 2)
|
||||
.reduce((map, [key, value]) => map.set(key.trim(), value.trim()), new Map<string, string>())
|
||||
|
||||
const iniParser = (text = '') => {
|
||||
const section = new Map<string, string>()
|
||||
if (text.length === 0) return
|
||||
const lines = text.split(lineBreak)
|
||||
let content: string[] = []
|
||||
let sectionName = ''
|
||||
for (const line of lines) {
|
||||
if (isSectionLine(line)) {
|
||||
if (sectionName !== '') {
|
||||
section.set(sectionName, content.join('\n'))
|
||||
}
|
||||
content = []
|
||||
const match = line.match(sectionExpr)
|
||||
sectionName = match && match[1]
|
||||
} else {
|
||||
content.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
if (sectionName !== '') {
|
||||
section.set(sectionName, content.join('\n'))
|
||||
}
|
||||
return section
|
||||
}
|
@ -25,7 +25,7 @@ export interface JsBridgeAPI {
|
||||
/**
|
||||
* Call a native handle
|
||||
*/
|
||||
callHandler: (handleName: string, data?: any, responseCallback?: (responseData: any) => void) => void
|
||||
callHandler: <T>(handleName: string, data?: any, responseCallback?: (responseData: T) => void) => void
|
||||
|
||||
/**
|
||||
* Who knows
|
||||
@ -115,8 +115,8 @@ export class JsBridge {
|
||||
setTimeout(() => document.documentElement.removeChild(WVJBIframe), 0)
|
||||
}
|
||||
|
||||
public callHandler (handleName: string, data?: any) {
|
||||
return new Promise(resolve => {
|
||||
public callHandler<T> (handleName: string, data?: any) {
|
||||
return new Promise<T>((resolve) => {
|
||||
this.instance.callHandler(
|
||||
handleName,
|
||||
data || undefined,
|
||||
@ -130,11 +130,11 @@ export class JsBridge {
|
||||
}
|
||||
|
||||
public readConfigString () {
|
||||
return this.callHandler('readConfigString')
|
||||
return this.callHandler<string>('readConfigString')
|
||||
}
|
||||
|
||||
public getPasteboard () {
|
||||
return this.callHandler('getPasteboard')
|
||||
return this.callHandler<string>('getPasteboard')
|
||||
}
|
||||
|
||||
public setPasteboard (data: string) {
|
||||
|
99
src/lib/request.ts
Normal file
99
src/lib/request.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
import { Partial, getLocalStorageItem } from '@lib/helper'
|
||||
import { isClashX } from '@lib/jsBridge'
|
||||
import { rootStores } from '@lib/createStore'
|
||||
|
||||
let instance: Request
|
||||
|
||||
export interface Config {
|
||||
port: number
|
||||
'socket-port': number
|
||||
'redir-port': number
|
||||
'allow-lan': boolean
|
||||
mode: string
|
||||
'log-level': string
|
||||
}
|
||||
|
||||
export interface Rules {
|
||||
rules: { name: string, payload: string }[]
|
||||
}
|
||||
|
||||
export interface Proxies {
|
||||
proxies: {
|
||||
[key: string]: Proxy
|
||||
}
|
||||
}
|
||||
|
||||
export interface Proxy {
|
||||
type: 'Direct' | 'Selector' | 'Reject' | 'URLTest' | 'Shadowsocks' | 'Vmess' | 'Socks' | 'Fallback'
|
||||
now?: string
|
||||
all?: string[]
|
||||
}
|
||||
|
||||
export class Request {
|
||||
protected instance: AxiosInstance
|
||||
|
||||
constructor (host: string, secret?: string) {
|
||||
this.instance = axios.create({
|
||||
baseURL: host,
|
||||
headers: secret ? { Authorization: `Bearer ${secret}` } : {}
|
||||
})
|
||||
}
|
||||
|
||||
getConfig () {
|
||||
return this.instance.get<Config>('configs')
|
||||
}
|
||||
|
||||
updateConfig (config: Partial<Config>) {
|
||||
return this.instance.put<void>('configs', config)
|
||||
}
|
||||
|
||||
getRules () {
|
||||
return this.instance.get<Rules>('rules')
|
||||
}
|
||||
|
||||
updateRules () {
|
||||
return this.instance.put<void>('rules')
|
||||
}
|
||||
|
||||
getProxies () {
|
||||
return this.instance.get<Proxies>('proxies')
|
||||
}
|
||||
|
||||
getProxy (name: string) {
|
||||
return this.instance.get<Proxy>('proxies/:name', { params: { name } })
|
||||
}
|
||||
|
||||
getProxyDelay (name: string) {
|
||||
return this.instance.get<{ delay: number }>('proxies/:name/delay', { params: { name } })
|
||||
}
|
||||
|
||||
changeProxySelected (name: string, select: string) {
|
||||
return this.instance.get<void>('proxies/:name', { params: { name }, data: { name: select } })
|
||||
}
|
||||
}
|
||||
|
||||
export async function Instance () {
|
||||
if (instance) {
|
||||
return instance
|
||||
}
|
||||
|
||||
if (isClashX()) {
|
||||
await rootStores.config.fetchAndParseConfig()
|
||||
const general = rootStores.config.config.general
|
||||
instance = new Request(
|
||||
`http://${general.externalControllerAddr}:${general.externalControllerPort}`,
|
||||
general.secret
|
||||
)
|
||||
return instance
|
||||
}
|
||||
|
||||
const hostname = getLocalStorageItem('externalControllerAddr', '')
|
||||
const port = getLocalStorageItem('externalControllerPort', '')
|
||||
const secret = getLocalStorageItem('secret', '')
|
||||
if (!hostname || !port) {
|
||||
throw new Error('can\'t get hostname or port')
|
||||
}
|
||||
instance = new Request(`http://${hostname}:${port}`, secret)
|
||||
return instance
|
||||
}
|
@ -19,11 +19,40 @@ export interface Config {
|
||||
*/
|
||||
socksPort?: number
|
||||
|
||||
/**
|
||||
* redir proxy port
|
||||
*/
|
||||
redirPort?: number
|
||||
|
||||
/**
|
||||
* proxy is allow lan
|
||||
*/
|
||||
allowLan?: boolean
|
||||
|
||||
/**
|
||||
* controller port
|
||||
*/
|
||||
externalController: number
|
||||
externalControllerPort?: number
|
||||
|
||||
/**
|
||||
* controller address
|
||||
*/
|
||||
externalControllerAddr?: string
|
||||
|
||||
/**
|
||||
* controller secret
|
||||
*/
|
||||
secret?: string
|
||||
|
||||
/**
|
||||
* clash proxy mode
|
||||
*/
|
||||
mode?: string
|
||||
|
||||
/**
|
||||
* clash tty log level
|
||||
*/
|
||||
logLevel?: string
|
||||
}
|
||||
|
||||
proxy?: Proxy[]
|
||||
|
@ -30,10 +30,14 @@ export interface ShadowsocksProxy {
|
||||
|
||||
port?: number
|
||||
|
||||
cipter?: string
|
||||
cipher?: string
|
||||
|
||||
password?: string
|
||||
|
||||
obfs?: string
|
||||
|
||||
'obfs-host'?: string
|
||||
|
||||
}
|
||||
|
||||
export interface VmessProxy {
|
||||
@ -48,7 +52,7 @@ export interface VmessProxy {
|
||||
|
||||
alterid?: number
|
||||
|
||||
cipter?: string
|
||||
cipher?: string
|
||||
|
||||
tls?: boolean
|
||||
|
||||
@ -75,7 +79,7 @@ export interface ProxyGroup {
|
||||
* configs of proxy server
|
||||
* now support select and url-test
|
||||
*/
|
||||
config?: SelectProxyGroup | UrlTestProxyGroup
|
||||
config?: SelectProxyGroup | UrlTestProxyGroup | FallbackProxyGroup
|
||||
|
||||
}
|
||||
|
||||
@ -87,6 +91,18 @@ export interface SelectProxyGroup {
|
||||
|
||||
}
|
||||
|
||||
export interface FallbackProxyGroup {
|
||||
|
||||
type?: 'fallback'
|
||||
|
||||
proxies?: string[] // proxy names
|
||||
|
||||
url?: string
|
||||
|
||||
interval?: number // second
|
||||
|
||||
}
|
||||
|
||||
export interface UrlTestProxyGroup {
|
||||
|
||||
type?: 'url-test'
|
||||
|
@ -2,9 +2,9 @@ export interface Rule {
|
||||
|
||||
type?: RuleType
|
||||
|
||||
value?: string
|
||||
payload?: string
|
||||
|
||||
use?: string // proxy or proxy group name
|
||||
proxy?: string // proxy or proxy group name
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { observable, action, runInAction } from 'mobx'
|
||||
import { parse } from 'ini'
|
||||
import { Config } from '@models'
|
||||
import * as yaml from 'yaml'
|
||||
import * as Models from '@models'
|
||||
import { jsBridge } from '@lib/jsBridge'
|
||||
|
||||
export class ConfigStore {
|
||||
|
||||
@observable
|
||||
config: Config = {}
|
||||
config: Models.Config = {}
|
||||
|
||||
@observable
|
||||
public state: 'pending' | 'ok' | 'error' = 'pending'
|
||||
@ -26,11 +26,37 @@ export class ConfigStore {
|
||||
}
|
||||
|
||||
// otherwise parse ini
|
||||
const config = parse(rawConfig)
|
||||
const config = yaml.parse(rawConfig)
|
||||
|
||||
console.log(config)
|
||||
const externalController = config['external-controller'] as string || ''
|
||||
const host = externalController.split(':')
|
||||
|
||||
this.config = config
|
||||
const proxies = config.Proxy as any[] || []
|
||||
const proxy: Models.Proxy[] = proxies
|
||||
.filter(p => ['vmess', 'ss', 'socks5'].includes(p.type))
|
||||
.map(p => ({ name: p.name, config: p }))
|
||||
|
||||
const proxyGroups = config['Proxy Group'] as any[] || []
|
||||
const proxyGroup: Models.ProxyGroup[] = proxyGroups
|
||||
.filter(p => ['url-test', 'select', 'fallback'].includes(p.type))
|
||||
.map(p => ({ name: p.name, config: p }))
|
||||
|
||||
this.config = {
|
||||
general: {
|
||||
port: config.port || 0,
|
||||
socksPort: config['socks-port'] || 0,
|
||||
redirPort: config['redir-port'] || 0,
|
||||
allowLan: config['allow-lan'] || false,
|
||||
externalControllerAddr: host[0] || '',
|
||||
externalControllerPort: parseInt(host[1], 10) || 0,
|
||||
secret: config.secret || '',
|
||||
logLevel: config['log-level'] || 'info',
|
||||
mode: config.mode || 'Rule'
|
||||
},
|
||||
proxy,
|
||||
proxyGroup,
|
||||
rules: config['Rule'] || []
|
||||
}
|
||||
this.state = 'ok'
|
||||
})
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"lib": ["es5", "es6", "dom"],
|
||||
"lib": ["es5", "es6", "dom", "es2017"],
|
||||
"experimentalDecorators": true,
|
||||
"downlevelIteration": true,
|
||||
"baseUrl": ".",
|
||||
|
Loading…
x
Reference in New Issue
Block a user