From be802be39cf8cc9c4327f15aa0c62461566cfd4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20HORTA?= Date: Tue, 9 Dec 2025 12:04:29 +0100 Subject: [PATCH] console: use URI-safe encoding for state "/" is by default in the base64 charset, Vue(?) URL-encodes it to %2F but the HTTP spec expects / and %2F to be treated the same way. Curiously, traefik 3.6.4 decided to break this behavior and rejects URLs with %2F. lz-string provides a URI-safe encoding scheme (which just maps base64 to a URI-safe charset), let's use that by default and try to decode from base64 otherwise, to preserve backwards-compatibility --- console/frontend/src/views/VisualizePage.vue | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/console/frontend/src/views/VisualizePage.vue b/console/frontend/src/views/VisualizePage.vue index f41896f9..9066477e 100644 --- a/console/frontend/src/views/VisualizePage.vue +++ b/console/frontend/src/views/VisualizePage.vue @@ -89,13 +89,23 @@ const state = ref(null); // Load data from URL const route = useRoute(); const router = useRouter(); +const statePrefixV1 = "v1-"; +const unserializeState = (serialized: string): string | undefined => { + if (serialized.startsWith(statePrefixV1)) { + return LZString.decompressFromEncodedURIComponent( + serialized.substring(statePrefixV1.length), + ); + } + // URL generated by the previous implementation + return LZString.decompressFromBase64(serialized); +}; const decodeState = (serialized: string | undefined): ModelType => { try { if (!serialized) { console.debug("no state"); return null; } - const unserialized = LZString.decompressFromBase64(serialized); + const unserialized = unserializeState(serialized); if (!unserialized) { console.debug("empty state"); return null; @@ -108,9 +118,10 @@ const decodeState = (serialized: string | undefined): ModelType => { }; const encodeState = (state: ModelType) => { if (state === null) return ""; - return LZString.compressToBase64( + const serialized = LZString.compressToEncodedURIComponent( JSON.stringify(state, Object.keys(state).sort()), ); + return statePrefixV1 + serialized; }; watch( () => props.routeState,