mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 03:52:26 +08:00
fix(plugins): ignore obsolete configuration UI sessions
This commit is contained in:
@@ -380,6 +380,7 @@ const uiError = ref("");
|
||||
const iframeHeight = ref(640);
|
||||
const pluginFrameLoaded = ref(false);
|
||||
const pendingBridgeRequests = new Map<string, number>();
|
||||
let uiSessionVersion = 0;
|
||||
|
||||
function errorMessage(error: unknown): string {
|
||||
if (typeof error === "object" && error !== null && "message" in error) {
|
||||
@@ -523,6 +524,7 @@ async function testPlugin(plugin: PluginInstallation): Promise<void> {
|
||||
}
|
||||
|
||||
async function openConfiguration(plugin: PluginInstallation): Promise<void> {
|
||||
const version = ++uiSessionVersion;
|
||||
configPlugin.value = plugin;
|
||||
uiSession.value = null;
|
||||
pluginFrameLoaded.value = false;
|
||||
@@ -531,14 +533,18 @@ async function openConfiguration(plugin: PluginInstallation): Promise<void> {
|
||||
uiError.value = "";
|
||||
iframeHeight.value = 640;
|
||||
try {
|
||||
uiSession.value = await adminAPI.plugins.createUISession(plugin.id);
|
||||
const session = await adminAPI.plugins.createUISession(plugin.id);
|
||||
if (version !== uiSessionVersion) return;
|
||||
uiSession.value = session;
|
||||
} catch (error: unknown) {
|
||||
if (version !== uiSessionVersion) return;
|
||||
uiLoading.value = false;
|
||||
uiError.value = errorMessage(error);
|
||||
}
|
||||
}
|
||||
|
||||
function closeConfiguration(): void {
|
||||
uiSessionVersion++;
|
||||
clearPendingBridgeRequests();
|
||||
pluginFrameLoaded.value = false;
|
||||
configPlugin.value = null;
|
||||
@@ -723,6 +729,6 @@ onMounted(() => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("message", handleBridgeMessage);
|
||||
clearPendingBridgeRequests();
|
||||
closeConfiguration();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import PluginsView from '../PluginsView.vue'
|
||||
import BaseDialog from '@/components/common/BaseDialog.vue'
|
||||
import type { PluginUISession } from '@/api/admin'
|
||||
|
||||
enableAutoUnmount(afterEach)
|
||||
|
||||
const {
|
||||
listPlugins,
|
||||
@@ -151,6 +155,60 @@ describe('管理员插件页二次验证', () => {
|
||||
expect(enablePlugin).toHaveBeenCalledWith(7, 100, false)
|
||||
})
|
||||
|
||||
it.each([7, 8])('keeps the latest session when reopening configuration for plugin %s', async (id) => {
|
||||
let resolveOld!: (session: PluginUISession) => void
|
||||
createUISession.mockReturnValueOnce(new Promise<PluginUISession>((resolve) => { resolveOld = resolve }))
|
||||
listPlugins.mockResolvedValue([plugin, { ...plugin, id: 8, name: 'Other Transport' }])
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
const buttons = wrapper.findAll('button').filter(item => item.text() === 'admin.plugins.configure')
|
||||
await buttons[0].trigger('click')
|
||||
wrapper.getComponent(BaseDialog).vm.$emit('close')
|
||||
await buttons[id === 7 ? 0 : 1].trigger('click')
|
||||
await flushPromises()
|
||||
const currentURL = wrapper.get('iframe').attributes('src')
|
||||
|
||||
resolveOld({ url: '/obsolete-session', bridge_token: 'old', ui_bridge_version: 1, expires_at: '' })
|
||||
await flushPromises()
|
||||
expect(wrapper.get('iframe').attributes('src')).toBe(currentURL)
|
||||
})
|
||||
|
||||
it('ignores an old session error while the reopened configuration is loading', async () => {
|
||||
let rejectOld!: (error: Error) => void
|
||||
let resolveCurrent!: (session: PluginUISession) => void
|
||||
createUISession
|
||||
.mockReturnValueOnce(new Promise((_, reject) => { rejectOld = reject }))
|
||||
.mockReturnValueOnce(new Promise<PluginUISession>((resolve) => { resolveCurrent = resolve }))
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
const button = wrapper.findAll('button').find(item => item.text() === 'admin.plugins.configure')!
|
||||
await button.trigger('click')
|
||||
wrapper.getComponent(BaseDialog).vm.$emit('close')
|
||||
await button.trigger('click')
|
||||
rejectOld(new Error('obsolete failure'))
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).not.toContain('obsolete failure')
|
||||
expect(wrapper.text()).toContain('admin.plugins.loadingUI')
|
||||
resolveCurrent({ url: '/current-session', bridge_token: 'current', ui_bridge_version: 1, expires_at: '' })
|
||||
await flushPromises()
|
||||
await wrapper.get('iframe').trigger('load')
|
||||
expect(wrapper.get('iframe').attributes('src')).toBe('/current-session')
|
||||
expect(wrapper.text()).not.toContain('admin.plugins.loadingUI')
|
||||
})
|
||||
|
||||
it('does not restore a session after the configuration dialog closes', async () => {
|
||||
let resolveSession!: (session: PluginUISession) => void
|
||||
createUISession.mockReturnValueOnce(new Promise<PluginUISession>((resolve) => { resolveSession = resolve }))
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
await wrapper.findAll('button').find(item => item.text() === 'admin.plugins.configure')!.trigger('click')
|
||||
wrapper.getComponent(BaseDialog).vm.$emit('close')
|
||||
resolveSession({ url: '/closed-session', bridge_token: 'old', ui_bridge_version: 1, expires_at: '' })
|
||||
await flushPromises()
|
||||
expect(wrapper.find('iframe').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('上传插件通过 step-up 控制器执行', async () => {
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
|
||||
Reference in New Issue
Block a user