console: use URI-safe encoding for state
Some checks failed
CI / 🤖 Check dependabot status (push) Has been cancelled
CI / 🐧 Test on Linux (${{ github.ref_type == 'tag' }}, misc) (push) Has been cancelled
CI / 🐧 Test on Linux (coverage) (push) Has been cancelled
CI / 🐧 Test on Linux (regular) (push) Has been cancelled
CI / ❄️ Build on Nix (push) Has been cancelled
CI / 🍏 Build and test on macOS (push) Has been cancelled
CI / 🧪 End-to-end testing (push) Has been cancelled
CI / 🔍 Upload code coverage (push) Has been cancelled
CI / 🔬 Test only Go (push) Has been cancelled
CI / 🔬 Test only JS (${{ needs.dependabot.outputs.package-ecosystem }}, 20) (push) Has been cancelled
CI / 🔬 Test only JS (${{ needs.dependabot.outputs.package-ecosystem }}, 22) (push) Has been cancelled
CI / 🔬 Test only JS (${{ needs.dependabot.outputs.package-ecosystem }}, 24) (push) Has been cancelled
CI / ⚖️ Check licenses (push) Has been cancelled
CI / 🐋 Build Docker images (push) Has been cancelled
CI / 🐋 Tag Docker images (push) Has been cancelled
CI / 🚀 Publish release (push) Has been cancelled
Update Nix dependency hashes / Update dependency hashes (push) Has been cancelled

"/" 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
This commit is contained in:
François HORTA
2025-12-09 12:04:29 +01:00
committed by Vincent Bernat
parent 2783bdea19
commit be802be39c

View File

@@ -89,13 +89,23 @@ const state = ref<ModelType>(null);
// Load data from URL // Load data from URL
const route = useRoute(); const route = useRoute();
const router = useRouter(); 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 => { const decodeState = (serialized: string | undefined): ModelType => {
try { try {
if (!serialized) { if (!serialized) {
console.debug("no state"); console.debug("no state");
return null; return null;
} }
const unserialized = LZString.decompressFromBase64(serialized); const unserialized = unserializeState(serialized);
if (!unserialized) { if (!unserialized) {
console.debug("empty state"); console.debug("empty state");
return null; return null;
@@ -108,9 +118,10 @@ const decodeState = (serialized: string | undefined): ModelType => {
}; };
const encodeState = (state: ModelType) => { const encodeState = (state: ModelType) => {
if (state === null) return ""; if (state === null) return "";
return LZString.compressToBase64( const serialized = LZString.compressToEncodedURIComponent(
JSON.stringify(state, Object.keys(state).sort()), JSON.stringify(state, Object.keys(state).sort()),
); );
return statePrefixV1 + serialized;
}; };
watch( watch(
() => props.routeState, () => props.routeState,