diff --git a/app/stores/app.js b/app/stores/app.js index 4fb94817..1670d53e 100644 --- a/app/stores/app.js +++ b/app/stores/app.js @@ -67,10 +67,157 @@ export const useAppStore = defineStore("app", () => { console.log(`[AppStore] Imported ${importedCount} stores`) } + const loadedExtensions = ref(new Map()) + const extensionAPI = ref(null) + const codeTransformer = ref(null) + + function setExtensionAPI(api) { + extensionAPI.value = api + } + + function setCodeTransformer(transformer) { + codeTransformer.value = transformer + } + + function getExtension(id) { + return loadedExtensions.value.get(id) + } + + async function loadExtension(path, backendPath = null) { + try { + let finalURL = path + + if (codeTransformer.value && path.startsWith("blob:")) { + const response = await fetch(path) + const code = await response.text() + const transformedCode = codeTransformer.value(code) + + const newBlob = new Blob([transformedCode], { + type: "application/javascript", + }) + finalURL = URL.createObjectURL(newBlob) + } + + const extensionModule = await import(finalURL) + + if (finalURL !== path && finalURL.startsWith("blob:")) { + URL.revokeObjectURL(finalURL) + } + + if (!extensionModule.metadata?.id) { + throw new Error("Extension must have metadata.id") + } + + const extensionId = extensionModule.metadata.id + + if (loadedExtensions.value.has(extensionId)) { + console.warn(`[AppStore] Extension "${extensionId}" is already loaded`) + throw new Error(`Extension "${extensionId}" is already loaded.`) + } + + if (!extensionAPI.value) { + throw new Error("Extension API not initialized") + } + + if (typeof extensionModule.install === "function") { + await extensionModule.install(extensionAPI.value, backendPath) + + const extensionData = { + module: extensionModule, + id: extensionId, + path, + backendPath, + loadedAt: new Date().toISOString(), + metadata: extensionModule.metadata, + enabled: true, + } + loadedExtensions.value.set(extensionId, extensionData) + + console.log(`[AppStore] Extension loaded successfully: ${extensionId}`) + + return extensionModule + } else { + throw new Error("Extension must export an install function") + } + } catch (error) { + console.error(`[AppStore] Failed to load extension from ${path}:`, error) + throw error + } + } + + function getLoadedExtensions() { + return Array.from(loadedExtensions.value.values()) + } + + function unloadExtension(id) { + const extensionData = getExtension(id) + if (!extensionData) return false + + if ( + extensionData.module && + typeof extensionData.module.uninstall === "function" + ) { + try { + extensionData.module.uninstall(extensionAPI.value) + console.log(`[AppStore] Extension uninstall called: ${id}`) + } catch (error) { + console.error(`[AppStore] Error calling uninstall for ${id}:`, error) + } + } + + if ( + extensionAPI.value && + typeof extensionAPI.value.unregisterToolsByExtension === "function" + ) { + extensionAPI.value.unregisterToolsByExtension(id) + } + + loadedExtensions.value.delete(id) + console.log(`[AppStore] Extension unloaded: ${id}`) + return true + } + + function toggleExtension(id) { + const extensionData = getExtension(id) + if (!extensionData) return false + + extensionData.enabled = !extensionData.enabled + console.log( + `[AppStore] Extension ${extensionData.enabled ? "enabled" : "disabled"}: ${id}`, + ) + return extensionData.enabled + } + + function setExtensionEnabled(id, enabled) { + const extensionData = getExtension(id) + if (!extensionData) return false + + extensionData.enabled = enabled + console.log( + `[AppStore] Extension ${enabled ? "enabled" : "disabled"}: ${id}`, + ) + return true + } + + function getExtensionEnabled(id) { + return getExtension(id)?.enabled ?? false + } + return { stores, registerStore, exportStores, importStores, + loadedExtensions, + extensionAPI, + setExtensionAPI, + setCodeTransformer, + loadExtension, + getLoadedExtensions, + getExtension, + unloadExtension, + toggleExtension, + setExtensionEnabled, + getExtensionEnabled, } }) diff --git a/app/utils/file_import_workflow.js b/app/utils/file_import_workflow.js index f0a22a0b..649a32c4 100644 --- a/app/utils/file_import_workflow.js +++ b/app/utils/file_import_workflow.js @@ -34,7 +34,15 @@ async function importItem(item) { const dataStyleStore = useDataStyleStore() const hybridViewerStore = useHybridViewerStore() const treeviewStore = useTreeviewStore() - await dataBaseStore.registerObject(item.id) + console.log("importItem", { item }) + try { + await dataBaseStore.registerObject(item.id) + } catch (error) { + console.warn( + `[Import] Failed to register object ${item.id} in backend viewer:`, + error, + ) + } await dataBaseStore.addItem(item.id, { ...item, }) diff --git a/tests/integration/microservices/back/requirements.txt b/tests/integration/microservices/back/requirements.txt index 642f9cb6..bd3a3ef5 100644 --- a/tests/integration/microservices/back/requirements.txt +++ b/tests/integration/microservices/back/requirements.txt @@ -5,4 +5,3 @@ # pip-compile --output-file=tests/integration/microservices/back/requirements.txt tests/integration/microservices/back/requirements.in # -opengeodeweb-back==5.*,>=5.14.1 diff --git a/tests/integration/microservices/viewer/requirements.txt b/tests/integration/microservices/viewer/requirements.txt index 147e1750..4d097394 100644 --- a/tests/integration/microservices/viewer/requirements.txt +++ b/tests/integration/microservices/viewer/requirements.txt @@ -5,4 +5,3 @@ # pip-compile --output-file=tests/integration/microservices/viewer/requirements.txt tests/integration/microservices/viewer/requirements.in # -opengeodeweb-viewer==1.*,>=1.13.2