mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
feat(balancers): allow fallback on all strategies + feed burstObservatory from random/roundRobin
Drops the random/roundRobin gate on the Fallback field in BalancerFormModal so every strategy can pick a fallback outbound. syncObservatories now feeds burstObservatory from leastLoad + random + roundRobin balancers (was leastLoad only), matching how leastPing feeds observatory. Fix the JsonEditor "Unexpected end of JSON input" that appeared when switching a balancer between leastPing and another strategy: the obsView watcher was gated on showObsEditor (a boolean OR of the two flags) and missed the case where one observatory swapped for the other in the same tick. Watch the individual flags instead so obsView flips to the surviving editor and the getter stops pointing at a deleted key. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
2ff3c12a42
commit
5f98a2db72
2 changed files with 14 additions and 22 deletions
|
|
@ -61,16 +61,6 @@ const isValid = computed(
|
|||
() => !tagEmpty.value && !duplicateTag.value && !emptySelector.value,
|
||||
);
|
||||
|
||||
const fallbackSupported = computed(
|
||||
() => form.strategy === 'leastPing' || form.strategy === 'leastLoad',
|
||||
);
|
||||
|
||||
watch(() => form.strategy, (next) => {
|
||||
if (next !== 'leastPing' && next !== 'leastLoad') {
|
||||
form.fallbackTag = '';
|
||||
}
|
||||
});
|
||||
|
||||
const tagValidateStatus = computed(() => {
|
||||
if (tagEmpty.value) return 'error';
|
||||
if (duplicateTag.value) return 'warning';
|
||||
|
|
@ -121,8 +111,8 @@ const okText = computed(() =>
|
|||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="Fallback" :help="fallbackSupported ? '' : 'Available only with Least ping / Least load'">
|
||||
<a-select v-model:value="form.fallbackTag" allow-clear :disabled="!fallbackSupported">
|
||||
<a-form-item label="Fallback">
|
||||
<a-select v-model:value="form.fallbackTag" allow-clear>
|
||||
<a-select-option v-for="tag in ['', ...outboundTags]" :key="tag || '__empty'" :value="tag">
|
||||
{{ tag || `(${t('none')})` }}
|
||||
</a-select-option>
|
||||
|
|
|
|||
|
|
@ -133,23 +133,25 @@ function syncObservatories() {
|
|||
delete t.observatory;
|
||||
}
|
||||
|
||||
const leastLoads = balancers.filter((b) => b.strategy?.type === 'leastLoad');
|
||||
if (leastLoads.length > 0) {
|
||||
const burstFeeders = balancers.filter((b) => {
|
||||
const type = b.strategy?.type || 'random';
|
||||
return type === 'leastLoad' || type === 'random' || type === 'roundRobin';
|
||||
});
|
||||
if (burstFeeders.length > 0) {
|
||||
if (!t.burstObservatory) {
|
||||
t.burstObservatory = JSON.parse(JSON.stringify(DEFAULT_BURST_OBSERVATORY));
|
||||
}
|
||||
t.burstObservatory.subjectSelector = collectSelectors(leastLoads);
|
||||
t.burstObservatory.subjectSelector = collectSelectors(burstFeeders);
|
||||
} else {
|
||||
delete t.burstObservatory;
|
||||
}
|
||||
}
|
||||
|
||||
function buildWireBalancer(form) {
|
||||
const supportsFallback = form.strategy === 'leastPing' || form.strategy === 'leastLoad';
|
||||
const out = {
|
||||
tag: form.tag,
|
||||
selector: [...form.selector],
|
||||
fallbackTag: supportsFallback ? form.fallbackTag : '',
|
||||
fallbackTag: form.fallbackTag || '',
|
||||
};
|
||||
if (form.strategy && form.strategy !== 'random') {
|
||||
out.strategy = { type: form.strategy };
|
||||
|
|
@ -218,11 +220,11 @@ const showObsEditor = computed(() => hasObservatory.value || hasBurstObservatory
|
|||
|
||||
const obsView = ref('observatory');
|
||||
|
||||
// Keep the radio selection valid as observatories appear/disappear —
|
||||
// e.g. deleting the last leastPing balancer should flip the editor to
|
||||
// the burstObservatory pane instead of leaving it pointing at the
|
||||
// (now-removed) observatory key.
|
||||
watch(showObsEditor, () => {
|
||||
// Watch each flag individually — watching showObsEditor (OR of the two)
|
||||
// misses the case where one observatory swaps for the other in the same
|
||||
// tick, leaving obsView pointing at a now-deleted key and JsonEditor
|
||||
// trying to parse an empty string.
|
||||
watch([hasObservatory, hasBurstObservatory], () => {
|
||||
if (obsView.value === 'observatory' && !hasObservatory.value && hasBurstObservatory.value) {
|
||||
obsView.value = 'burstObservatory';
|
||||
} else if (obsView.value === 'burstObservatory' && !hasBurstObservatory.value && hasObservatory.value) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue