diff --git a/api/src/app.ts b/api/src/app.ts index 967776a..e6dfcf0 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -1,7 +1,7 @@ import { resolve } from 'node:path' import express from 'express' import { errorHandler, createSiteMiddleware, createSpaMiddleware } from '@data-fair/lib-express' -import config, { uiConfig } from '#config' +import config, { uiConfig, siteResourcesHtml } from '#config' import adminRouter from './admin/router.ts' export const app = express() @@ -16,7 +16,10 @@ app.use(express.urlencoded({ extended: true })) app.use('/api/admin', adminRouter) if (config.serveUi) { - app.use(await createSpaMiddleware(resolve(import.meta.dirname, '../../ui/dist'), uiConfig)) + app.use(await createSpaMiddleware(resolve(import.meta.dirname, '../../ui/dist'), uiConfig, { + extraHtmlTemplateParams: { SITE_RESOURCES: siteResourcesHtml }, + privateDirectoryUrl: config.useSimpleDirectory ? config.privateDirectoryUrl : undefined + })) } app.use(errorHandler) diff --git a/api/src/config.ts b/api/src/config.ts index cc95e4f..4edf3df 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -14,3 +14,10 @@ export const uiConfig = { useSimpleDirectory: apiConfig.useSimpleDirectory } export type UiConfig = typeof uiConfig + +// site theme and public info served by simple-directory, absent in standalone mode +// the hash placeholders are filled by createSpaMiddleware (privateDirectoryUrl option) +export const siteResourcesHtml = apiConfig.useSimpleDirectory + ? ` + ` + : '' diff --git a/ui/index.html b/ui/index.html index 38faf6f..bd29ac2 100644 --- a/ui/index.html +++ b/ui/index.html @@ -3,7 +3,7 @@
- + {SITE_RESOURCES} diff --git a/ui/src/components/home.vue b/ui/src/components/home.vue index e05f34a..ab0d627 100644 --- a/ui/src/components/home.vue +++ b/ui/src/components/home.vue @@ -208,10 +208,10 @@ marked.setOptions({ gfm: true }) -const { info, externalDocs, servers, schemas } = defineProps<{ +const { info, externalDocs = undefined, servers = undefined, schemas } = defineProps<{ info: OpenAPISpecs['info'], - externalDocs: OpenAPISpecs['externalDocs'], - servers: OpenAPISpecs['servers'], + externalDocs?: OpenAPISpecs['externalDocs'], + servers?: OpenAPISpecs['servers'], schemas: Components['schemas'] | undefined }>() diff --git a/ui/src/main.ts b/ui/src/main.ts index 0708b5f..4c72c0b 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -26,7 +26,7 @@ import DOMPurify from 'dompurify' let session, localeDayjs, i18n if ($uiConfig.useSimpleDirectory) { // Don't integrate session in standalone mode - session = await createSession({ directoryUrl: $sitePath + '/simple-directory', siteInfo: true }) + session = await createSession({ directoryUrl: $sitePath + '/simple-directory' }) localeDayjs = createLocaleDayjs(session.state.lang) i18n = createI18n({ locale: session.state.lang }) Object.assign(vuetifyOptions, vuetifySessionOptions(session)) diff --git a/ui/src/pages/index.vue b/ui/src/pages/index.vue index 9be5d80..65a3354 100644 --- a/ui/src/pages/index.vue +++ b/ui/src/pages/index.vue @@ -141,15 +141,15 @@ const fullOperation = computed<{ return null }) -watch(route.query, () => { - derefDoc.value = undefined - errorMessage.value = null - +/* + * Resolve the doc URL from the query + * -> watched by value, so that operation/params changes in the query do not refetch the doc + */ +const target = computed<{ url?: string, error?: string }>(() => { if (route.query.urlType) { // Check url type if (!Object.keys($uiConfig.allowedUrls).includes(route.query.urlType as string)) { - errorMessage.value = t('errors.invalidUrlType') - return + return { error: t('errors.invalidUrlType') } } const template = $uiConfig.allowedUrls[route.query.urlType as string] @@ -159,18 +159,24 @@ watch(route.query, () => { const requiredParams = (template.match(/{[^}]+}/g) || []).map((param) => param.slice(1, -1)) const missingParams = requiredParams.filter((param) => !queryParams[param]) if (missingParams.length > 0) { - errorMessage.value = t('errors.missingParams', { params: missingParams.join(', ') }) - return + return { error: t('errors.missingParams', { params: missingParams.join(', ') }) } } // Replace the template params with the query params - url.value = urlTemplate.parseTemplate(template).expand(queryParams) + return { url: urlTemplate.parseTemplate(template).expand(queryParams) } } else if ($uiConfig.defaultUrl) { - url.value = $uiConfig.defaultUrl + return { url: $uiConfig.defaultUrl } } else { - errorMessage.value = t('errors.missingUrl') + return { error: t('errors.missingUrl') } } +}) + +watch([() => target.value.url, () => target.value.error], ([newUrl, error]) => { + derefDoc.value = undefined + errorMessage.value = error ?? null + if (!newUrl) return + url.value = newUrl urlFetch.refresh() }, { immediate: true }) diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 52c3963..3c1d341 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -70,8 +70,8 @@ export default defineConfig(({ command }) => ({ async transformIndexHtml (html) { // in production this injection will be performed by an express middleware if (process.env.NODE_ENV !== 'development') return html - const { uiConfig } = await import('../api/src/config.ts') - return microTemplate(html, { SITE_PATH: '', UI_CONFIG: JSON.stringify(uiConfig) }) + const { uiConfig, siteResourcesHtml } = await import('../api/src/config.ts') + return microTemplate(html, { SITE_RESOURCES: siteResourcesHtml, SITE_PATH: '', UI_CONFIG: JSON.stringify(uiConfig), THEME_CSS_HASH: '', PUBLIC_SITE_INFO_HASH: '' }) } } ],