diff --git a/app/components/Launcher.vue b/app/components/Launcher.vue index dbeb0e556..519d76d02 100644 --- a/app/components/Launcher.vue +++ b/app/components/Launcher.vue @@ -2,7 +2,7 @@ import Loading from "@ogw_front/components/Loading"; import Recaptcha from "@ogw_front/components/Recaptcha"; import { Status } from "@ogw_front/utils/status"; -import { appMode } from "@ogw_front/utils/local/app_mode"; +import { appMode } from "@ogw_shared/app_mode"; import { useInfraStore } from "@ogw_front/stores/infra"; const { appName, email, isUserAuthenticated, logo } = defineProps({ diff --git a/app/stores/app.js b/app/stores/app.js index 4051efe68..0a20e081a 100644 --- a/app/stores/app.js +++ b/app/stores/app.js @@ -1,13 +1,29 @@ // Local imports +import { getRestApiPort, getRestApiProtocol, isCloudMode } from "@ogw_front/utils/stores.js"; +import { Status } from "@ogw_front/utils/status"; import { api_fetch } from "@ogw_internal/utils/api_fetch.js"; -import { upload_file } from "@ogw_internal/utils/upload_file.js"; - import { killExtension } from "@ogw_front/utils/extension.js"; +import { upload_file } from "@ogw_internal/utils/upload_file.js"; import { useInfraStore } from "@ogw_front/stores/infra"; // oxlint-disable-next-line max-lines-per-function, max-statements export const useAppStore = defineStore("app", () => { const stores = []; + const default_local_port = ref(globalThis.location.port); + const status = ref(Status.NOT_CONNECTED); + + const protocol = computed(() => getRestApiProtocol()); + + const port = computed(() => getRestApiPort(default_local_port.value)); + + const base_url = computed(() => { + const infraStore = useInfraStore(); + let app_url = `${protocol.value}://${infraStore.domain_name}:${port.value}`; + if (isCloudMode()) { + app_url += `/server`; + } + return app_url; + }); function registerStore(store) { const isAlreadyRegistered = stores.some((registeredStore) => registeredStore.$id === store.$id); @@ -91,7 +107,7 @@ export const useAppStore = defineStore("app", () => { return loadedExtensions.value.get(id); } - async function loadExtension(path, port, backendPath = undefined) { + async function loadExtension(path, extensionPort, backendPath = undefined) { try { let finalURL = path; @@ -108,7 +124,7 @@ export const useAppStore = defineStore("app", () => { // oxlint-disable-next-line no-inline-comments const extensionModule = await import(/* @vite-ignore */ finalURL); const store = extensionModule.metadata.store(); - store.$patch({ default_local_port: port }); + store.$patch({ default_local_port: extensionPort }); if (finalURL !== path && finalURL.startsWith("blob:")) { URL.revokeObjectURL(finalURL); @@ -196,7 +212,7 @@ export const useAppStore = defineStore("app", () => { } function upload(file, callbacks = {}) { - const route = "/api/microservice/extensions/upload"; + const route = "/api/local/extensions/upload"; const store = useAppStore(); return upload_file( store, @@ -239,12 +255,13 @@ export const useAppStore = defineStore("app", () => { function stop_request() { request_counter.value -= 1; } + const is_busy = computed(() => request_counter.value > 0); const projectFolderPath = ref(""); function createProjectFolder() { const { PROJECT } = useRuntimeConfig().public; const schema = { - $id: "/api/local/project_folder_path", + $id: "/api/local/app/project_folder_path", methods: ["POST"], type: "object", properties: { PROJECT: { type: "string" } }, @@ -265,6 +282,13 @@ export const useAppStore = defineStore("app", () => { return { stores, + default_local_port, + request_counter, + status, + protocol, + port, + base_url, + is_busy, registerStore, exportStores, importStores, diff --git a/app/stores/back.js b/app/stores/back.js index e1a915422..8b6ff01e8 100644 --- a/app/stores/back.js +++ b/app/stores/back.js @@ -1,6 +1,6 @@ +import { getRestApiPort, getRestApiProtocol, isCloudMode } from "@ogw_front/utils/stores"; import { Status } from "@ogw_front/utils/status"; import { api_fetch } from "@ogw_internal/utils/api_fetch"; -import { appMode } from "@ogw_front/utils/local/app_mode"; import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json"; import { upload_file } from "@ogw_internal/utils/upload_file.js"; import { useAppStore } from "@ogw_front/stores/app"; @@ -19,24 +19,18 @@ export const useBackStore = defineStore("back", { }), getters: { protocol() { - if (useInfraStore().app_mode === appMode.CLOUD) { - return "https"; - } - return "http"; + return getRestApiProtocol(); }, port() { - if (useInfraStore().app_mode === appMode.CLOUD) { - return "443"; - } - return this.default_local_port; + return getRestApiPort(this.default_local_port); }, base_url() { const infraStore = useInfraStore(); - let geode_url = `${this.protocol}://${infraStore.domain_name}:${this.port}`; - if (infraStore.app_mode === appMode.CLOUD) { - geode_url += `/geode`; + let back_url = `${this.protocol}://${infraStore.domain_name}:${this.port}`; + if (isCloudMode()) { + back_url += `/geode`; } - return geode_url; + return back_url; }, is_busy() { return this.request_counter > 0; @@ -81,7 +75,7 @@ export const useBackStore = defineStore("back", { const appStore = useAppStore(); const { COMMAND_BACK, NUXT_ROOT_PATH } = useRuntimeConfig().public; const schema = { - $id: "/api/local/run_back", + $id: "/api/local/app/run_back", methods: ["POST"], type: "object", properties: { diff --git a/app/stores/cloud.js b/app/stores/cloud.js index 2852da698..6a478b93b 100644 --- a/app/stores/cloud.js +++ b/app/stores/cloud.js @@ -1,4 +1,5 @@ import { Status } from "@ogw_front/utils/status"; +import { setAppBaseUrl } from "@ogw_shared/scripts"; import { useAppStore } from "./app"; import { useFeedbackStore } from "./feedback"; import { useInfraStore } from "./infra"; @@ -40,6 +41,7 @@ export const useCloudStore = defineStore("cloud", { infraStore.$patch({ domain_name: response.url, }); + setAppBaseUrl(appStore.base_url); }, response_error_function: () => { feedbackStore.$patch({ server_error: true }); diff --git a/app/stores/infra.js b/app/stores/infra.js index 2fbdeffd8..11df9ee69 100644 --- a/app/stores/infra.js +++ b/app/stores/infra.js @@ -1,5 +1,5 @@ import { Status } from "@ogw_front/utils/status"; -import { appMode } from "@ogw_front/utils/local/app_mode"; +import { appMode } from "@ogw_shared/app_mode"; import { registerRunningExtensions } from "@ogw_front/utils/extension"; import { useAppStore } from "@ogw_front/stores/app"; import { useCloudStore } from "@ogw_front/stores/cloud"; diff --git a/app/stores/viewer.js b/app/stores/viewer.js index 51d702d1b..e3763a271 100644 --- a/app/stores/viewer.js +++ b/app/stores/viewer.js @@ -8,8 +8,12 @@ import { connectImageStream } from "@kitware/vtk.js/Rendering/Misc/RemoteView"; import schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json"; // Local imports +import { + getWebsocketApiPort, + getWebsocketApiProtocol, + isCloudMode, +} from "@ogw_front/utils/stores.js"; import { Status } from "@ogw_front/utils/status"; -import { appMode } from "@ogw_front/utils/local/app_mode"; import { useAppStore } from "@ogw_front/stores/app"; import { useInfraStore } from "@ogw_front/stores/infra"; import { viewer_call } from "@ogw_internal/utils/viewer_call"; @@ -33,23 +37,13 @@ export const useViewerStore = defineStore( const version = ref("0.0.0"); const busy = ref(0); - const protocol = computed(() => { - if (infraStore.app_mode === appMode.CLOUD) { - return "wss"; - } - return "ws"; - }); + const protocol = computed(() => getWebsocketApiProtocol()); - const port = computed(() => { - if (infraStore.app_mode === appMode.CLOUD) { - return "443"; - } - return default_local_port.value; - }); + const port = computed(() => getWebsocketApiPort(default_local_port.value)); const base_url = computed(() => { let viewer_url = `${protocol.value}://${infraStore.domain_name}:${port.value}`; - if (infraStore.app_mode === appMode.CLOUD) { + if (isCloudMode()) { viewer_url += `/viewer`; } viewer_url += "/ws"; @@ -133,7 +127,7 @@ export const useViewerStore = defineStore( const { COMMAND_VIEWER, NUXT_ROOT_PATH } = useRuntimeConfig().public; const schema = { - $id: "/api/local/run_viewer", + $id: "/api/local/app/run_viewer", methods: ["POST"], type: "object", properties: { COMMAND_VIEWER: { type: "string" }, NUXT_ROOT_PATH: { type: "string" } }, diff --git a/app/utils/extension.js b/app/utils/extension.js index 3afdcf88d..bc8504641 100644 --- a/app/utils/extension.js +++ b/app/utils/extension.js @@ -12,6 +12,11 @@ async function importExtensionFile(file) { return registerRunningExtensions(); } +async function importExtensionURL(url) { + await downloadExtension(url); + return registerRunningExtensions(); +} + async function registerRunningExtensions() { const appStore = useAppStore(); const infraStore = useInfraStore(); @@ -74,6 +79,27 @@ async function uploadExtension(file) { await appStore.upload(file); } +function downloadExtension({ url, extensionFileName }) { + const appStore = useAppStore(); + const { PROJECT: projectName } = useRuntimeConfig().public; + const params = { projectName, url, extensionFileName }; + + const schema = { + $id: "/api/microservice/extensions/download", + methods: ["POST"], + type: "object", + properties: { + extensionFileName: { type: "string" }, + projectName: { type: "string" }, + url: { type: "string" }, + }, + required: ["extensionFileName", "projectName", "url"], + additionalProperties: false, + }; + + return appStore.request({ schema, params }); +} + function runExtensions() { const appStore = useAppStore(); const { projectFolderPath } = appStore; @@ -121,9 +147,10 @@ function killExtension(extensionId) { export { importExtensionFile, - unloadExtension, - uploadExtension, + importExtensionURL, + killExtension, registerRunningExtensions, runExtensions, - killExtension, + unloadExtension, + uploadExtension, }; diff --git a/app/utils/stores.js b/app/utils/stores.js new file mode 100644 index 000000000..f1b224d17 --- /dev/null +++ b/app/utils/stores.js @@ -0,0 +1,32 @@ +import { appMode } from "@ogw_shared/app_mode"; +import { useInfraStore } from "@ogw_front/stores/infra"; + +function isCloudMode() { + const infraStore = useInfraStore(); + return infraStore.app_mode === appMode.CLOUD; +} + +function getRestApiProtocol() { + const protocol = isCloudMode() ? "https" : "http"; + return protocol; +} +function getRestApiPort(defaultLocalPort) { + const port = isCloudMode() ? "443" : defaultLocalPort; + return port; +} +function getWebsocketApiProtocol() { + const protocol = isCloudMode() ? "wss" : "ws"; + return protocol; +} +function getWebsocketApiPort(defaultLocalPort) { + const port = isCloudMode() ? "443" : defaultLocalPort; + return port; +} + +export { + isCloudMode, + getRestApiPort, + getRestApiProtocol, + getWebsocketApiProtocol, + getWebsocketApiPort, +}; diff --git a/internal/utils/api_fetch.js b/internal/utils/api_fetch.js index 086e48cf3..954810c0c 100644 --- a/internal/utils/api_fetch.js +++ b/internal/utils/api_fetch.js @@ -9,6 +9,7 @@ export function api_fetch( { schema, params, headers }, { request_error_function, response_function, response_error_function, timeout } = {}, ) { + console.log("[API] Fetching", microservice.base_url); const feedbackStore = useFeedbackStore(); const body = params || {}; diff --git a/nuxt.config.js b/nuxt.config.js index 3e6d32304..f01973c08 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -27,8 +27,9 @@ export default defineNuxtConfig({ alias: { "@ogw_front": path.resolve(__dirname, "app"), "@ogw_internal": path.resolve(__dirname, "internal"), - "@ogw_server": path.resolve(__dirname, "server"), "@ogw_tests": path.resolve(__dirname, "tests"), + "@ogw_shared": path.resolve(__dirname, "shared"), + "@ogw_server": path.resolve(__dirname, "server"), }, // ** Global CSS diff --git a/server/api/local/kill.post.js b/server/api/local/app/kill.post.js similarity index 100% rename from server/api/local/kill.post.js rename to server/api/local/app/kill.post.js diff --git a/server/api/local/project_folder_path.post.js b/server/api/local/app/project_folder_path.post.js similarity index 90% rename from server/api/local/project_folder_path.post.js rename to server/api/local/app/project_folder_path.post.js index 7de3d9498..b09c26355 100644 --- a/server/api/local/project_folder_path.post.js +++ b/server/api/local/app/project_folder_path.post.js @@ -7,7 +7,7 @@ import { createError, defineEventHandler, readBody } from "h3"; import { createPath, generateProjectFolderPath, -} from "@geode/opengeodeweb-front/app/utils/local/path.js"; +} from "@geode/opengeodeweb-front/server/utils/path.js"; export default defineEventHandler(async (event) => { try { diff --git a/server/api/local/run_back.post.js b/server/api/local/app/run_back.post.js similarity index 91% rename from server/api/local/run_back.post.js rename to server/api/local/app/run_back.post.js index f0e8ee805..90fd46da1 100644 --- a/server/api/local/run_back.post.js +++ b/server/api/local/app/run_back.post.js @@ -7,7 +7,7 @@ import { createError, defineEventHandler, readBody } from "h3"; import { addMicroserviceMetadatas, runBack, -} from "@geode/opengeodeweb-front/app/utils/local/microservices.js"; +} from "@geode/opengeodeweb-front/server/utils/microservices.js"; export default defineEventHandler(async (event) => { try { diff --git a/server/api/local/run_viewer.post.js b/server/api/local/app/run_viewer.post.js similarity index 91% rename from server/api/local/run_viewer.post.js rename to server/api/local/app/run_viewer.post.js index acd040bc9..72adde882 100644 --- a/server/api/local/run_viewer.post.js +++ b/server/api/local/app/run_viewer.post.js @@ -7,7 +7,7 @@ import { createError, defineEventHandler, readBody } from "h3"; import { addMicroserviceMetadatas, runViewer, -} from "@geode/opengeodeweb-front/app/utils/local/microservices.js"; +} from "@geode/opengeodeweb-front/server/utils/microservices.js"; export default defineEventHandler(async (event) => { try { diff --git a/server/api/microservice/extensions/upload.put.js b/server/api/local/extensions/upload.put.js similarity index 67% rename from server/api/microservice/extensions/upload.put.js rename to server/api/local/extensions/upload.put.js index 5a812e9e4..4c6cfc91f 100644 --- a/server/api/microservice/extensions/upload.put.js +++ b/server/api/local/extensions/upload.put.js @@ -2,27 +2,26 @@ import { finished, pipeline } from "node:stream/promises"; import { Readable } from "node:stream"; import fs from "node:fs"; -import path from "node:path"; // Third party imports import { createError, defineEventHandler, getRequestHeaders, getRequestWebStream } from "h3"; -import StreamZip from "node-stream-zip"; import busboy from "busboy"; -import sanitize from "sanitize-filename"; // Local imports -import { addExtensionToConf, confFolderPath } from "@geode/opengeodeweb-front/app/utils/config.js"; +import { + registerExtensionFile, + targetExtensionFilePath, +} from "@geode/opengeodeweb-front/server/utils/app_config.js"; const CODE_201 = 201; const FILE_SIZE_LIMIT = 107_374_182; export default defineEventHandler(async (event) => { - const projectName = "vease"; + const body = await readBody(event); + const { projectName } = body; const writePromises = []; const savedFiles = []; - const configFolderPath = confFolderPath(projectName); - const busboyInstance = busboy({ headers: getRequestHeaders(event), limits: { @@ -37,17 +36,13 @@ export default defineEventHandler(async (event) => { fileStream.resume(); return; } - - const safeFilename = sanitize(info.filename); - const targetPath = path.join(configFolderPath, safeFilename); - + const targetPath = targetExtensionFilePath(projectName, info.filename); const writePromise = (async () => { const writeStream = fs.createWriteStream(targetPath); await pipeline(fileStream, writeStream); savedFiles.push(targetPath); console.log("File written:", targetPath); })(); - writePromises.push(writePromise); fileStream.on("limit", () => busboyInstance.destroy(new Error("File too large"))); }); @@ -62,32 +57,13 @@ export default defineEventHandler(async (event) => { const webStream = getRequestWebStream(event); Readable.fromWeb(webStream).pipe(busboyInstance); await finished(busboyInstance); - if (writePromises.length > 0) { await Promise.all(writePromises); console.log("All disk writes completed"); } - if (savedFiles.length === 0) { throw createError({ statusCode: 400, message: "No file received" }); } - - await Promise.all( - savedFiles.map(async (file) => { - const StreamZipAsync = StreamZip.async; - const zip = new StreamZipAsync({ - file, - storeEntries: true, - }); - const metadataJson = await zip.entryData("metadata.json"); - const metadata = JSON.parse(metadataJson); - const { id } = metadata; - await addExtensionToConf(projectName, { - extensionId: id, - extensionPath: file, - }); - }), - ); - + await Promise.all(savedFiles.map(async (file) => await registerExtensionFile(projectName, file))); return { statusCode: CODE_201 }; }); diff --git a/server/api/microservice/app/set_app_base_url.post.js b/server/api/microservice/app/set_app_base_url.post.js new file mode 100644 index 000000000..fb8dc99f4 --- /dev/null +++ b/server/api/microservice/app/set_app_base_url.post.js @@ -0,0 +1,25 @@ +// Third party imports +import { createError, defineEventHandler, readBody } from "h3"; + +// Local imports +import { setAppBaseUrl } from "@geode/opengeodeweb-front/server/utils/server_config.js"; + +export default defineEventHandler(async (event) => { + try { + const { baseUrl } = await readBody(event); + if (!baseUrl) { + throw createError({ statusCode: 400, statusMessage: "baseUrl is required" }); + } + + await setAppBaseUrl(baseUrl); + console.log(`Updated APP_BASE_URL to ${baseUrl}`); + + return { statusCode: 200, baseUrl }; + } catch (error) { + console.log(error); + throw createError({ + statusCode: error.statusCode, + statusMessage: error.statusMessage ?? error.message, + }); + } +}); diff --git a/server/api/microservice/extensions/download.post.js b/server/api/microservice/extensions/download.post.js new file mode 100644 index 000000000..ea33ef4e0 --- /dev/null +++ b/server/api/microservice/extensions/download.post.js @@ -0,0 +1,32 @@ +// Node imports +import { promises as fs } from "node:fs"; + +// Third party imports +import { createError, defineEventHandler, readBody } from "h3"; + +// Local imports +import { + registerExtensionFile, + targetExtensionFilePath, +} from "@geode/opengeodeweb-front/server/utils/app_config.js"; + +export default defineEventHandler(async (event) => { + try { + const body = await readBody(event); + const { projectName, url, extensionFileName } = body; + console.log({ projectName, url, extensionFileName }); + const fileBuffer = await fetch(url).then((file) => file.arrayBuffer()); + const filePath = targetExtensionFilePath(projectName, extensionFileName); + await fs.writeFile(filePath, Buffer.from(fileBuffer)); + await registerExtensionFile(projectName, filePath); + return { + statusCode: 200, + }; + } catch (error) { + console.error("Error downloading extension:", error); + throw createError({ + statusCode: 500, + statusMessage: error.message, + }); + } +}); diff --git a/server/api/microservice/extensions/kill.post.js b/server/api/microservice/extensions/kill.post.js index b68d300d2..acf4246c8 100644 --- a/server/api/microservice/extensions/kill.post.js +++ b/server/api/microservice/extensions/kill.post.js @@ -9,9 +9,9 @@ import { getMicroserviceByName, killMicroservice, projectMicroservices, -} from "@geode/opengeodeweb-front/app/utils/local/cleanup.js"; -import { extensionFolderPath } from "@geode/opengeodeweb-front/app/utils/local/path.js"; -import { removeExtensionFromConf } from "@geode/opengeodeweb-front/app/utils/config.js"; +} from "@geode/opengeodeweb-front/server/utils/cleanup.js"; +import { extensionFolderPath } from "@geode/opengeodeweb-front/server/utils/path.js"; +import { removeExtensionFromConf } from "@geode/opengeodeweb-front/server/utils/app_config.js"; export default defineEventHandler(async (event) => { try { diff --git a/server/api/microservice/extensions/run.post.js b/server/api/microservice/extensions/run.post.js index 355d5206d..4297209c2 100644 --- a/server/api/microservice/extensions/run.post.js +++ b/server/api/microservice/extensions/run.post.js @@ -9,14 +9,14 @@ import { createError, defineEventHandler, readBody } from "h3"; import { addMicroserviceMetadatas, runBack, -} from "@geode/opengeodeweb-front/app/utils/local/microservices.js"; +} from "@geode/opengeodeweb-front/server/utils/microservices.js"; import { executableName, extensionFolderPath, extensionFrontendPath, -} from "@geode/opengeodeweb-front/app/utils/local/path.js"; -import { extensionsConf } from "@geode/opengeodeweb-front/app/utils/config.js"; -import { unzipFile } from "@geode/opengeodeweb-front/app/utils/server.js"; +} from "@geode/opengeodeweb-front/server/utils/path.js"; +import { extensionsConf } from "@geode/opengeodeweb-front/server/utils/app_config.js"; +import { unzipFile } from "@geode/opengeodeweb-front/server/utils/server.js"; export default defineEventHandler(async (event) => { try { diff --git a/server/api/serverless/run_cloud.js b/server/api/serverless/run_cloud.js index 04ca962fa..da60e4fa1 100644 --- a/server/api/serverless/run_cloud.js +++ b/server/api/serverless/run_cloud.js @@ -6,7 +6,7 @@ import { GoogleAuth } from "google-auth-library"; import { ServicesClient } from "@google-cloud/run"; // Local imports -import { artifactImage, requestConfig } from "@ogw_server/utils/cloud"; +import { artifactImage, requestConfig } from "@geode/opengeodeweb-front/server/utils/cloud"; export default defineEventHandler(async (event) => { try { diff --git a/app/utils/config.js b/server/utils/app_config.js similarity index 69% rename from app/utils/config.js rename to server/utils/app_config.js index 49a517166..dd6fe1b9e 100644 --- a/app/utils/config.js +++ b/server/utils/app_config.js @@ -4,6 +4,8 @@ import { unlink } from "node:fs"; // Third party imports import Conf from "conf"; +import StreamZip from "node-stream-zip"; +import sanitize from "sanitize-filename"; // Local imports @@ -18,6 +20,12 @@ function confFolderPath(projectName) { return path.dirname(projectConfig.path); } +function targetExtensionFilePath(projectName, filename) { + const safeFilename = sanitize(filename); + const targetPath = path.join(confFolderPath(projectName), safeFilename); + return targetPath; +} + function extensionsConf(projectName) { const projectConfig = projectConf(projectName); if (!projectConfig.has("extensions")) { @@ -51,11 +59,28 @@ function extensionPathFromConf(projectName, extensionId) { return projectConfig.get(`extensions.${extensionId}.path`); } +async function registerExtensionFile(projectName, file) { + const StreamZipAsync = StreamZip.async; + const zip = new StreamZipAsync({ + file, + storeEntries: true, + }); + const metadataJson = await zip.entryData("metadata.json"); + const metadata = JSON.parse(metadataJson); + const { id } = metadata; + addExtensionToConf(projectName, { + extensionId: id, + extensionPath: file, + }); +} + export { + addExtensionToConf, confFolderPath, - projectConf, + extensionPathFromConf, extensionsConf, - addExtensionToConf, + projectConf, + registerExtensionFile, removeExtensionFromConf, - extensionPathFromConf, + targetExtensionFilePath, }; diff --git a/app/utils/local/cleanup.js b/server/utils/cleanup.js similarity index 100% rename from app/utils/local/cleanup.js rename to server/utils/cleanup.js diff --git a/app/utils/local/microservices.js b/server/utils/microservices.js similarity index 100% rename from app/utils/local/microservices.js rename to server/utils/microservices.js diff --git a/app/utils/local/path.js b/server/utils/path.js similarity index 98% rename from app/utils/local/path.js rename to server/utils/path.js index ca5d30971..b34b76702 100644 --- a/app/utils/local/path.js +++ b/server/utils/path.js @@ -8,7 +8,7 @@ import { setTimeout } from "node:timers/promises"; import { v4 as uuidv4 } from "uuid"; // Local imports -import { appMode } from "./app_mode.js"; +import { appMode } from "@geode/opengeodeweb-front/shared/app_mode.js"; import { commandExistsSync } from "./scripts.js"; function findExecutableInDir(baseDir, execName, osExecutableName) { diff --git a/app/utils/local/scripts.js b/server/utils/scripts.js similarity index 95% rename from app/utils/local/scripts.js rename to server/utils/scripts.js index 06b903a09..b57f3b8a2 100644 --- a/app/utils/local/scripts.js +++ b/server/utils/scripts.js @@ -9,7 +9,8 @@ import readline from "node:readline"; import { getPort } from "get-port-please"; // Local imports -import { appMode } from "./app_mode.js"; +import { appMode } from "@geode/opengeodeweb-front/shared/app_mode.js"; +import { setAppBaseUrl } from "@geode/opengeodeweb-front/shared/scripts.js"; const BYTES_PER_KIBIBYTE = 1024; const MAX_ERROR_BUFFER_KIBIBYTES = 64; @@ -164,7 +165,9 @@ async function runBrowser(scriptName) { PORT: port, }, }); - return await waitNuxt(nuxtProcess); + await waitNuxt(nuxtProcess); + await setAppBaseUrl(`http://localhost:${port}`); + return port; } export { commandExistsSync, getAvailablePort, runBrowser, waitForReady }; diff --git a/app/utils/server.js b/server/utils/server.js similarity index 100% rename from app/utils/server.js rename to server/utils/server.js diff --git a/server/utils/server_config.js b/server/utils/server_config.js new file mode 100644 index 000000000..51fa0acc6 --- /dev/null +++ b/server/utils/server_config.js @@ -0,0 +1,38 @@ +import { useStorage } from "#imports"; + +function getConfig() { + return useStorage("config"); +} +function getAppBaseUrl() { + const config = getConfig(); + return config.getItem("APP_BASE_URL"); +} +function setAppBaseUrl(baseUrl) { + const config = getConfig(); + return config.setItem("APP_BASE_URL", baseUrl); +} +function getBackBaseUrl() { + const config = getConfig(); + return config.getItem("BACK_BASE_URL"); +} +function setBackBaseUrl(baseUrl) { + const config = getConfig(); + return config.setItem("BACK_BASE_URL", baseUrl); +} +function getViewerBaseUrl() { + const config = getConfig(); + return config.getItem("VIEWER_BASE_URL"); +} +function setViewerBaseUrl(baseUrl) { + const config = getConfig(); + return config.setItem("VIEWER_BASE_URL", baseUrl); +} + +export { + getAppBaseUrl, + setAppBaseUrl, + getBackBaseUrl, + getViewerBaseUrl, + setBackBaseUrl, + setViewerBaseUrl, +}; diff --git a/app/utils/local/app_mode.js b/shared/app_mode.js similarity index 100% rename from app/utils/local/app_mode.js rename to shared/app_mode.js diff --git a/shared/scripts.js b/shared/scripts.js new file mode 100644 index 000000000..c19bb4e65 --- /dev/null +++ b/shared/scripts.js @@ -0,0 +1,18 @@ +// Node imports + +// Third party imports + +// Local imports + +function setAppBaseUrl(baseUrl) { + console.log(`Setting APP_BASE_URL to ${baseUrl}`); + return fetch(`${baseUrl}/api/microservice/app/set_app_base_url`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ baseUrl }), + }); +} + +export { setAppBaseUrl }; diff --git a/tests/integration/setup.js b/tests/integration/setup.js index 6b8c4e3c8..aad2cf66b 100644 --- a/tests/integration/setup.js +++ b/tests/integration/setup.js @@ -8,10 +8,10 @@ import path from "node:path"; import { afterAll, beforeAll, expect, vi } from "vitest"; // Local imports -import { addMicroserviceMetadatas, runBack, runViewer } from "@ogw_front/utils/local/microservices"; -import { createPath, generateProjectFolderPath } from "@ogw_front/utils/local/path"; +import { addMicroserviceMetadatas, runBack, runViewer } from "@ogw_server/utils/microservices"; +import { createPath, generateProjectFolderPath } from "@ogw_server/utils/path"; import { Status } from "@ogw_front/utils/status"; -import { appMode } from "@ogw_front/utils/local/app_mode"; +import { appMode } from "@ogw_shared/app_mode"; import { importFile } from "@ogw_front/utils/import_workflow"; import { setupActivePinia } from "@ogw_tests/utils"; import { useBackStore } from "@ogw_front/stores/back"; diff --git a/tests/integration/stores/data_style/mesh/cells.nuxt.test.js b/tests/integration/stores/data_style/mesh/cells.nuxt.test.js index ed96c8b5e..c568e0123 100644 --- a/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +++ b/tests/integration/stores/data_style/mesh/cells.nuxt.test.js @@ -6,7 +6,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { getRGBPointsFromPreset } from "@ogw_front/utils/colormap"; import { isMeshCellsVertexAttributeValid } from "@ogw_internal/stores/data_style/mesh/cells/vertex"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; diff --git a/tests/integration/stores/data_style/mesh/edges.nuxt.test.js b/tests/integration/stores/data_style/mesh/edges.nuxt.test.js index 4faa6d380..0714920cb 100644 --- a/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +++ b/tests/integration/stores/data_style/mesh/edges.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { getRGBPointsFromPreset } from "@ogw_front/utils/colormap"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/mesh/index.nuxt.test.js b/tests/integration/stores/data_style/mesh/index.nuxt.test.js index c39aa9ff4..0ec5de82e 100644 --- a/tests/integration/stores/data_style/mesh/index.nuxt.test.js +++ b/tests/integration/stores/data_style/mesh/index.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/mesh/points.nuxt.test.js b/tests/integration/stores/data_style/mesh/points.nuxt.test.js index f3ac15190..0640ba060 100644 --- a/tests/integration/stores/data_style/mesh/points.nuxt.test.js +++ b/tests/integration/stores/data_style/mesh/points.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { getRGBPointsFromPreset } from "@ogw_front/utils/colormap"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js b/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js index 24221e2c3..74ff1e5cd 100644 --- a/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +++ b/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { getRGBPointsFromPreset } from "@ogw_front/utils/colormap"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js b/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js index 1c5156e32..65863ef37 100644 --- a/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +++ b/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { getRGBPointsFromPreset } from "@ogw_front/utils/colormap"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/model/blocks.nuxt.test.js b/tests/integration/stores/data_style/model/blocks.nuxt.test.js index 82c5f3f10..cd9e443a9 100644 --- a/tests/integration/stores/data_style/model/blocks.nuxt.test.js +++ b/tests/integration/stores/data_style/model/blocks.nuxt.test.js @@ -6,7 +6,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { useDataStore } from "@ogw_front/stores/data"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/model/corners.nuxt.test.js b/tests/integration/stores/data_style/model/corners.nuxt.test.js index 256b0f98b..9b4627947 100644 --- a/tests/integration/stores/data_style/model/corners.nuxt.test.js +++ b/tests/integration/stores/data_style/model/corners.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { useDataStore } from "@ogw_front/stores/data"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/model/edges.nuxt.test.js b/tests/integration/stores/data_style/model/edges.nuxt.test.js index 0ab9c78ed..1a144415b 100644 --- a/tests/integration/stores/data_style/model/edges.nuxt.test.js +++ b/tests/integration/stores/data_style/model/edges.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/model/index.nuxt.test.js b/tests/integration/stores/data_style/model/index.nuxt.test.js index d2d81a9c4..c2658c7ee 100644 --- a/tests/integration/stores/data_style/model/index.nuxt.test.js +++ b/tests/integration/stores/data_style/model/index.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { useDataStore } from "@ogw_front/stores/data"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/model/lines.nuxt.test.js b/tests/integration/stores/data_style/model/lines.nuxt.test.js index 25fe6576d..e79b45f7e 100644 --- a/tests/integration/stores/data_style/model/lines.nuxt.test.js +++ b/tests/integration/stores/data_style/model/lines.nuxt.test.js @@ -6,7 +6,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { useDataStore } from "@ogw_front/stores/data"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/model/points.nuxt.test.js b/tests/integration/stores/data_style/model/points.nuxt.test.js index 634804497..18805ba91 100644 --- a/tests/integration/stores/data_style/model/points.nuxt.test.js +++ b/tests/integration/stores/data_style/model/points.nuxt.test.js @@ -5,7 +5,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { useDataStyleStore } from "@ogw_front/stores/data_style"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/integration/stores/data_style/model/surfaces.nuxt.test.js b/tests/integration/stores/data_style/model/surfaces.nuxt.test.js index 9e676d1c1..7d3a4d3d3 100644 --- a/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +++ b/tests/integration/stores/data_style/model/surfaces.nuxt.test.js @@ -6,7 +6,7 @@ import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schem // Local imports import { beforeAllTimeout, setupIntegrationTests } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { getRGBPointsFromPreset } from "@ogw_front/utils/colormap"; import { isModelSurfacesPolygonAttributeValid } from "@ogw_internal/stores/data_style/model/surfaces/polygon"; import { isModelSurfacesVertexAttributeValid } from "@ogw_internal/stores/data_style/model/surfaces/vertex"; diff --git a/tests/integration/stores/viewer.nuxt.test.js b/tests/integration/stores/viewer.nuxt.test.js index 3b58f0d68..f91ab183d 100644 --- a/tests/integration/stores/viewer.nuxt.test.js +++ b/tests/integration/stores/viewer.nuxt.test.js @@ -7,7 +7,7 @@ import opengeodeweb_viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb // Local imports import { beforeAllTimeout, runMicroservices } from "@ogw_tests/integration/setup"; import { Status } from "@ogw_front/utils/status"; -import { cleanupBackend } from "@ogw_front/utils/local/cleanup"; +import { cleanupBackend } from "@ogw_server/utils/cleanup"; import { setupActivePinia } from "@ogw_tests/utils"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/unit/composables/project_manager.nuxt.test.js b/tests/unit/composables/project_manager.nuxt.test.js index c35b5a7ca..d5d3dacb5 100644 --- a/tests/unit/composables/project_manager.nuxt.test.js +++ b/tests/unit/composables/project_manager.nuxt.test.js @@ -5,7 +5,7 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import { exportProject, importProject } from "@ogw_front/composables/project_manager"; -import { appMode } from "@ogw_front/utils/local/app_mode"; +import { appMode } from "@ogw_shared/app_mode"; import { setupActivePinia } from "@ogw_tests/utils"; // Constants diff --git a/tests/unit/stores/back.nuxt.test.js b/tests/unit/stores/back.nuxt.test.js index 233b676e6..c348e7d9c 100644 --- a/tests/unit/stores/back.nuxt.test.js +++ b/tests/unit/stores/back.nuxt.test.js @@ -5,7 +5,7 @@ import { registerEndpoint } from "@nuxt/test-utils/runtime"; // Local imports import { Status } from "@ogw_front/utils/status"; -import { appMode } from "@ogw_front/utils/local/app_mode"; +import { appMode } from "@ogw_shared/app_mode"; import { setupActivePinia } from "@ogw_tests/utils"; import { useBackStore } from "@ogw_front/stores/back"; import { useInfraStore } from "@ogw_front/stores/infra"; diff --git a/tests/unit/stores/cloud.nuxt.test.js b/tests/unit/stores/cloud.nuxt.test.js index 982392d72..a51787829 100644 --- a/tests/unit/stores/cloud.nuxt.test.js +++ b/tests/unit/stores/cloud.nuxt.test.js @@ -38,16 +38,21 @@ describe("cloud store", () => { const cloudStore = useCloudStore(); const feedbackStore = useFeedbackStore(); - registerEndpoint("/api/serverless/run_cloud", { + registerEndpoint("https://localhost:443/server/api/serverless/run_cloud", { method: "POST", handler: postFakeCall, }); - postFakeCall.mockReturnValue({ - url: "http://test.com", + registerEndpoint("https://test.com:443/server/api/microservice/app/set_app_base_url", { + method: "POST", + handler: () => console.log("coucou from endpoint"), }); - await cloudStore.launch("", "", false); + postFakeCall.mockReturnValue({ + url: "test.com", + }); + const email = "noreply@example.com"; + await cloudStore.launch(email); expect(cloudStore.status).toBe(Status.CONNECTED); expect(feedbackStore.server_error).toBe(false); @@ -58,7 +63,7 @@ describe("cloud store", () => { const cloudStore = useCloudStore(); const feedbackStore = useFeedbackStore(); - registerEndpoint("/api/serverless/run_cloud", { + registerEndpoint("https://localhost:443/server/api/serverless/run_cloud", { method: "POST", handler: postFakeCall, }); @@ -69,8 +74,8 @@ describe("cloud store", () => { statusMessage: "Internal Server Error", }); }); - - await expect(cloudStore.launch("", "", false)).rejects.toThrow("500 Internal Server Error"); + const email = "noreply@example.com"; + await expect(cloudStore.launch(email)).rejects.toThrow("500 Internal Server Error"); expect(cloudStore.status).toBe(Status.NOT_CONNECTED); expect(feedbackStore.server_error).toBe(true); diff --git a/tests/unit/stores/infra.nuxt.test.js b/tests/unit/stores/infra.nuxt.test.js index 26e407ce3..744ac5c2d 100644 --- a/tests/unit/stores/infra.nuxt.test.js +++ b/tests/unit/stores/infra.nuxt.test.js @@ -4,7 +4,7 @@ import { registerEndpoint } from "@nuxt/test-utils/runtime"; // Local imports import { Status } from "@ogw_front/utils/status"; -import { appMode } from "@ogw_front/utils/local/app_mode"; +import { appMode } from "@ogw_shared/app_mode"; import { setupActivePinia } from "@ogw_tests/utils"; import { useBackStore } from "@ogw_front/stores/back"; import { useInfraStore } from "@ogw_front/stores/infra"; @@ -291,7 +291,7 @@ describe("infra store", () => { infraStore.app_mode = appMode.CLOUD; const url = "test.com"; - registerEndpoint("/api/serverless/run_cloud", { + registerEndpoint("https://localhost:443/server/api/serverless/run_cloud", { method: "POST", handler: () => ({ url }), }); diff --git a/tests/unit/stores/viewer.nuxt.test.js b/tests/unit/stores/viewer.nuxt.test.js index fd0204e5c..06db76893 100644 --- a/tests/unit/stores/viewer.nuxt.test.js +++ b/tests/unit/stores/viewer.nuxt.test.js @@ -3,7 +3,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, expectTypeOf, test, import { WebSocket } from "ws"; // Local imports -import { appMode } from "@ogw_front/utils/local/app_mode"; +import { appMode } from "@ogw_shared/app_mode"; import { setupActivePinia } from "@ogw_tests/utils"; import { useInfraStore } from "@ogw_front/stores/infra"; import { useViewerStore } from "@ogw_front/stores/viewer"; diff --git a/tests/vitest.config.js b/tests/vitest.config.js index 4dd35e974..9daf0e533 100644 --- a/tests/vitest.config.js +++ b/tests/vitest.config.js @@ -15,13 +15,13 @@ const CI_WORKERS = 2; const globalRetry = process.env.CI ? RETRIES : DEFAULT_RETRY; const maxWorkers = process.env.CI ? CI_WORKERS : 3; +const aliases = { + "@ogw_tests": path.resolve(__dirname, "."), + "@geode/opengeodeweb-front/shared": path.resolve(__dirname, "..", "shared"), +}; + // oxlint-disable-next-line import/no-default-export export default defineConfig({ - resolve: { - alias: { - "@ogw_tests": path.resolve(__dirname, "."), - }, - }, test: { setupFiles: [path.resolve(__dirname, "./setup_indexeddb.js")], projects: [ @@ -32,6 +32,7 @@ export default defineConfig({ include: ["tests/unit/**/*.test.js"], globals: true, environment: "nuxt", + alias: aliases, testTimeout: TIMEOUTS.unit, setupFiles: [path.resolve(__dirname, "./setup_indexeddb.js")], server: { @@ -49,6 +50,7 @@ export default defineConfig({ include: ["tests/integration/**/*.test.js"], globals: true, environment: "nuxt", + alias: aliases, maxWorkers, testTimeout: TIMEOUTS.integration, setupFiles: [path.resolve(__dirname, "./setup_indexeddb.js")],