diff --git a/.github/workflows/reusable-build-deploy-api-editor-docs.yml b/.github/workflows/reusable-build-deploy-api-editor-docs.yml
index 0ee2b9a0..19fd5e5a 100644
--- a/.github/workflows/reusable-build-deploy-api-editor-docs.yml
+++ b/.github/workflows/reusable-build-deploy-api-editor-docs.yml
@@ -51,6 +51,7 @@ jobs:
uses: ./.github/workflows/reusable-build-docs-application.yml
with:
artifact-name: docs
+ build-name: ${{ inputs.build-name }}
api-url: https://${{ inputs.build-name }}.facturxdotnet.org/api
editor-url: https://${{ inputs.build-name }}.facturxdotnet.org/editor
version: ${{ needs.compute-version.outputs.version }}
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index 237fb916..af841d0f 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -8,6 +8,11 @@ on:
type: string
required: true
+ build-name:
+ description: The name of the build, defaults to 'build'
+ type: string
+ required: true
+
api-url:
description: The URL at which the FacturX.NET API will be served
type: string
@@ -198,10 +203,20 @@ jobs:
echo
echo
- - name: Set version number
+ - name: Write env.json
run: |
- sed -i -e 's|{VERSION}|${{ inputs.version }}|g' -e 's|{API-URL}|${{ inputs.api-url }}|g' -e 's|{EDITOR-URL}|${{ inputs.editor-url }}|g' docs/.vitepress/config.mts
- sed -i -e 's|{VERSION}|${{ inputs.version }}|g' -e 's|{API-URL}|${{ inputs.api-url }}|g' -e 's|{EDITOR-URL}|${{ inputs.editor-url }}|g' docs/src/index.md
+ echo '{' > docs/src/env.json
+ echo ' "buildName": "${{ inputs.build-name }}",' >> docs/src/env.json
+ echo ' "version": "${{ inputs.version }}",' >> docs/src/env.json
+ echo ' "editor": {' >> docs/src/env.json
+ echo ' "url": "${{ inputs.editor-url }}"' >> docs/src/env.json
+ echo ' },' >> docs/src/env.json
+ echo ' "api": {' >> docs/src/env.json
+ echo ' "url": "${{ inputs.api-url }}"' >> docs/src/env.json
+ echo ' }' >> docs/src/env.json
+ echo '}' >> docs/src/env.json
+
+ cat docs/src/env.json
- name: Build
run: cd docs; npm run build -- --base /docs/ --outDir dist/
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 79ffeab5..d4505fd4 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -1,7 +1,9 @@
-import {defineConfigWithTheme} from "vitepress"; // https://vitepress.dev/reference/site-config
-import {useSidebar} from "vitepress-openapi";
-import {getSidebar} from "vitepress-plugin-auto-sidebar";
-import spec from "../src/assets/facturxdotnet.openapi.json" with {type: "json"};
+import { defineConfigWithTheme } from "vitepress"; // https://vitepress.dev/reference/site-config
+import { useSidebar } from "vitepress-openapi";
+import { getSidebar } from "vitepress-plugin-auto-sidebar";
+import semver from "semver";
+import env from "../src/env.json" with { type: "json" };
+import spec from "../src/assets/facturxdotnet.openapi.json" with { type: "json" };
const specSidebar = useSidebar({
spec,
@@ -23,7 +25,7 @@ let cliItems =
collapsed: false,
collapsible: false,
})[0]?.items ?? [];
-cliItems = cliItems.map(item => ({
+cliItems = cliItems.map((item) => ({
...item,
text: item.text.toLowerCase() === "subcommands" ? "Sub Commands" : item.text,
}));
@@ -37,9 +39,26 @@ let apiReferenceItems =
collapsible: true,
})[0]?.items ?? [];
apiReferenceItems = apiReferenceItems.filter(
- i => i.items !== undefined && i.items.length > 0,
+ (i) => i.items !== undefined && i.items.length > 0,
);
+const semVersion = semver.valid(env.version)
+ ? semver.parse(env.version)
+ : { version: env.version, build: [] };
+
+const footerText = [`${env.buildName} ยท v${semVersion.version}`];
+if (semVersion.build.length) {
+ footerText.push(`ID: ${semVersion.build.join(".")}`);
+}
+
+const sidebarFooter = [
+ {
+ text: ``,
+ },
+];
+
// https://vitepress.dev/reference/site-config
export default defineConfigWithTheme({
srcDir: "./src",
@@ -71,11 +90,11 @@ export default defineConfigWithTheme({
items: [
{
text: "Try the Editor",
- link: "{EDITOR-URL}",
+ link: env.editor.url,
},
{
text: "Try the API",
- link: "{API-URL}",
+ link: env.api.url,
},
],
},
@@ -110,7 +129,7 @@ export default defineConfigWithTheme({
text: "Use cases",
items: [
{
- text: "Generation",
+ text: `Generation`,
items: [
{
text: "Generate a Factur-X document",
@@ -123,7 +142,7 @@ export default defineConfigWithTheme({
],
},
{
- text: "Validation",
+ text: `Validation`,
items: [
{
text: "Validate a Factur-X document",
@@ -136,7 +155,7 @@ export default defineConfigWithTheme({
],
},
{
- text: "Extraction",
+ text: `Extraction`,
items: [
{
text: "Extract Cross-Industry Invoice data",
@@ -151,9 +170,7 @@ export default defineConfigWithTheme({
text: "About",
link: "/guides/about",
},
- {
- text: "",
- },
+ ...sidebarFooter,
],
"/openapi-specification/": [
{
@@ -161,22 +178,13 @@ export default defineConfigWithTheme({
link: "/openapi-specification/introduction",
},
...specSidebar.generateSidebarGroups(),
- {
- text: "",
- },
- ],
- "/cli/": [
- ...cliItems,
- {
- text: "",
- },
+ ...sidebarFooter,
],
+ "/cli/": [...cliItems, ...sidebarFooter],
"/api-reference/": [
{ text: "Index", link: "/api-reference/index" },
...apiReferenceItems,
- {
- text: "",
- },
+ ...sidebarFooter,
],
},
@@ -209,4 +217,55 @@ export default defineConfigWithTheme({
noExternal: ["vitepress-plugin-nprogress"],
},
},
+
+ transformPageData: (pageData) => {
+ const result = { ...pageData };
+ result.frontmatter = expandEnvInRecord(pageData.frontmatter);
+ return result;
+ },
});
+
+function expandEnv(value: unknown): unknown {
+ if (typeof value === "string" || value instanceof String) {
+ return expandEnvInString(value);
+ } else if (value.constructor.name == "Array") {
+ return expandEnvInArray(value);
+ } else {
+ return expandEnvInRecord(value);
+ }
+}
+
+function expandEnvInArray(array: unknown[]): unknown[] {
+ return array.map(expandEnv);
+}
+
+function expandEnvInRecord(
+ record: Record,
+): Record {
+ const result = {};
+
+ for (const key of Object.keys(record)) {
+ result[key] = expandEnv(record[key]);
+ }
+
+ return result;
+}
+
+function expandEnvInString(value: string): string {
+ return value.replace(/\$env\.([^ ]*)/g, (_, varName) => getEnvValue(varName));
+}
+
+function getEnvValue(varName: string): string {
+ const fragments = varName.split(".");
+ let result = env;
+
+ for (const fragment of fragments) {
+ if (!Object.keys(result).includes(fragment)) {
+ return "";
+ }
+
+ result = result[fragment];
+ }
+
+ return result;
+}
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
index f4c7db15..110b3b18 100644
--- a/docs/.vitepress/theme/custom.css
+++ b/docs/.vitepress/theme/custom.css
@@ -10,6 +10,16 @@
--vp-c-brand-3: #512bd4;
}
+.VPSidebarItem .item {
+ cursor: unset;
+}
+
+p:has(.sidebar-footer) {
+ margin-top: 24px;
+ padding: 0 !important;
+ line-height: unset !important;
+}
+
.sidebar-footer {
font-size: 0.75rem;
color: var(--vp-c-text-3);
diff --git a/docs/package-lock.json b/docs/package-lock.json
index 23635727..cf529657 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -5,6 +5,7 @@
"packages": {
"": {
"dependencies": {
+ "semver": "^7.7.1",
"vitepress-openapi": "^0.0.3-alpha.76",
"vitepress-plugin-auto-sidebar": "^1.3.5",
"vitepress-plugin-image-viewer": "^1.1.6"
@@ -2715,7 +2716,6 @@
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
- "dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
diff --git a/docs/package.json b/docs/package.json
index 67975c29..d26ecc87 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -11,6 +11,7 @@
"vitepress-plugin-nprogress": "^0.0.4"
},
"dependencies": {
+ "semver": "^7.7.1",
"vitepress-openapi": "^0.0.3-alpha.76",
"vitepress-plugin-auto-sidebar": "^1.3.5",
"vitepress-plugin-image-viewer": "^1.1.6"
diff --git a/docs/src/env.data.ts b/docs/src/env.data.ts
new file mode 100644
index 00000000..9cdb8d59
--- /dev/null
+++ b/docs/src/env.data.ts
@@ -0,0 +1,21 @@
+import fs from "node:fs";
+
+export default {
+ watch: ["./env.json"],
+ load(watchedFile) {
+ const file = watchedFile[0];
+ const fileContent = fs.readFileSync(file, "utf-8");
+ return JSON.parse(fileContent) as Env;
+ },
+};
+
+interface Env {
+ buildName: string;
+ version: string;
+ editor: {
+ url: string;
+ };
+ api: {
+ url: string;
+ };
+}
diff --git a/docs/src/env.json b/docs/src/env.json
new file mode 100644
index 00000000..bc44b21f
--- /dev/null
+++ b/docs/src/env.json
@@ -0,0 +1,10 @@
+{
+ "buildName": "dev",
+ "version": "~dev",
+ "editor": {
+ "url": "http://localhost:4200"
+ },
+ "api": {
+ "url": "http://localhost:5295"
+ }
+}
diff --git a/docs/src/index.md b/docs/src/index.md
index ed7c2d92..f2387ff4 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -28,7 +28,7 @@ features:
alt: 'WebSite'
}
details: Create, view, and edit Factur-X documents directly in your browser with our user-friendly editor.
- link: {EDITOR-URL}
+ link: $env.editor.url
linkText: Try it live
- title: API
icon: {
@@ -37,7 +37,7 @@ features:
alt: 'API'
}
details: Programmatically generate, read, and validate Factur-X documents through our powerful API.
- link: {API-URL}
+ link: $env.api.url
linkText: Try it live
- title: .NET library and tool
icon: {