Skip to content

Commit

Permalink
feat: Mixin & Script
Browse files Browse the repository at this point in the history
  • Loading branch information
Ayideyia committed Jul 6, 2024
1 parent 1ac3563 commit 8b0130f
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 17 deletions.
8 changes: 8 additions & 0 deletions frontend/src/components/Icon/CodeIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<svg viewBox="0 0 1024 1024">
<path
d="M153.770667 517.558857l200.387047-197.241905L302.86019 268.190476 48.761905 518.290286l254.439619 243.614476 50.590476-52.833524-200.021333-191.512381zM658.285714 320.316952L709.583238 268.190476l254.098286 250.09981L709.241905 761.904762l-50.590476-52.833524 200.021333-191.512381L658.285714 320.316952z m-112.981333-86.186666L393.99619 785.554286l70.534096 19.358476 151.30819-551.399619-70.534095-19.358476z"
v-bind="$attrs"
></path>
</svg>
</template>
8 changes: 8 additions & 0 deletions frontend/src/constant/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,11 @@ export const RulesConfigDefaults = (ids: string[]): ProfileType['rulesConfig'] =
'no-resolve': false
}
]

export const MixinConfigDefaults = (): ProfileType['mixinConfig'] => {
return { priority: 'mixin', config: '' }
}

export const ScriptConfigDefaults = (): ProfileType['scriptConfig'] => {
return { code: `const onGenerate = async (config) => {\n\treturn config\n}` }
}
14 changes: 12 additions & 2 deletions frontend/src/lang/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,14 +379,24 @@ export default {
tun: 'TUN',
dns: 'DNS',
groups: 'Proxy Groups',
rules: 'Rules'
rules: 'Rules',
'mixin-script': 'Mixin & Script'
},
proxies: 'Reference proxies',
use: 'Reference subscriptions',
noSubs: 'There are no available subscriptions.',
group: 'Group Details',
rule: 'Rule Details',
auto: 'This configuration is managed by subscription and will be overwritten when the subscription is updated!\nIf you want to modify this profile, please use the plugin system.'
auto: 'This configuration is managed by subscription and will be overwritten when the subscription is updated!\nIf you want to modify this profile, please use the plugin system.',
mixinSettings: {
name: 'Mixin',
priority: 'Priority',
mixin: 'Mixin',
gui: 'GUI'
},
scriptSettings: {
name: 'Script'
}
},
profiles: {
shouldStop: 'Unable to delete, this profile is in use.',
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/lang/locale/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,18 +379,29 @@ export default {
tun: 'TUN 设置',
dns: 'DNS 设置',
groups: '策略组设置',
rules: '规则设置'
rules: '规则设置',
'mixin-script': '混入和脚本'
},
proxies: '引用节点',
use: '引用订阅',
noSubs: '没有可用的订阅',
group: '策略组详情',
rule: '规则详情',
auto: '此配置由订阅接管,更新订阅时会被覆盖!\n如果你想修改此配置,请使用插件系统。'
auto: '此配置由订阅接管,更新订阅时会被覆盖!\n如果你想修改此配置,请使用插件系统。',
mixinSettings: {
name: '混入配置',
priority: '优先级',
mixin: '混入优先',
gui: 'GUI优先'
},
scriptSettings: {
name: '脚本操作'
}
},
profiles: {
shouldStop: '当前配置正在使用,无法删除',
empty: '配置列表为空,请先{action}配置。',
'mixin-script': '混入和脚本',
copytoClipboard: '生成配置到剪切板',
generateAndView: '生成配置并查看',
copy: '复制并粘贴',
Expand Down
17 changes: 16 additions & 1 deletion frontend/src/stores/profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { parse, stringify } from 'yaml'

import { Readfile, Writefile } from '@/bridge'
import { debounce, ignoredError } from '@/utils'
import { TunConfigDefaults, ProfilesFilePath, ProxyGroup } from '@/constant'
import {
TunConfigDefaults,
ProfilesFilePath,
ProxyGroup,
MixinConfigDefaults,
ScriptConfigDefaults
} from '@/constant'

export type ProfileType = {
id: string
Expand Down Expand Up @@ -120,6 +126,13 @@ export type ProfileType = {
proxy: string
'no-resolve': boolean
}[]
mixinConfig: {
priority: 'mixin' | 'gui'
config: string
}
scriptConfig: {
code: string
}
}

export const useProfilesStore = defineStore('profiles', () => {
Expand All @@ -133,6 +146,8 @@ export const useProfilesStore = defineStore('profiles', () => {
profile.dnsConfig.hosts = profile.dnsConfig.hosts ?? {}
profile.tunConfig['route-address'] =
profile.tunConfig['route-address'] ?? TunConfigDefaults()['route-address']
profile.mixinConfig = profile.mixinConfig ?? MixinConfigDefaults()
profile.scriptConfig = profile.scriptConfig ?? ScriptConfigDefaults()
})
}

Expand Down
37 changes: 35 additions & 2 deletions frontend/src/utils/generator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { parse, stringify } from 'yaml'

import { Readfile, Writefile } from '@/bridge'
import { deepClone, APP_TITLE } from '@/utils'
import { deepClone, APP_TITLE, deepAssign } from '@/utils'
import { KernelConfigFilePath, ProxyGroup } from '@/constant/kernel'
import { type ProfileType, useSubscribesStore, useRulesetsStore, usePluginsStore } from '@/stores'

Expand Down Expand Up @@ -243,6 +243,13 @@ const generateRuleProviders = async (
return providers
}

/**
Processing steps
1. Generate the config from the profile.
2. Merge the config using mixins.
3. Process the config using scripts.
4. Handle the config using plugins.
*/
export const generateConfig = async (originalProfile: ProfileType) => {
const profile = deepClone(originalProfile)

Expand All @@ -254,6 +261,7 @@ export const generateConfig = async (originalProfile: ProfileType) => {
hosts: {}
}

// step 1
if (config.tun.enable && config.tun['auto-route']) {
config['route-address'] = config.tun['route-address']
}
Expand Down Expand Up @@ -305,9 +313,34 @@ export const generateConfig = async (originalProfile: ProfileType) => {
.filter(({ type }) => profile.advancedConfig['geodata-mode'] || !type.startsWith('GEO'))
.map((rule) => generateRule(rule, profile.proxyGroupsConfig))

// step 2
const { priority, config: mixin } = originalProfile.mixinConfig
if (priority === 'mixin') {
deepAssign(config, parse(mixin))
} else if (priority === 'gui') {
deepAssign(config, deepAssign(parse(mixin), config))
}

// step 3
const fn = new AsyncFunction(
`${profile.scriptConfig.code};return await onGenerate(${JSON.stringify(config)})`
)
let _config
try {
_config = await fn()
} catch (error: any) {
throw error.message || error
}

if (typeof _config !== 'object') {
throw 'Wrong result'
}

// step 4
const pluginsStore = usePluginsStore()
const result = await pluginsStore.onGenerateTrigger(_config, originalProfile)

return await pluginsStore.onGenerateTrigger(config, originalProfile)
return result
}

export const generateConfigFile = async (profile: ProfileType) => {
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/utils/restorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
AdvancedConfigDefaults,
DnsConfigDefaults,
GeneralConfigDefaults,
TunConfigDefaults
TunConfigDefaults,
MixinConfigDefaults,
ScriptConfigDefaults
} from '@/constant'

export const restoreProfile = (
Expand All @@ -22,7 +24,9 @@ export const restoreProfile = (
dnsConfig: DnsConfigDefaults(),
tunConfig: TunConfigDefaults(),
proxyGroupsConfig: [],
rulesConfig: []
rulesConfig: [],
mixinConfig: MixinConfigDefaults(),
scriptConfig: ScriptConfigDefaults()
}

const GroupNameIdMap: Record<string, string> = {}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/views/HomeView/components/QuickStart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ const handleSubmit = async () => {
tunConfig: Defaults.TunConfigDefaults(),
dnsConfig: Defaults.DnsConfigDefaults(),
proxyGroupsConfig: Defaults.ProxyGroupsConfigDefaults(ids),
rulesConfig: Defaults.RulesConfigDefaults(ids)
rulesConfig: Defaults.RulesConfigDefaults(ids),
mixinConfig: Defaults.MixinConfigDefaults(),
scriptConfig: Defaults.ScriptConfigDefaults()
}
profile.proxyGroupsConfig[0].use = [subscribeID]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { type ProfileType } from '@/stores'
import { MixinConfigDefaults, ScriptConfigDefaults } from '@/constant'
const model = defineModel<{
mixin: ProfileType['mixinConfig']
script: ProfileType['scriptConfig']
}>({
default: {
mixin: MixinConfigDefaults(),
script: ScriptConfigDefaults()
}
})
const { t } = useI18n()
const activeTab = ref('mixin')
const tabItems = [
{ key: 'mixin', tab: 'profile.mixinSettings.name' },
{ key: 'script', tab: 'profile.scriptSettings.name' }
]
const MixinPriorityOptions = [
{ label: 'profile.mixinSettings.mixin', value: 'mixin' },
{ label: 'profile.mixinSettings.gui', value: 'gui' }
]
</script>

<template>
<Tabs v-model:active-key="activeTab" :items="tabItems">
<template #mixin>
<div class="form-item">
{{ t('profile.mixinSettings.priority') }}
<Radio v-model="model.mixin.priority" :options="MixinPriorityOptions" />
</div>
<CodeViewer v-model="model.mixin.config" lang="yaml" editable />
</template>
<template #script>
<CodeViewer v-model="model.script.code" lang="javascript" editable />
</template>
</Tabs>
</template>

<style lang="less" scoped></style>
34 changes: 28 additions & 6 deletions frontend/src/views/ProfilesView/components/ProfileForm.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { stringify } from 'yaml'
import { ref, inject, type Ref } from 'vue'
import { ref, inject, type Ref, computed } from 'vue'
import * as Defaults from '@/constant/profile'
import { WindowToggleMaximise } from '@/bridge'
Expand All @@ -15,6 +15,7 @@ import TunConfig from './TunConfig.vue'
import DnsConfig from './DnsConfig.vue'
import ProxyGroupsConfig from './ProxyGroupsConfig.vue'
import RulesConfig from './RulesConfig.vue'
import MixinAndScript from './MixinAndScriptConfig.vue'
interface Props {
id?: string
Expand All @@ -39,7 +40,8 @@ const stepItems = [
{ title: 'profile.step.tun' },
{ title: 'profile.step.dns' },
{ title: 'profile.step.groups' },
{ title: 'profile.step.rules' }
{ title: 'profile.step.rules' },
{ title: 'profile.step.mixin-script' }
]
const ids = [sampleID(), sampleID(), sampleID(), sampleID(), sampleID()]
Expand All @@ -52,7 +54,19 @@ const profile = ref<ProfileType>({
tunConfig: Defaults.TunConfigDefaults(),
dnsConfig: Defaults.DnsConfigDefaults(),
proxyGroupsConfig: Defaults.ProxyGroupsConfigDefaults(ids),
rulesConfig: Defaults.RulesConfigDefaults(ids)
rulesConfig: Defaults.RulesConfigDefaults(ids),
mixinConfig: Defaults.MixinConfigDefaults(),
scriptConfig: Defaults.ScriptConfigDefaults()
})
const mixinAndScriptConfig = computed({
get() {
return { mixin: profile.value.mixinConfig, script: profile.value.scriptConfig }
},
set({ mixin, script }) {
profile.value.mixinConfig = mixin
profile.value.scriptConfig = script
}
})
const { t } = useI18n()
Expand Down Expand Up @@ -92,8 +106,12 @@ const handleAdd = () => {
}
const handlePreview = async () => {
const config = await generateConfig(profile.value)
alert(profile.value.name, stringify(config))
try {
const config = await generateConfig(profile.value)
alert(profile.value.name, stringify(config))
} catch (error: any) {
message.error(error.message || error)
}
}
if (props.isUpdate) {
Expand Down Expand Up @@ -157,6 +175,10 @@ if (props.isUpdate) {
:profile="profile"
/>
</div>

<div v-show="currentStep === 6">
<MixinAndScript v-model="mixinAndScriptConfig" />
</div>
</div>

<div class="form-action">
Expand All @@ -165,7 +187,7 @@ if (props.isUpdate) {
</Button>
<Button
@click="handleNextStep"
:disabled="!profile.name || currentStep == 5"
:disabled="!profile.name || currentStep == stepItems.length - 1"
type="text"
class="mr-auto"
>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/views/ProfilesView/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ const menus: Menu[] = [
'profile.step.tun',
'profile.step.dns',
'profile.step.groups',
'profile.step.rules'
'profile.step.rules',
'profile.step.mixin-script'
].map((v, i) => {
return {
label: v,
Expand Down

0 comments on commit 8b0130f

Please sign in to comment.