feat(clients): add Reverse tag field for VLESS-attached clients

Mirrors the Flow field's pattern: a Reverse tag input appears in the
Add/Edit Client modal whenever at least one selected inbound is VLESS
or PortFallback. The value rides over the wire as
client.reverse = { tag: '...' } so it lands directly in model.Client's
*ClientReverse field; an empty value omits the reverse key entirely.

On edit the field is hydrated from props.client.reverse?.tag, and the
showReverseTag watcher clears the field if the user drops the last
VLESS-like inbound from the selection.
This commit is contained in:
MHSanaei 2026-05-17 12:44:51 +02:00
parent e9fce827ac
commit cfd8cc3cbb
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A

View file

@ -33,6 +33,7 @@ function emptyForm() {
password: '',
auth: '',
flow: '',
reverseTag: '',
totalGB: 0,
expiryTime: null,
limitIp: 0,
@ -56,6 +57,7 @@ watch(
form.password = props.client.password || '';
form.auth = props.client.auth || '';
form.flow = props.client.flow || '';
form.reverseTag = props.client.reverse?.tag || '';
form.totalGB = bytesToGB(props.client.totalGB || 0);
form.expiryTime = props.client.expiryTime ? dayjs(props.client.expiryTime) : null;
form.limitIp = props.client.limitIp || 0;
@ -110,6 +112,24 @@ watch(showFlow, (next) => {
if (!next) form.flow = '';
});
const vlessLikeIds = computed(() => {
const ids = new Set();
for (const row of props.inbounds || []) {
if (row && (row.protocol === 'vless' || row.protocol === 'portfallback')) {
ids.add(row.id);
}
}
return ids;
});
const showReverseTag = computed(() =>
(form.inboundIds || []).some((id) => vlessLikeIds.value.has(id)),
);
watch(showReverseTag, (next) => {
if (!next) form.reverseTag = '';
});
const clientIps = ref([]);
const ipsLoading = ref(false);
const ipsClearing = ref(false);
@ -184,6 +204,10 @@ async function onSubmit() {
comment: form.comment,
enable: !!form.enable,
};
const reverseTag = showReverseTag.value ? (form.reverseTag || '').trim() : '';
if (reverseTag) {
clientPayload.reverse = { tag: reverseTag };
}
submitting.value = true;
try {
@ -271,17 +295,6 @@ async function onSubmit() {
</a-col>
</a-row>
<a-row v-if="showFlow" :gutter="16">
<a-col :span="12">
<a-form-item label="Flow">
<a-select v-model:value="form.flow">
<a-select-option value="">none</a-select-option>
<a-select-option v-for="k in FLOW_OPTIONS" :key="k" :value="k">{{ k }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item :label="t('pages.clients.totalGB') || 'Total (GB, 0 = unlimited)'">
@ -295,6 +308,22 @@ async function onSubmit() {
</a-col>
</a-row>
<a-row v-if="showFlow || showReverseTag" :gutter="16">
<a-col v-if="showFlow" :span="12">
<a-form-item label="Flow">
<a-select v-model:value="form.flow">
<a-select-option value="">none</a-select-option>
<a-select-option v-for="k in FLOW_OPTIONS" :key="k" :value="k">{{ k }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col v-if="showReverseTag" :span="12">
<a-form-item label="Reverse tag">
<a-input v-model:value="form.reverseTag" placeholder="Optional reverse tag" />
</a-form-item>
</a-col>
</a-row>
<a-form-item :label="t('pages.clients.comment') || 'Comment'">
<a-input v-model:value="form.comment" />
</a-form-item>