fix(hysteria2): restore missing masquerade config in inbound form

Fixes #4303

The Hysteria2 Masquerade option was missing from the Stream settings
tab after the v3.0.0 form rewrite. Added the UI form and ensured the
masquerade block is passed through in subscription JSON generation.
This commit is contained in:
abdulrahman 2026-05-12 20:03:06 +03:00
parent 07bc74a521
commit c7e838c95a
2 changed files with 71 additions and 0 deletions

View file

@ -1671,6 +1671,74 @@ watch(
</a-select>
</a-form-item>
</template>
<!-- ====== Hysteria Masquerade ====== -->
<template v-if="protocol === Protocols.HYSTERIA">
<a-divider :style="{ margin: '12px 0' }">Masquerade</a-divider>
<a-form-item label="Enable">
<a-switch v-model:checked="inbound.stream.hysteria.masqueradeSwitch" />
</a-form-item>
<template v-if="inbound.stream.hysteria.masqueradeSwitch">
<a-form-item label="Type">
<a-select v-model:value="inbound.stream.hysteria.masquerade.type" :style="{ width: '50%' }">
<a-select-option value="proxy">Proxy</a-select-option>
<a-select-option value="http">HTTP</a-select-option>
<a-select-option value="file">File</a-select-option>
<a-select-option value="string">String</a-select-option>
<a-select-option value="404">404</a-select-option>
</a-select>
</a-form-item>
<!-- File type -->
<a-form-item v-if="inbound.stream.hysteria.masquerade.type === 'file'" label="Directory">
<a-input v-model:value="inbound.stream.hysteria.masquerade.dir" placeholder="/path/to/www" />
</a-form-item>
<!-- HTTP type -->
<template v-if="inbound.stream.hysteria.masquerade.type === 'http'">
<a-form-item label="URL">
<a-input v-model:value="inbound.stream.hysteria.masquerade.url" placeholder="https://example.com" />
</a-form-item>
<a-form-item label="Rewrite Host">
<a-switch v-model:checked="inbound.stream.hysteria.masquerade.rewriteHost" />
</a-form-item>
<a-form-item label="Insecure">
<a-switch v-model:checked="inbound.stream.hysteria.masquerade.insecure" />
</a-form-item>
<a-form-item label="Status Code">
<a-input-number v-model:value="inbound.stream.hysteria.masquerade.statusCode" :min="100" :max="599"
placeholder="200" />
</a-form-item>
<a-form-item label="Headers">
<a-button size="small" @click="inbound.stream.hysteria.masquerade.addHeader('', '')">
<template #icon>
<PlusOutlined />
</template>
</a-button>
</a-form-item>
<a-form-item v-if="inbound.stream.hysteria.masquerade.headers.length > 0" :wrapper-col="{ span: 24 }">
<a-input-group v-for="(h, idx) in inbound.stream.hysteria.masquerade.headers" :key="`mh-${idx}`" compact
class="mb-8">
<a-input :style="{ width: '45%' }" v-model:value="h.name" placeholder="Name">
<template #addonBefore>{{ idx + 1 }}</template>
</a-input>
<a-input :style="{ width: '45%' }" v-model:value="h.value" placeholder="Value" />
<a-button @click="inbound.stream.hysteria.masquerade.removeHeader(idx)">
<template #icon>
<MinusOutlined />
</template>
</a-button>
</a-input-group>
</a-form-item>
</template>
<!-- String type -->
<a-form-item v-if="inbound.stream.hysteria.masquerade.type === 'string'" label="Content">
<a-textarea v-model:value="inbound.stream.hysteria.masquerade.content"
:auto-size="{ minRows: 2, maxRows: 6 }" />
</a-form-item>
</template>
</template>
</a-form>
<!-- ====== FinalMask (TCP/UDP masks + QUIC params) ====== -->

View file

@ -447,6 +447,9 @@ func (s *SubJsonService) genHy(inbound *model.Inbound, newStream map[string]any,
if udpIdleTimeout, ok := hyStream["udpIdleTimeout"].(float64); ok {
outHyStream["udpIdleTimeout"] = int(udpIdleTimeout)
}
if masquerade, ok := hyStream["masquerade"].(map[string]any); ok {
outHyStream["masquerade"] = masquerade
}
newStream["hysteriaSettings"] = outHyStream
if finalmask, ok := hyStream["finalmask"].(map[string]any); ok {