From cd183e14c8c5ab747a9eace0e6562f844cbb29e6 Mon Sep 17 00:00:00 2001 From: Marcel Hellkamp Date: Thu, 27 Jul 2023 17:12:56 +0200 Subject: [PATCH] Allow overriding site config URL via query parameter Remodeled config dialog a bit and added a way to fetch site config from any url via ?load=URL parameter. --- src/components/ConfigModal.vue | 63 ++++++++++++++++----------------- src/config.ts | 64 +++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/src/components/ConfigModal.vue b/src/components/ConfigModal.vue index 28c47b2..f506d26 100644 --- a/src/components/ConfigModal.vue +++ b/src/components/ConfigModal.vue @@ -2,8 +2,8 @@ import { sanatizeConfig, isServer, isLanguage, toQuery } from '@/config'; import { computed, ref } from 'vue'; import { arrayUnique } from '@/utils'; -import { useClipboard } from '@vueuse/core' -import {type Config} from '@/types'; +import { type Config } from '@/types'; +import { siteConfigParam } from '@/config' const emit = defineEmits(['update:modelValue']) const modalDom = ref(null) @@ -96,10 +96,15 @@ const sourceCount = computed(() => { return c; }) -const clip = useClipboard() +const rateLimitRisk = computed(() => { + return sourceCount.value / config.value.interval > 1 +}) -const fullConfig = computed(() => { - return JSON.stringify(config.value) +const saveConfigLink = computed(() => { + const json = JSON.stringify(config.value, undefined, 2) + const bytes = new TextEncoder().encode(json) + const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join(""); + return "data:application/json;charset=utf-8;base64," + btoa(binString); }) const fullUrl = computed(() => { @@ -312,54 +317,45 @@ const onSubmit = () => {
- +
- -
Update interval in seconds.
+ +
Number of seconds to wait between updates.
- +
- -
Fetch this many new posts per request.
+ +
Limit number of results per hashtag, account or timeline.
-
Save config
+
Download config
-
- - +
+ You can download the + current configuration as a JSON file and either upload it as wall-config.json + to your own self-hosted Fediwall, or host it somewhere else and load it via the + ?{{ siteConfigParam }}=URL query parameter.
-
Bookmark or share this Fediwall link.
-
- - -
-
Only useful if you plan to self-host Fediwall on your own domain.
-
- - - @@ -371,5 +367,6 @@ const onSubmit = () => { .form-label, h6 { font-weight: bolder; -} +} + diff --git a/src/config.ts b/src/config.ts index 5871d80..051bc94 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,8 +4,9 @@ import { fallbackConfig, siteConfigUrl } from "@/defaults"; import { type Config } from '@/types'; - -let siteConfig: Config | null = null; +export const siteConfigParam = "load" +let siteConfig: Config | undefined; +let siteConfigSource: string|undefined = undefined; const themes = ["dark", "light", "auto"]; const boolYes = ["", "y", "yes", "true"]; @@ -183,10 +184,13 @@ export function fromQuery(query: string): Config { return sanatizeConfig(config); } -export function toQuery(config: Config): string { +export function toQuery(config: Config, userConfig?: string): string { const params = new URLSearchParams(); const defaults = siteConfig || fallbackConfig; + if (siteConfigSource && siteConfigSource !== siteConfigUrl) + params.set(siteConfigParam, siteConfigSource) + for (const { names, to } of parameterDefinitions) { const value = to(config) if (value !== to(defaults)) @@ -231,6 +235,15 @@ export function sanatizeConfig(config: any): Config { return choices.includes(value) ? value : fallback; } + // Migrate old configuration within same minor release + if (isString(config.server)) { + console.warn("DEPRECATED: Config parameter 'server' is now an array and called 'servers'."); + (config.servers ??= []).push(config.server); + } + if (isString(config.info)) + config.showinfo = config.info == "top" + + const fallback = siteConfig || fallbackConfig; const result: Partial = {} @@ -268,35 +281,28 @@ export function sanatizeConfig(config: any): Config { return result as Config; } -async function loadSideConfig() { - let config; - - try { - config = await (await fetch(siteConfigUrl)).json() || {}; - } catch (e) { - console.warn("Site config failed to load, falling back to hard-coded defaults!") - return; - } - - // Migrate old configuration within same minor release - if (isString(config.server)) { - console.warn("DEPRECATED: Config parameter 'server' is now an array and called 'servers'."); - (config.servers ??= []).push(config.server); - } - if (isString(config.info)) - config.showinfo = config.info == "top" - - return sanatizeConfig(config) -} - export async function loadConfig() { + const params = new URLSearchParams(window.location.search); + const loadUrl = params.get(siteConfigParam)?.trim() + + const loadJson = async (url: string) => { + try { + const rs = await fetch(url, {cache: "reload", }) + if(!rs.ok) throw new Error(`HTTP error! Status: ${rs.status}`); + siteConfig = sanatizeConfig(await rs.json() || {}); + siteConfigSource = url + } catch (e) { + console.warn(`Failed to load (or parse) [${url}], falling back to defaults.`) + return; + } + } + + if (!siteConfig && loadUrl) + await loadJson(loadUrl) if (!siteConfig && siteConfigUrl) - siteConfig = await loadSideConfig() || null + await loadJson(siteConfigUrl) if (!siteConfig) siteConfig = sanatizeConfig(deepClone(fallbackConfig)) - if (window.location.search) - return fromQuery(window.location.search) - - return deepClone({ ...siteConfig }) + return fromQuery(window.location.search) } \ No newline at end of file