mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-09-07 00:27:44 +08:00
feat: add versioning support for plugin assets and improve loading status handling
This commit is contained in:
@@ -67,10 +67,12 @@ async def delete_plugin(request: Request, key_or_id: str):
|
|||||||
async def get_bundle(request: Request, key_or_id: str):
|
async def get_bundle(request: Request, key_or_id: str):
|
||||||
"""获取插件前端 bundle"""
|
"""获取插件前端 bundle"""
|
||||||
path = await PluginService.get_bundle_path(key_or_id)
|
path = await PluginService.get_bundle_path(key_or_id)
|
||||||
|
v = (request.query_params.get("v") or "").strip()
|
||||||
|
cache_control = "public, max-age=31536000, immutable" if v else "no-cache"
|
||||||
return FileResponse(
|
return FileResponse(
|
||||||
path,
|
path,
|
||||||
media_type="application/javascript",
|
media_type="application/javascript",
|
||||||
headers={"Cache-Control": "no-store"},
|
headers={"Cache-Control": cache_control},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+34
-7
@@ -31,6 +31,19 @@ function renderStatus(text: string, isError: boolean = false) {
|
|||||||
root.appendChild(el);
|
root.appendChild(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scheduleStatus(text: string, delayMs: number) {
|
||||||
|
let canceled = false;
|
||||||
|
const t = window.setTimeout(() => {
|
||||||
|
if (canceled) return;
|
||||||
|
renderStatus(text);
|
||||||
|
}, delayMs);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
canceled = true;
|
||||||
|
window.clearTimeout(t);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getQuery() {
|
function getQuery() {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const pluginKey = (params.get('pluginKey') || '').trim();
|
const pluginKey = (params.get('pluginKey') || '').trim();
|
||||||
@@ -89,14 +102,25 @@ function getPluginStylePaths(plugin: PluginItem): string[] {
|
|||||||
return styles.filter((s) => typeof s === 'string' && s.trim().length > 0);
|
return styles.filter((s) => typeof s === 'string' && s.trim().length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadPluginStyles(pluginKey: string, plugin: PluginItem) {
|
function withVersion(url: string, version?: string | null): string {
|
||||||
|
const v = typeof version === 'string' ? version.trim() : '';
|
||||||
|
if (!v) return url;
|
||||||
|
const u = new URL(url, window.location.origin);
|
||||||
|
u.searchParams.set('v', v);
|
||||||
|
return u.pathname + u.search;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPluginStyles(pluginKey: string, plugin: PluginItem, version?: string | null) {
|
||||||
const stylePaths = getPluginStylePaths(plugin);
|
const stylePaths = getPluginStylePaths(plugin);
|
||||||
if (stylePaths.length === 0) return;
|
if (stylePaths.length === 0) return;
|
||||||
|
|
||||||
const tasks = stylePaths.map(
|
const tasks = stylePaths.map(
|
||||||
(p) =>
|
(p) =>
|
||||||
new Promise<void>((resolve) => {
|
new Promise<void>((resolve) => {
|
||||||
const href = `/api/plugins/${pluginKey}/assets/${p.replace(/^\/+/, '')}`;
|
const href = withVersion(
|
||||||
|
`/api/plugins/${pluginKey}/assets/${p.replace(/^\/+/, '')}`,
|
||||||
|
version
|
||||||
|
);
|
||||||
const link = document.createElement('link');
|
const link = document.createElement('link');
|
||||||
link.rel = 'stylesheet';
|
link.rel = 'stylesheet';
|
||||||
link.href = href;
|
link.href = href;
|
||||||
@@ -109,8 +133,8 @@ async function loadPluginStyles(pluginKey: string, plugin: PluginItem) {
|
|||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadPluginBundle(pluginKey: string): Promise<RegisteredPlugin> {
|
async function loadPluginBundle(pluginKey: string, version?: string | null): Promise<RegisteredPlugin> {
|
||||||
const url = `/api/plugins/${pluginKey}/bundle.js`;
|
const url = withVersion(`/api/plugins/${pluginKey}/bundle.js`, version);
|
||||||
|
|
||||||
return new Promise<RegisteredPlugin>((resolve, reject) => {
|
return new Promise<RegisteredPlugin>((resolve, reject) => {
|
||||||
let done = false;
|
let done = false;
|
||||||
@@ -178,32 +202,35 @@ async function main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderStatus('Loading plugin...');
|
const cancelLoading = scheduleStatus('Loading plugin...', 200);
|
||||||
|
|
||||||
let plugin: PluginItem;
|
let plugin: PluginItem;
|
||||||
try {
|
try {
|
||||||
plugin = await pluginsApi.get(pluginKey);
|
plugin = await pluginsApi.get(pluginKey);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
|
cancelLoading();
|
||||||
renderStatus(`Failed to load plugin info: ${msg}`, true);
|
renderStatus(`Failed to load plugin info: ${msg}`, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await loadPluginStyles(pluginKey, plugin);
|
await loadPluginStyles(pluginKey, plugin, plugin.version);
|
||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
let registered: RegisteredPlugin;
|
let registered: RegisteredPlugin;
|
||||||
try {
|
try {
|
||||||
registered = await loadPluginBundle(pluginKey);
|
registered = await loadPluginBundle(pluginKey, plugin.version);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
|
cancelLoading();
|
||||||
renderStatus(`Failed to load plugin bundle: ${msg}`, true);
|
renderStatus(`Failed to load plugin bundle: ${msg}`, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelLoading();
|
||||||
const host = createHostApi(pluginKey);
|
const host = createHostApi(pluginKey);
|
||||||
|
|
||||||
let cleanup: (() => void) | null = null;
|
let cleanup: (() => void) | null = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user