Skip to content

Commit

Permalink
feat(manager): support unload/reload temporary plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jun 9, 2022
1 parent 104b942 commit 890b7c7
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 64 deletions.
15 changes: 8 additions & 7 deletions packages/cli/src/worker/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,22 +108,23 @@ export default class Loader extends ConfigLoader<App.Config> {
return parent.plugin(plugin, this.interpolate(config))
}

reloadPlugin(runtime: Plugin.Runtime, name: string, config: any) {
const fork = runtime[Loader.kRecord][name]
reloadPlugin(runtime: Plugin.Runtime, key: string, config: any) {
const fork = runtime[Loader.kRecord][key]
const name = key.split('@')[0]
if (fork) {
fork.update(config, true)
logger.info(`reload plugin %c`, name)
} else {
runtime[Loader.kRecord][name] = this.forkPlugin(name, config, runtime.context)
runtime[Loader.kRecord][key] = this.forkPlugin(name, config, runtime.context)
}
}

unloadPlugin(runtime: Plugin.Runtime, name: string) {
const fork = runtime[Loader.kRecord][name]
unloadPlugin(runtime: Plugin.Runtime, key: string) {
const fork = runtime[Loader.kRecord][key]
if (fork) {
fork.dispose()
logger.info(`unload plugin %c`, name)
delete runtime[Loader.kRecord][name]
delete runtime[Loader.kRecord][key]
logger.info(`unload plugin %c`, key)
this.diagnose(true)
}
}
Expand Down
1 change: 0 additions & 1 deletion plugins/frontend/manager/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export default (ctx: Context) => {
icon: 'cog',
order: 630,
authority: 4,
strict: true,
fields: ['config', 'packages', 'services', 'dependencies'],
component: Settings,
})
Expand Down
14 changes: 5 additions & 9 deletions plugins/frontend/manager/client/settings/group.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { send } from '@koishijs/client'
import { Tree, plugins } from './utils'
const router = useRouter()
Expand All @@ -18,15 +19,10 @@ const props = defineProps<{
}>()
function addPlugin() {
const tree: Tree = {
label: '',
path: props.current.path + '$',
config: {},
disabled: true,
}
props.current.children.push(tree)
plugins.value.paths[tree.path] = tree
router.replace('/plugins/' + tree.path)
const id = Math.random().toString(36).slice(2, 8)
const path = (props.current.path ? props.current.path + '/' : '') + '@' + id
send(`manager/plugin-unload`, path, {})
router.replace('/plugins/' + path)
}
</script>
18 changes: 7 additions & 11 deletions plugins/frontend/manager/client/settings/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<keep-alive>
<k-content class="plugin-view" :key="path">
<global-settings v-if="current.path === '@global'" :current="current"></global-settings>
<group-settings v-else-if="current.children" :current="current"></group-settings>
<group-settings v-else-if="current.children" v-model="path" :current="current"></group-settings>
<plugin-settings v-else :current="current"></plugin-settings>
</k-content>
</keep-alive>
Expand All @@ -17,22 +17,18 @@
import { computed, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { plugins } from './utils'
import { plugins, Tree } from './utils'
import GlobalSettings from './global.vue'
import GroupSettings from './group.vue'
import TreeView from './tree.vue'
import PluginSettings from './plugin.vue'
function join(source: string | string[], trailing = false) {
return Array.isArray(source) ? source.join('/') + (trailing ? '/' : '') : source || ''
}
const route = useRoute()
const router = useRouter()
const path = computed<string>({
get() {
const name = join(route.params.name, route.fullPath.endsWith('/'))
const name = route.path.slice(9)
return name in plugins.value.paths ? name : '@global'
},
set(name) {
Expand All @@ -41,11 +37,11 @@ const path = computed<string>({
},
})
const current = ref(plugins.value.paths[path.value])
const current = ref<Tree>()
watch(() => path.value, () => {
current.value = plugins.value.paths[path.value]
})
watch(() => plugins.value.paths[path.value], (value) => {
current.value = value
}, { immediate: true })
</script>

Expand Down
27 changes: 18 additions & 9 deletions plugins/frontend/manager/client/settings/plugin.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<h1 class="config-header">
<template v-if="current.path.endsWith('$')">
<el-select v-model="current.label" placeholder="插件选择">
<template v-if="!current.label">
<el-select v-model="current.target" placeholder="插件选择">
<el-option
v-for="value in Object.values(store.packages).slice(1)"
:key="value.name"
Expand All @@ -18,7 +18,7 @@
<k-button solid type="error" @click="execute('unload')">停用插件</k-button>
<k-button solid :disabled="env.invalid" @click="execute('reload')">重载配置</k-button>
</template>
<template v-else-if="name">
<template v-else-if="env">
<k-button solid :disabled="env.invalid" @click="execute('reload')">启用插件</k-button>
<k-button solid @click="execute('unload')">保存配置</k-button>
</template>
Expand Down Expand Up @@ -80,7 +80,7 @@

<script lang="ts" setup>
import { send, store, clone } from '@koishijs/client'
import { send, store, clone, router } from '@koishijs/client'
import { computed, ref, watch } from 'vue'
import { getMixedMeta } from '../utils'
import { envMap, Tree } from './utils'
Expand All @@ -97,12 +97,16 @@ watch(() => props.current.config, (value) => {
}, { immediate: true })
const name = computed(() => {
const { label } = props.current
if (label.includes('/')) {
const [left, right] = label.split('/')
const { label, target: temporary } = props.current
const shortname = temporary || label
if (shortname.includes('/')) {
const [left, right] = shortname.split('/')
return `${left}/koishi-plugin-${right}`
}
return [`@koishijs/plugin-${label}`, `koishi-plugin-${label}`].find(name => name in store.packages)
return [
`@koishijs/plugin-${shortname}`,
`koishi-plugin-${shortname}`,
].find(name => name in store.packages)
})
const data = computed(() => getMixedMeta(name.value))
Expand All @@ -115,7 +119,12 @@ const hasUpdate = computed(() => {
})
function execute(event: 'unload' | 'reload') {
send(`manager/plugin-${event}`, props.current.path, config.value)
send(`manager/plugin-${event}`, props.current.path, config.value, props.current.target)
if (props.current.target) {
const segments = props.current.path.split('/')
segments[segments.length - 1] = props.current.target
router.replace('/plugins/' + segments.join('/'))
}
}
</script>
Expand Down
6 changes: 2 additions & 4 deletions plugins/frontend/manager/client/settings/tree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<k-tab-item class="k-tab-group-title" label="@global" v-model="model">全局设置</k-tab-item>
<el-tree
ref="tree"
:data="[plugins.root]"
:data="plugins.data"
:draggable="true"
:default-expand-all="true"
:expand-on-click-node="false"
Expand Down Expand Up @@ -69,9 +69,7 @@ function handleDrop(source: Node, target: Node, position: 'before' | 'after' | '
const oldPath = source.data.path
const ctxPath = parent.data.path
const index = parent.childNodes.findIndex(node => node.data.path === oldPath)
if (!oldPath.endsWith('$')) {
send('manager/teleport', oldPath, ctxPath, index)
}
send('manager/teleport', oldPath, ctxPath, index)
const segments1 = oldPath.split('/')
const segments2 = ctxPath ? ctxPath.split('/') : []
segments2.push(segments1.pop())
Expand Down
19 changes: 11 additions & 8 deletions plugins/frontend/manager/client/settings/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,25 +100,28 @@ export interface Tree {
label: string
path: string
config?: any
target?: string
disabled?: boolean
children?: Tree[]
}

function getTree(prefix: string, plugins: any): Tree[] {
const trees: Tree[] = []
for (const key in plugins) {
for (let key in plugins) {
if (key.startsWith('$')) continue
const label = key.replace(/^~/, '')
const path = prefix + label
const config = plugins[key]
const node: Tree = { label, path, config }
const node = { config } as Tree
if (key.startsWith('~')) {
node.disabled = true
key = key.slice(1)
}
if (key.startsWith('group@')) {
node.path += '/'
node.label = '分组:' + label.slice(6)
node.children = getTree(path + '/', config)
node.label = '分组:' + key.slice(6)
node.path = prefix + key
node.children = getTree(node.path + '/', config)
} else {
node.label = key.split('@')[0]
node.path = prefix + key
}
trees.push(node)
}
Expand All @@ -144,5 +147,5 @@ export const plugins = computed(() => {
tree.children?.forEach(traverse)
}
traverse(root)
return { root, paths }
return { data: [root], paths }
})
34 changes: 19 additions & 15 deletions plugins/frontend/manager/src/writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ declare module '@koishijs/plugin-console' {
interface Events {
'manager/app-reload'(config: any): void
'manager/teleport'(source: string, target: string, index: number): void
'manager/plugin-reload'(path: string, config: any): void
'manager/plugin-unload'(path: string, config: any): void
'manager/plugin-reload'(path: string, config: any, key?: string): void
'manager/plugin-unload'(path: string, config: any, key?: string): void
'manager/bot-update'(id: string, adapter: string, config: any): void
'manager/bot-remove'(id: string): void
}
Expand All @@ -23,10 +23,11 @@ function insertKey(object: {}, temp: {}, rest: string[]) {

function rename(object: any, old: string, neo: string, value: string) {
const keys = Object.keys(object)
const index = keys.indexOf(old)
const index = keys.findIndex(key => key === old || key === '~' + old)
const rest = index < 0 ? [] : keys.slice(index + 1)
const temp = { [neo]: value }
delete object[old]
delete object['~' + old]
insertKey(object, temp, rest)
}

Expand Down Expand Up @@ -56,12 +57,12 @@ class ConfigWriter extends DataService<App.Config> {
this.teleport(source, target, index)
}, { authority: 4 })

ctx.console.addListener('manager/plugin-reload', (name, config) => {
this.reloadPlugin(name, config)
ctx.console.addListener('manager/plugin-reload', (name, config, key) => {
this.reloadPlugin(name, config, key)
}, { authority: 4 })

ctx.console.addListener('manager/plugin-unload', (name, config) => {
this.unloadPlugin(name, config)
ctx.console.addListener('manager/plugin-unload', (name, config, key) => {
this.unloadPlugin(name, config, key)
}, { authority: 4 })

ctx.console.addListener('manager/bot-update', (id, adapter, config) => {
Expand Down Expand Up @@ -97,17 +98,20 @@ class ConfigWriter extends DataService<App.Config> {
return [runtime, name] as const
}

reloadPlugin(path: string, config: any) {
const [runtime, name] = this.resolve(path)
this.loader.reloadPlugin(runtime, name, config)
rename(runtime.config, '~' + name, name, config)
reloadPlugin(path: string, config: any, newKey?: string) {
const [runtime, oldKey] = this.resolve(path)
if (newKey) {
this.loader.unloadPlugin(runtime, oldKey)
}
this.loader.reloadPlugin(runtime, newKey, config)
rename(runtime.config, oldKey, newKey || oldKey, config)
this.loader.writeConfig()
}

unloadPlugin(path: string, config: any) {
const [runtime, name] = this.resolve(path)
this.loader.unloadPlugin(runtime, name)
rename(runtime.config, name, '~' + name, config)
unloadPlugin(path: string, config: any, newKey?: string) {
const [runtime, oldKey] = this.resolve(path)
this.loader.unloadPlugin(runtime, oldKey)
rename(runtime.config, oldKey, '~' + (newKey || oldKey), config)
this.loader.writeConfig()
}

Expand Down

0 comments on commit 890b7c7

Please sign in to comment.