mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Feat: support web
This commit is contained in:
parent
6ff226ce3f
commit
91f3c9e348
@ -12,6 +12,7 @@ import Logs from '@containers/Logs'
|
||||
import Rules from '@containers/Rules'
|
||||
import Settings from '@containers/Settings'
|
||||
import SlideBar from '@containers/Sidebar'
|
||||
import ExternalControllerModal from '@containers/ExternalControllerDrawer'
|
||||
import { getLogsStreamReader } from '@lib/request'
|
||||
|
||||
export interface AppProps extends I18nProps {
|
||||
@ -42,6 +43,7 @@ export default class App extends React.Component<AppProps, {}> {
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<ExternalControllerModal />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,48 +1,55 @@
|
||||
import * as React from 'react'
|
||||
import { translate } from 'react-i18next'
|
||||
import { inject, observer } from 'mobx-react'
|
||||
import { storeKeys } from '@lib/createStore'
|
||||
import { Modal, Input, Row, Col, Alert } from '@components'
|
||||
import { I18nProps } from '@models'
|
||||
import { BaseProps, I18nProps } from '@models'
|
||||
import './style.scss'
|
||||
|
||||
interface ExternalControllerModalProps extends I18nProps {
|
||||
show: boolean
|
||||
host: string
|
||||
port: string
|
||||
secret?: string
|
||||
onConfirm: (host: string, port: string, secret: string) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
interface ExternalControllerModalProps extends I18nProps, BaseProps {}
|
||||
|
||||
interface ExternalControllerModalState {
|
||||
host: string
|
||||
hostname: string
|
||||
port: string
|
||||
secret: string
|
||||
}
|
||||
|
||||
@inject(...storeKeys)
|
||||
@observer
|
||||
class ExternalController extends React.Component<ExternalControllerModalProps, ExternalControllerModalState> {
|
||||
|
||||
state = {
|
||||
host: this.props.host,
|
||||
port: this.props.port,
|
||||
secret: this.props.secret || ''
|
||||
hostname: '',
|
||||
port: '',
|
||||
secret: ''
|
||||
}
|
||||
|
||||
private handleOk = () => {
|
||||
const { onConfirm } = this.props
|
||||
const { host, port, secret } = this.state
|
||||
onConfirm(host, port, secret)
|
||||
const { hostname, port, secret } = this.state
|
||||
this.props.store.updateAPIInfo({ hostname, port, secret })
|
||||
}
|
||||
|
||||
private handleCancel = () => {
|
||||
this.props.store.setShowAPIModal(false)
|
||||
}
|
||||
|
||||
async componentWillMount () {
|
||||
await this.props.store.fetchAPIInfo()
|
||||
const info = this.props.store.apiInfo
|
||||
this.setState({ hostname: info.hostname, port: info.port, secret: info.secret })
|
||||
}
|
||||
|
||||
render () {
|
||||
const { show, onCancel, t } = this.props
|
||||
const { host, port, secret } = this.state
|
||||
const { t } = this.props
|
||||
const { hostname, port, secret } = this.state
|
||||
const show = this.props.store.showAPIModal
|
||||
|
||||
return (
|
||||
<Modal
|
||||
show={show}
|
||||
title={t('externalControllerSetting.title')}
|
||||
bodyClassName="external-controller"
|
||||
onClose={onCancel}
|
||||
onClose={this.handleCancel}
|
||||
onOk={this.handleOk}
|
||||
>
|
||||
<Alert type="info" inside={true}>
|
||||
@ -54,8 +61,8 @@ class ExternalController extends React.Component<ExternalControllerModalProps, E
|
||||
<Input
|
||||
align="left"
|
||||
inside={true}
|
||||
value={host}
|
||||
onChange={host => this.setState({ host })}
|
||||
value={hostname}
|
||||
onChange={hostname => this.setState({ hostname })}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@ -86,4 +93,4 @@ class ExternalController extends React.Component<ExternalControllerModalProps, E
|
||||
}
|
||||
}
|
||||
|
||||
export const ExternalControllerModal = translate(['Settings'])(ExternalController)
|
||||
export default translate(['Settings'])(ExternalController)
|
@ -1 +0,0 @@
|
||||
export * from './ExternalControllerDrawer'
|
@ -3,10 +3,9 @@ import { translate } from 'react-i18next'
|
||||
import { changeLanguage } from 'i18next'
|
||||
import { inject, observer } from 'mobx-react'
|
||||
import { Header, Card, Row, Col, Switch, ButtonSelect, ButtonSelectOptions, Input, Icon } from '@components'
|
||||
import { ExternalControllerModal } from './components'
|
||||
import { I18nProps, BaseRouterProps } from '@models'
|
||||
import { updateConfig } from '@lib/request'
|
||||
import { setLocalStorageItem, to } from '@lib/helper'
|
||||
import { to } from '@lib/helper'
|
||||
import { rootStores, storeKeys } from '@lib/createStore'
|
||||
import './style.scss'
|
||||
import { isClashX, jsBridge } from '@lib/jsBridge'
|
||||
@ -19,10 +18,6 @@ class Settings extends React.Component<SettingProps, {}> {
|
||||
state = {
|
||||
socks5ProxyPort: 7891,
|
||||
httpProxyPort: 7890,
|
||||
externalControllerHost: '127.0.0.1',
|
||||
externalControllerPort: '8080',
|
||||
externalControllerSecret: '',
|
||||
showEditDrawer: false,
|
||||
isClashX: false
|
||||
}
|
||||
|
||||
@ -32,18 +27,6 @@ class Settings extends React.Component<SettingProps, {}> {
|
||||
changeLanguage(language)
|
||||
}
|
||||
|
||||
handleExternalControllerChange = (host: string, port: string, secret: string) => {
|
||||
setLocalStorageItem('externalControllerAddr', host)
|
||||
setLocalStorageItem('externalControllerPort', port)
|
||||
setLocalStorageItem('secret', secret)
|
||||
this.setState({
|
||||
showEditDrawer: false,
|
||||
externalControllerHost: host,
|
||||
externalControllerPort: port,
|
||||
externalControllerSecret: secret
|
||||
})
|
||||
}
|
||||
|
||||
handleProxyModeChange = async (mode: string) => {
|
||||
const [, err] = await to(updateConfig({ mode }))
|
||||
if (!err) {
|
||||
@ -84,16 +67,12 @@ class Settings extends React.Component<SettingProps, {}> {
|
||||
this.setState({ setAsSystemProxy: state })
|
||||
}
|
||||
|
||||
async componentWillMount () {
|
||||
async componentDidMount () {
|
||||
await rootStores.store.fetchData()
|
||||
if (isClashX()) {
|
||||
await rootStores.store.fetchClashXData()
|
||||
const apiInfo = await jsBridge.getAPIInfo()
|
||||
this.setState({
|
||||
isClashX: true,
|
||||
externalControllerHost: apiInfo.host,
|
||||
externalControllerPort: apiInfo.port,
|
||||
externalControllerSecret: apiInfo.secret
|
||||
isClashX: true
|
||||
})
|
||||
}
|
||||
|
||||
@ -108,14 +87,15 @@ class Settings extends React.Component<SettingProps, {}> {
|
||||
const { t, lng, store } = this.props
|
||||
const {
|
||||
isClashX,
|
||||
externalControllerHost,
|
||||
externalControllerPort,
|
||||
externalControllerSecret,
|
||||
showEditDrawer,
|
||||
socks5ProxyPort,
|
||||
httpProxyPort
|
||||
} = this.state
|
||||
|
||||
const {
|
||||
hostname: externalControllerHost,
|
||||
port: externalControllerPort
|
||||
} = store.apiInfo
|
||||
|
||||
const { allowLan, mode } = store.data.general
|
||||
const {
|
||||
startAtLogin,
|
||||
@ -208,7 +188,7 @@ class Settings extends React.Component<SettingProps, {}> {
|
||||
</Col>
|
||||
<Col className="external-controller" span={6} offset={1}>
|
||||
<span>{`${externalControllerHost}:${externalControllerPort}`}</span>
|
||||
<span className="modify-btn" onClick={() => this.setState({ showEditDrawer: true })}>
|
||||
<span className="modify-btn" onClick={() => this.props.store.setShowAPIModal(true)}>
|
||||
修改
|
||||
</span>
|
||||
</Col>
|
||||
@ -222,15 +202,6 @@ class Settings extends React.Component<SettingProps, {}> {
|
||||
<p className="version-info">{t('versionString', { version: 'unknown' })}</p>
|
||||
<span className="check-update-btn">{t('checkUpdate')}</span>
|
||||
</Card>
|
||||
|
||||
<ExternalControllerModal
|
||||
show={showEditDrawer}
|
||||
host={externalControllerHost}
|
||||
port={externalControllerPort}
|
||||
secret={externalControllerSecret}
|
||||
onConfirm={this.handleExternalControllerChange}
|
||||
onCancel={() => this.setState({ showEditDrawer: false })}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
import { Partial, getLocalStorageItem } from '@lib/helper'
|
||||
import { isClashX } from '@lib/jsBridge'
|
||||
import { isClashX, jsBridge } from '@lib/jsBridge'
|
||||
import { rootStores } from '@lib/createStore'
|
||||
import { StreamReader } from './streamer'
|
||||
|
||||
@ -105,18 +105,27 @@ export async function getInstance () {
|
||||
headers: secret ? { Authorization: `Bearer ${secret}` } : {}
|
||||
})
|
||||
|
||||
instance.interceptors.response.use(
|
||||
resp => resp,
|
||||
err => {
|
||||
if (!err.response || err.response.status === 401) {
|
||||
rootStores.store.setShowAPIModal(true)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
export async function getExternalControllerConfig () {
|
||||
if (isClashX()) {
|
||||
await rootStores.store.fetchAndParseConfig()
|
||||
const general = rootStores.store.config.general
|
||||
const info = await jsBridge.getAPIInfo()
|
||||
|
||||
return {
|
||||
hostname: general.externalControllerAddr,
|
||||
port: general.externalControllerPort,
|
||||
secret: general.secret
|
||||
hostname: info.host,
|
||||
port: info.port,
|
||||
secret: info.secret
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,6 +147,7 @@ export async function getLogsStreamReader () {
|
||||
const externalController = await getExternalControllerConfig()
|
||||
const { data: config } = await getConfig()
|
||||
const logUrl = `http://${externalController.hostname}:${externalController.port}/logs?level=${config['log-level']}`
|
||||
logsStreamReader = new StreamReader({ url: logUrl, bufferLength: 200 })
|
||||
const auth = externalController.secret ? { Authorization: `Bearer ${externalController.secret}` } : {}
|
||||
logsStreamReader = new StreamReader({ url: logUrl, bufferLength: 200, headers: auth })
|
||||
return logsStreamReader
|
||||
}
|
||||
|
@ -69,6 +69,12 @@ export interface ClashXData {
|
||||
systemProxy: boolean
|
||||
}
|
||||
|
||||
export interface APIInfo {
|
||||
hostname: string
|
||||
port: string
|
||||
secret?: string
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
|
||||
general?: {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { observable, action, runInAction } from 'mobx'
|
||||
import * as yaml from 'yaml'
|
||||
import * as Models from '@models'
|
||||
import { jsBridge } from '@lib/jsBridge'
|
||||
import { jsBridge, isClashX } from '@lib/jsBridge'
|
||||
import * as API from '@lib/request'
|
||||
import { getLocalStorageItem, partition } from '@lib/helper'
|
||||
import { getLocalStorageItem, setLocalStorageItem, partition } from '@lib/helper'
|
||||
|
||||
export class ConfigStore {
|
||||
|
||||
@ -22,12 +22,38 @@ export class ConfigStore {
|
||||
rules: []
|
||||
}
|
||||
|
||||
@observable
|
||||
apiInfo: Models.APIInfo = {
|
||||
hostname: '127.0.0.1',
|
||||
port: '8080',
|
||||
secret: ''
|
||||
}
|
||||
|
||||
@observable
|
||||
showAPIModal = false
|
||||
|
||||
@observable
|
||||
clashxData: Models.ClashXData = {
|
||||
startAtLogin: false,
|
||||
systemProxy: false
|
||||
}
|
||||
|
||||
@action
|
||||
async fetchAPIInfo () {
|
||||
if (isClashX()) {
|
||||
const apiInfo = await jsBridge.getAPIInfo()
|
||||
runInAction(() => {
|
||||
this.apiInfo = { hostname: apiInfo.host, port: apiInfo.port, secret: apiInfo.secret }
|
||||
})
|
||||
return
|
||||
}
|
||||
const info = await API.getExternalControllerConfig()
|
||||
|
||||
runInAction(() => {
|
||||
this.apiInfo = { ...info }
|
||||
})
|
||||
}
|
||||
|
||||
@action
|
||||
async fetchData () {
|
||||
const [{ data: general }, rawProxies, rules] = await Promise.all([API.getConfig(), API.getProxies(), API.getRules()])
|
||||
@ -156,6 +182,22 @@ export class ConfigStore {
|
||||
jsBridge.writeConfigWithString(data)
|
||||
}
|
||||
|
||||
@action
|
||||
async updateAPIInfo (info: Models.APIInfo) {
|
||||
const { hostname, port, secret } = info
|
||||
setLocalStorageItem('externalControllerAddr', hostname)
|
||||
setLocalStorageItem('externalControllerPort', port)
|
||||
setLocalStorageItem('secret', secret)
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
@action
|
||||
setShowAPIModal (visible: boolean) {
|
||||
runInAction(() => {
|
||||
this.showAPIModal = visible
|
||||
})
|
||||
}
|
||||
|
||||
@action
|
||||
async modifyProxyByIndexAndSave (index: number, config: Models.Proxy) {
|
||||
const { proxy } = this.config
|
||||
|
Loading…
x
Reference in New Issue
Block a user