mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Add: proxy modify
This commit is contained in:
parent
49d256a51f
commit
aec5b72987
@ -9,6 +9,7 @@ interface InputProps extends BaseComponentProps {
|
||||
align?: 'left' | 'center' | 'right'
|
||||
inside?: boolean
|
||||
autoFocus?: boolean
|
||||
type?: string
|
||||
onChange?: (value: string, event?: React.ChangeEvent<HTMLInputElement>) => void
|
||||
onBlur?: (event?: React.FocusEvent<HTMLInputElement>) => void
|
||||
}
|
||||
@ -20,6 +21,7 @@ export class Input extends React.Component<InputProps, {}> {
|
||||
align: 'center',
|
||||
inside: false,
|
||||
autoFocus: false,
|
||||
type: 'text',
|
||||
onChange: () => {},
|
||||
onBlur: () => {}
|
||||
}
|
||||
@ -32,6 +34,7 @@ export class Input extends React.Component<InputProps, {}> {
|
||||
align,
|
||||
inside,
|
||||
autoFocus,
|
||||
type,
|
||||
onChange,
|
||||
onBlur
|
||||
} = this.props
|
||||
@ -42,6 +45,7 @@ export class Input extends React.Component<InputProps, {}> {
|
||||
style={style}
|
||||
value={value}
|
||||
autoFocus={autoFocus}
|
||||
type={type}
|
||||
onChange={event => onChange(event.target.value, event)}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
|
@ -49,6 +49,8 @@ export class Modal extends React.Component<ModalProps, {}> {
|
||||
|
||||
$modal = React.createRef<HTMLDivElement>()
|
||||
|
||||
$mask = React.createRef<HTMLDivElement>()
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
@ -64,9 +66,8 @@ export class Modal extends React.Component<ModalProps, {}> {
|
||||
|
||||
private handleMaskClick = (e) => {
|
||||
const { onClose } = this.props
|
||||
const el = this.$modal.current
|
||||
|
||||
if (el && !el.contains(e.target)) {
|
||||
if (e.target === this.$mask) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
@ -76,6 +77,7 @@ export class Modal extends React.Component<ModalProps, {}> {
|
||||
const modal = (
|
||||
<div
|
||||
className={classnames('modal-mask', { 'modal-show': show })}
|
||||
ref={this.$mask}
|
||||
onClick={this.handleMaskClick}
|
||||
>
|
||||
<div
|
||||
|
@ -44,12 +44,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
console.log('update')
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
|
||||
document.addEventListener('click', this.handleGlobalClick, true)
|
||||
this.setState({ dropdownListStyles: this.calculateAttachmentPosition() })
|
||||
}
|
||||
@ -60,6 +55,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
}
|
||||
document.removeEventListener('click', this.handleGlobalClick, true)
|
||||
}
|
||||
|
||||
shouldComponentUpdate (nextProps, nextState) {
|
||||
if (nextProps.value === this.props.value && nextState.showDropDownList === this.state.showDropDownList) {
|
||||
return false
|
||||
@ -96,7 +92,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
return {
|
||||
top: Math.floor(targetRectInfo.top) - 10,
|
||||
left: Math.floor(targetRectInfo.left) - 10,
|
||||
width: Math.floor(targetRectInfo.width)
|
||||
width: Math.floor(targetRectInfo.width) + 10
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +159,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||
ref={this.$target}
|
||||
onClick={this.handleShowDropList}
|
||||
>
|
||||
{matchChild.props.children}
|
||||
{matchChild && matchChild.props && matchChild.props.children}
|
||||
<Icon type="triangle-down" />
|
||||
</div>
|
||||
{hasCreateDropList && createPortal(dropDownList, this.$container)}
|
||||
|
@ -1,7 +1,6 @@
|
||||
@import '~@styles/variables';
|
||||
|
||||
.select {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
|
@ -0,0 +1,129 @@
|
||||
import * as React from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { Row, Col, Input, Icon, Select, Option } from '@components'
|
||||
import { noop } from '@lib/helper'
|
||||
|
||||
// type selector
|
||||
export function ProxyTypeSelector ({ types, label, value, onSelect = noop }: {
|
||||
types: { [key: string]: string },
|
||||
label: string,
|
||||
value: string,
|
||||
onSelect?: (type: string) => void
|
||||
}) {
|
||||
return (
|
||||
<Row gutter={24} className="proxy-editor-row">
|
||||
<Col span={6} className="proxy-editor-label">{label}</Col>
|
||||
<Col span={18}>
|
||||
<Select value={value} onSelect={onSelect}>
|
||||
{
|
||||
Object.keys(types)
|
||||
.map(typeName => {
|
||||
const type = types[typeName]
|
||||
return (
|
||||
<Option value={type} key={type}>{typeName}</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
// color selector
|
||||
export function ProxyColorSelector ({ colors, value, onSelect = noop }: {
|
||||
colors: string[],
|
||||
value: string,
|
||||
onSelect?: (color: string) => void
|
||||
}) {
|
||||
return (
|
||||
<Row gutter={24} style={{ padding: '12px 0' }}>
|
||||
<div className="proxy-editor-color-selector">
|
||||
{
|
||||
colors.map(color => (
|
||||
<span
|
||||
className={classnames('color-item', {
|
||||
'color-item-active': value === color
|
||||
})}
|
||||
key={color}
|
||||
style={{ background: color }}
|
||||
onClick={() => onSelect(color)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
// input form
|
||||
export function ProxyInputForm ({ label, value, onChange = noop }: {
|
||||
label: string,
|
||||
value: string,
|
||||
onChange?: (value: string) => void
|
||||
}) {
|
||||
return (
|
||||
<Row gutter={24} className="proxy-editor-row">
|
||||
<Col span={6} className="proxy-editor-label">{label}</Col>
|
||||
<Col span={18}>
|
||||
<Input value={value} onChange={onChange} align="left"/>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
// password form
|
||||
export class ProxyPasswordForm extends React.Component<{
|
||||
label: string,
|
||||
value: string,
|
||||
onChange?: (value: string) => void
|
||||
}, { showPassword: boolean }> {
|
||||
|
||||
state = {
|
||||
showPassword: false
|
||||
}
|
||||
|
||||
render () {
|
||||
const { label, value, onChange } = this.props
|
||||
const { showPassword } = this.state
|
||||
const type = showPassword ? 'text' : 'password'
|
||||
|
||||
return (
|
||||
<Row gutter={24} className="proxy-editor-row">
|
||||
<Col span={6} className="proxy-editor-label">{label}</Col>
|
||||
<Col span={18} className="proxy-editor-value">
|
||||
<Input type={type} value={value} onChange={onChange} align="left"/>
|
||||
<Icon
|
||||
className="proxy-editor-passsword-icon"
|
||||
type={showPassword ? 'hide' : 'show'}
|
||||
size={20}
|
||||
onClick={() => this.setState({ showPassword: !showPassword })}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// cipher selector
|
||||
export function ProxyCipherSelector ({ ciphers, label, value, onSelect = noop }: {
|
||||
ciphers: string[],
|
||||
label: string,
|
||||
value: string,
|
||||
onSelect?: (type: string) => void
|
||||
}) {
|
||||
return (
|
||||
<Row gutter={24} className="proxy-editor-row">
|
||||
<Col span={6} className="proxy-editor-label">{label}</Col>
|
||||
<Col span={18}>
|
||||
<Select value={value} onSelect={onSelect}>
|
||||
{
|
||||
ciphers.map(cipher => (
|
||||
<Option value={cipher} key={cipher}>{cipher}</Option>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
@ -1,11 +1,27 @@
|
||||
import * as React from 'react'
|
||||
import { translate } from 'react-i18next'
|
||||
import classnames from 'classnames'
|
||||
import { BaseComponentProps, Proxy as IProxy, I18nProps, TagColors } from '@models'
|
||||
import { Modal, Row, Col } from '@components'
|
||||
import { Modal } from '@components'
|
||||
import { getLocalStorageItem, setLocalStorageItem } from '@lib/helper'
|
||||
import './style.scss'
|
||||
|
||||
import {
|
||||
BaseComponentProps,
|
||||
Proxy as IProxy,
|
||||
SsProxyConfigList, VmessProxyConfigList, Socks5ProxyConfigList,
|
||||
I18nProps,
|
||||
TagColors,
|
||||
ProxyType,
|
||||
SsCipher, VmessCipher, pickCipherWithAlias
|
||||
} from '@models'
|
||||
|
||||
import {
|
||||
ProxyInputForm,
|
||||
ProxyColorSelector,
|
||||
ProxyTypeSelector,
|
||||
ProxyPasswordForm,
|
||||
ProxyCipherSelector
|
||||
} from './FormItems'
|
||||
|
||||
interface ModifyProxyDialogProps extends BaseComponentProps, I18nProps {
|
||||
config: IProxy
|
||||
onOk?: (config: IProxy) => void
|
||||
@ -40,9 +56,151 @@ class RawDialog extends React.Component<ModifyProxyDialogProps, ModifyProxyDialo
|
||||
onOk(config)
|
||||
}
|
||||
|
||||
handleConfigChange = (key: string, value: any) => {
|
||||
console.log(key, value)
|
||||
const { config } = this.state
|
||||
this.setState({ config: { ...config, [key]: value } })
|
||||
}
|
||||
|
||||
getCipherFromType (type) {
|
||||
switch (type) {
|
||||
case 'ss':
|
||||
return SsCipher
|
||||
case 'vmess':
|
||||
return VmessCipher
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
getConfigListFromType (type) {
|
||||
switch (type) {
|
||||
case 'ss':
|
||||
return SsProxyConfigList
|
||||
case 'vmess':
|
||||
return VmessProxyConfigList
|
||||
case 'socks5':
|
||||
return Socks5ProxyConfigList
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
renderFormItem (key) {
|
||||
const { t } = this.props
|
||||
const { config } = this.state
|
||||
|
||||
switch (key) {
|
||||
case 'type':
|
||||
return (
|
||||
<ProxyTypeSelector
|
||||
key={key}
|
||||
types={ProxyType}
|
||||
label={t('editDialog.type')}
|
||||
value={config.type}
|
||||
onSelect={value => this.handleConfigChange('type', value)}
|
||||
/>
|
||||
)
|
||||
case 'name':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
key={key}
|
||||
label={t('editDialog.name')}
|
||||
value={config.name}
|
||||
onChange={value => this.handleConfigChange('name', value)}
|
||||
/>
|
||||
)
|
||||
case 'server':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
key={key}
|
||||
label={t('editDialog.server')}
|
||||
value={config.server}
|
||||
onChange={value => this.handleConfigChange('server', value)}
|
||||
/>
|
||||
)
|
||||
case 'port':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
key={key}
|
||||
label={t('editDialog.port')}
|
||||
value={config.port ? config.port.toString() : ''}
|
||||
onChange={value => this.handleConfigChange('port', +value)}
|
||||
/>
|
||||
)
|
||||
case 'password':
|
||||
return (
|
||||
<ProxyPasswordForm
|
||||
key={key}
|
||||
label={t('editDialog.password')}
|
||||
value={config.password}
|
||||
onChange={value => this.handleConfigChange('password', value)}
|
||||
/>
|
||||
)
|
||||
case 'cipher':
|
||||
return (
|
||||
<ProxyCipherSelector
|
||||
key={key}
|
||||
ciphers={this.getCipherFromType(config.type)}
|
||||
label={t('editDialog.cipher')}
|
||||
value={pickCipherWithAlias(config.cipher)}
|
||||
onSelect={value => this.handleConfigChange('cipher', value)}
|
||||
/>
|
||||
)
|
||||
case 'obfs':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
label={t('editDialog.obfs')}
|
||||
value={config.obfs}
|
||||
onChange={value => this.handleConfigChange('obfs', value)}
|
||||
/>
|
||||
)
|
||||
case 'obfs-host':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
key={key}
|
||||
label={t('editDialog.obfs-host')}
|
||||
value={config['obfs-host']}
|
||||
onChange={value => this.handleConfigChange('obfs-host', value)}
|
||||
/>
|
||||
)
|
||||
case 'uuid':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
key={key}
|
||||
label={t('editDialog.uuid')}
|
||||
value={config.uuid}
|
||||
onChange={value => this.handleConfigChange('uuid', value)}
|
||||
/>
|
||||
)
|
||||
case 'alterid':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
key={key}
|
||||
label={t('editDialog.alterid')}
|
||||
value={config.alterid ? config.alterid.toString() : ''}
|
||||
onChange={value => this.handleConfigChange('alterid', +value)}
|
||||
/>
|
||||
)
|
||||
case 'tls':
|
||||
return (
|
||||
<ProxyInputForm
|
||||
key={key}
|
||||
label={t('editDialog.tls')}
|
||||
value={config.tls ? config.tls.toString() : ''}
|
||||
onChange={value => this.handleConfigChange('tls', !!value)}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { onCancel, t } = this.props
|
||||
const { currentColor } = this.state
|
||||
const { currentColor, config } = this.state
|
||||
const { type } = config
|
||||
const configList = this.getConfigListFromType(type)
|
||||
|
||||
return <Modal
|
||||
className="proxy-editor"
|
||||
@ -50,25 +208,14 @@ class RawDialog extends React.Component<ModifyProxyDialogProps, ModifyProxyDialo
|
||||
onOk={this.handleOk}
|
||||
onClose={onCancel}
|
||||
>
|
||||
<Row gutter={24} style={{ padding: '12px 0' }}>
|
||||
<Col span={6} style={{ paddingLeft: 0 }}>{t('editDialog.color')}</Col>
|
||||
<Col span={18}>
|
||||
<div className="proxy-editor-color-selector">
|
||||
{
|
||||
TagColors.map(color => (
|
||||
<span
|
||||
className={classnames('color-item', {
|
||||
'color-item-active': currentColor === color
|
||||
})}
|
||||
key={color}
|
||||
style={{ background: color }}
|
||||
onClick={() => this.setState({ currentColor: color })}
|
||||
<ProxyColorSelector
|
||||
colors={TagColors}
|
||||
value={currentColor}
|
||||
onSelect={color => this.setState({ currentColor: color })}
|
||||
/>
|
||||
))
|
||||
{
|
||||
configList.map(c => this.renderFormItem(c))
|
||||
}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,35 @@
|
||||
@import '~@styles/variables';
|
||||
|
||||
.proxy-editor {
|
||||
.proxy-editor-row {
|
||||
padding: 5px 0;
|
||||
|
||||
.proxy-editor-label {
|
||||
padding-left: 0;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.proxy-editor-value {
|
||||
position: relative;
|
||||
|
||||
.proxy-editor-passsword-icon {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 4px;
|
||||
cursor: pointer;
|
||||
color: $color-primary-darken;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.proxy-editor-color-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.color-item {
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
margin-right: 20px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
|
@ -13,6 +13,7 @@ interface ProxiesProps extends BaseRouterProps, I18nProps {}
|
||||
interface ProxiesState {
|
||||
showModifyProxyDialog: boolean
|
||||
activeConfig?: IProxy
|
||||
activeConfigIndex?: number
|
||||
}
|
||||
|
||||
@inject(...storeKeys)
|
||||
@ -21,13 +22,19 @@ class Proxies extends React.Component<ProxiesProps, ProxiesState> {
|
||||
|
||||
state = {
|
||||
showModifyProxyDialog: false,
|
||||
activeConfig: null
|
||||
activeConfig: null,
|
||||
activeConfigIndex: -1
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.props.config.fetchAndParseConfig()
|
||||
}
|
||||
|
||||
handleConfigApply = async (config: IProxy) => {
|
||||
await this.props.config.modifyProxyByIndexAndSave(this.state.activeConfigIndex, config)
|
||||
this.setState({ showModifyProxyDialog: false, activeConfig: null })
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t, config } = this.props
|
||||
const { showModifyProxyDialog, activeConfig } = this.state
|
||||
@ -46,7 +53,8 @@ class Proxies extends React.Component<ProxiesProps, ProxiesState> {
|
||||
<li key={index}>
|
||||
<Proxy config={p} onEdit={() => this.setState({
|
||||
showModifyProxyDialog: true,
|
||||
activeConfig: p
|
||||
activeConfig: p,
|
||||
activeConfigIndex: index
|
||||
})} />
|
||||
</li>
|
||||
))
|
||||
@ -61,11 +69,8 @@ class Proxies extends React.Component<ProxiesProps, ProxiesState> {
|
||||
{
|
||||
showModifyProxyDialog && <ModifyProxyDialog
|
||||
config={activeConfig}
|
||||
onOk={config => {
|
||||
console.log(config)
|
||||
this.setState({ showModifyProxyDialog: false, activeConfig: null })
|
||||
}}
|
||||
onCancel={() => this.setState({ showModifyProxyDialog: false, activeConfig: null })}
|
||||
onOk={this.handleConfigApply}
|
||||
onCancel={() => this.setState({ showModifyProxyDialog: false, activeConfig: null, activeConfigIndex: -1 })}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
@ -144,7 +144,12 @@ class Rules extends React.Component<RulesProps, RulesState> {
|
||||
isFinal
|
||||
? rule.type
|
||||
: (
|
||||
<Select key={index} value={rule.type} onSelect={type => this.handleModifyType(index, type)}>
|
||||
<Select
|
||||
key={index}
|
||||
value={rule.type}
|
||||
onSelect={type => this.handleModifyType(index, type)}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
{
|
||||
Object.keys(RuleType)
|
||||
.filter(type => type !== 'FINAL')
|
||||
|
@ -66,7 +66,7 @@ export async function getProxyDelay (name: string) {
|
||||
const req = await getInstance()
|
||||
return req.get<{ delay: number }>(`proxies/${name}/delay`, {
|
||||
params: {
|
||||
timeout: 2000,
|
||||
timeout: 20000,
|
||||
url: 'http://www.gstatic.com/generate_204'
|
||||
}
|
||||
})
|
||||
|
57
src/models/Cipher.ts
Normal file
57
src/models/Cipher.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* ss ciphers which clash supported
|
||||
* @see https://github.com/Dreamacro/go-shadowsocks2/blob/master/core/cipher.go
|
||||
*/
|
||||
export const SsCipher = [
|
||||
// AEAD ciphers
|
||||
'AEAD_AES_128_GCM',
|
||||
'AEAD_AES_192_GCM',
|
||||
'AEAD_AES_256_GCM',
|
||||
'AEAD_CHACHA20_POLY1305',
|
||||
'AEAD_XCHACHA20_POLY1305',
|
||||
|
||||
// stream ciphers
|
||||
'RC4-MD5',
|
||||
'AES-128-CTR',
|
||||
'AES-192-CTR',
|
||||
'AES-256-CTR',
|
||||
'AES-128-CFB',
|
||||
'AES-192-CFB',
|
||||
'AES-256-CFB',
|
||||
'CHACHA20',
|
||||
'CHACHA20-IETF',
|
||||
'XCHACHA20'
|
||||
]
|
||||
|
||||
/**
|
||||
* vmess ciphers which clash supported
|
||||
* @see https://github.com/Dreamacro/clash/blob/master/component/vmess/vmess.go#L34
|
||||
*/
|
||||
export const VmessCipher = [
|
||||
'auto',
|
||||
'none',
|
||||
'aes-128-gcm',
|
||||
'chacha20-poly1305'
|
||||
]
|
||||
|
||||
/**
|
||||
* pickCipherWithAlias returns a cipher of the given name.
|
||||
*/
|
||||
export function pickCipherWithAlias (c: string) {
|
||||
const cipher = c.toUpperCase()
|
||||
|
||||
switch (cipher) {
|
||||
case 'CHACHA20-IETF-POLY1305':
|
||||
return 'AEAD_CHACHA20_POLY1305'
|
||||
case 'XCHACHA20-IETF-POLY1305':
|
||||
return 'AEAD_XCHACHA20_POLY1305'
|
||||
case 'AES-128-GCM':
|
||||
return 'AEAD_AES_128_GCM'
|
||||
case 'AES-196-GCM':
|
||||
return 'AEAD_AES_196_GCM'
|
||||
case 'AES-256-GCM':
|
||||
return 'AEAD_AES_256_GCM'
|
||||
}
|
||||
|
||||
return SsCipher.find(c => c === cipher) || ''
|
||||
}
|
@ -2,14 +2,17 @@
|
||||
* proxy config interface
|
||||
*/
|
||||
|
||||
export enum ProxyType {
|
||||
Shadowsocks = 'Shadowsocks',
|
||||
Vmess = 'Vmess',
|
||||
Socks5 = 'Socks5'
|
||||
export const ProxyType = {
|
||||
Shadowsocks: 'ss',
|
||||
Vmess: 'vmess',
|
||||
Socks5: 'socks5'
|
||||
}
|
||||
|
||||
export type Proxy = ShadowsocksProxy | VmessProxy | Socks5Proxy
|
||||
export type Proxy = ShadowsocksProxy & VmessProxy & Socks5Proxy
|
||||
|
||||
export const SsProxyConfigList = [
|
||||
'name', 'type', 'server', 'port', 'cipher', 'password', 'obfs', 'obfs-host'
|
||||
]
|
||||
export interface ShadowsocksProxy {
|
||||
name?: string
|
||||
|
||||
@ -29,6 +32,9 @@ export interface ShadowsocksProxy {
|
||||
|
||||
}
|
||||
|
||||
export const VmessProxyConfigList = [
|
||||
'name', 'type', 'server', 'port', 'uuid', 'alterid', 'cipher', 'tls'
|
||||
]
|
||||
export interface VmessProxy {
|
||||
name?: string
|
||||
|
||||
@ -48,6 +54,7 @@ export interface VmessProxy {
|
||||
|
||||
}
|
||||
|
||||
export const Socks5ProxyConfigList = ['name', 'type', 'server', 'port']
|
||||
export interface Socks5Proxy {
|
||||
name?: string
|
||||
|
||||
@ -59,7 +66,7 @@ export interface Socks5Proxy {
|
||||
|
||||
}
|
||||
|
||||
export type ProxyGroup = SelectProxyGroup | UrlTestProxyGroup | FallbackProxyGroup
|
||||
export type ProxyGroup = SelectProxyGroup & UrlTestProxyGroup & FallbackProxyGroup
|
||||
|
||||
export interface SelectProxyGroup {
|
||||
name?: string
|
||||
|
@ -4,3 +4,4 @@ export * from './Proxy'
|
||||
export * from './Rule'
|
||||
export * from './I18n'
|
||||
export * from './TagColors'
|
||||
export * from './Cipher'
|
||||
|
@ -81,6 +81,7 @@ export class ConfigStore {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async updateConfig () {
|
||||
const { general, proxy, proxyGroup, rules } = this.config
|
||||
@ -103,4 +104,32 @@ export class ConfigStore {
|
||||
// console.log(data)
|
||||
jsBridge.writeConfigWithString(data)
|
||||
}
|
||||
|
||||
@action
|
||||
async modifyProxyByIndexAndSave (index: number, config: Models.Proxy) {
|
||||
const { proxy } = this.config
|
||||
const fomatedConfig: Models.Proxy = {}
|
||||
const { type } = config
|
||||
let configList: string[] = []
|
||||
|
||||
switch (type) {
|
||||
case 'ss':
|
||||
configList = Models.SsProxyConfigList
|
||||
break
|
||||
case 'vmess':
|
||||
configList = Models.VmessProxyConfigList
|
||||
break
|
||||
case 'socks5':
|
||||
configList = Models.Socks5ProxyConfigList
|
||||
break
|
||||
}
|
||||
|
||||
for (const configKey of configList) {
|
||||
fomatedConfig[configKey] = config[configKey]
|
||||
}
|
||||
|
||||
proxy[index] = fomatedConfig
|
||||
await this.updateConfig()
|
||||
await this.fetchAndParseConfig()
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "clash-iconfont";
|
||||
src: url('//at.alicdn.com/t/font_841708_7ge2gqse7qv.ttf') format('truetype');
|
||||
src: url('//at.alicdn.com/t/font_841708_e9dax11p22i.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.clash-iconfont {
|
||||
@ -39,3 +39,7 @@
|
||||
.icon-info-o::before { content: "\e60c"; }
|
||||
|
||||
.icon-setting::before { content: "\e60d"; }
|
||||
|
||||
.icon-show::before { content: "\e60e"; }
|
||||
|
||||
.icon-hide::before { content: "\e60f"; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user