forked from extern/fediwall
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.
This commit is contained in:
parent
9a00596ff1
commit
cd183e14c8
@ -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 { 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 = () => {
|
||||
<div class="tab-pane" id="ctab-advanced" aria-labelledby="btab-advanced" role="tabpanel">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="interval" class="form-label">Interval</label>
|
||||
<label for="edit-interval" class="form-label">Update interval</label>
|
||||
<div class="ms-5">
|
||||
<input type="text" class="form-control" name="interval" v-model.lazy="formInterval">
|
||||
<div class="form-text">Update interval in seconds.</div>
|
||||
<input type="text" class="form-control" name="edit-interval" v-model.lazy="formInterval">
|
||||
<div class="form-text">Number of seconds to wait between updates.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="limit" class="form-label">Limit results per source</label>
|
||||
<label for="edit-limit" class="form-label">Results per source</label>
|
||||
<div class="ms-5">
|
||||
<input type="text" class="form-control" name="limit" v-model.lazy="formLimit">
|
||||
<div class="form-text">Fetch this many new posts per request.</div>
|
||||
<input type="text" class="form-control" name="edit-limit" v-model.lazy="formLimit">
|
||||
<div class="form-text">Limit number of results per hashtag, account or timeline.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<h6>Save config</h6>
|
||||
<h6>Download config</h6>
|
||||
<div class="ms-5">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" readonly :value="fullUrl">
|
||||
<button v-if="clip.isSupported" class="btn btn-outline-secondary" type="button"
|
||||
@click.prevent="clip.copy(fullUrl)">Copy</button>
|
||||
</div>
|
||||
<div class="form-text">Bookmark or share this <a :href="fullUrl" target="_blank">Fediwall link</a>.</div>
|
||||
<div class="mt-3 input-group">
|
||||
<input type="text" class="form-control" readonly :value="fullConfig">
|
||||
<button v-if="clip.isSupported" class="btn btn-outline-secondary" type="button"
|
||||
@click.prevent="clip.copy(fullUrl)">Copy</button>
|
||||
</div>
|
||||
<div class="form-text">Only useful if you plan to self-host Fediwall on your own domain.</div>
|
||||
<div class="form-text">
|
||||
You can <a :href="saveConfigLink" target="_blank" download="wall-config.json">download</a> the
|
||||
current configuration as a JSON file and either upload it as <code>wall-config.json</code>
|
||||
to your own self-hosted Fediwall, or host it somewhere else and load it via the
|
||||
<code>?{{ siteConfigParam }}=URL</code> query parameter.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div v-if="sourceCount / config.interval > 1" class="alert alert-warning mt-5" role="alert">
|
||||
Checking {{ sourceCount }} sources every {{ formInterval }} seconds requires a high number
|
||||
of API requests per second. Please reduce the number of servers, hashtags or accounts, or increase the update
|
||||
interval.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary float-end" form="settings-form">Apply</button>
|
||||
<div v-if="rateLimitRisk" class="alert alert-warning mb-3" role="alert">
|
||||
Current configuration updates {{ sourceCount }} sources every {{ formInterval }} seconds and may
|
||||
run into issues with rate-limited servers. Reduce the number of sources (servers, hashtags, accounts)
|
||||
or increase the update interval.
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" form="settings-form">Apply config</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -371,5 +367,6 @@ const onSubmit = () => {
|
||||
.form-label,
|
||||
h6 {
|
||||
font-weight: bolder;
|
||||
}</style>
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -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<Config> = {}
|
||||
|
||||
@ -268,35 +281,28 @@ export function sanatizeConfig(config: any): Config {
|
||||
return result as Config;
|
||||
}
|
||||
|
||||
async function loadSideConfig() {
|
||||
let config;
|
||||
export async function loadConfig() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const loadUrl = params.get(siteConfigParam)?.trim()
|
||||
|
||||
const loadJson = async (url: string) => {
|
||||
try {
|
||||
config = await (await fetch(siteConfigUrl)).json() || {};
|
||||
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("Site config failed to load, falling back to hard-coded defaults!")
|
||||
console.warn(`Failed to load (or parse) [${url}], falling back to 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() {
|
||||
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 })
|
||||
}
|
Loading…
Reference in New Issue
Block a user