소스 검색

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

* 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.
Abdalrahman 12 시간 전
부모
커밋
60e6b12f4c
3개의 변경된 파일73개의 추가작업 그리고 1개의 파일을 삭제
  1. 2 1
      frontend/src/models/inbound.js
  2. 68 0
      frontend/src/pages/inbounds/InboundFormModal.vue
  3. 3 0
      sub/subJsonService.go

+ 2 - 1
frontend/src/models/inbound.js

@@ -687,8 +687,9 @@ export class HysteriaMasquerade extends XrayCommonClass {
     }
 
     static fromJson(json = {}) {
+        const type = ['proxy', 'file', 'string'].includes(json.type) ? json.type : 'proxy';
         return new HysteriaMasquerade(
-            json.type,
+            type,
             json.dir,
             json.url,
             json.rewriteHost,

+ 68 - 0
frontend/src/pages/inbounds/InboundFormModal.vue

@@ -1671,6 +1671,74 @@ watch(
               </a-select>
             </a-form-item>
           </template>
+
+          <!-- ====== Hysteria Masquerade ====== -->
+          <!-- Per https://xtls.github.io/config/transports/hysteria.html#masqobject -->
+          <template v-if="protocol === Protocols.HYSTERIA">
+            <a-form-item label="Masquerade">
+              <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="file">File</a-select-option>
+                  <a-select-option value="string">String</a-select-option>
+                </a-select>
+              </a-form-item>
+
+              <!-- Proxy type: url / rewriteHost / insecure -->
+              <template v-if="inbound.stream.hysteria.masquerade.type === 'proxy'">
+                <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>
+              </template>
+
+              <!-- File type: dir -->
+              <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>
+
+              <!-- String type: content / statusCode / headers -->
+              <template v-if="inbound.stream.hysteria.masquerade.type === 'string'">
+                <a-form-item label="Content">
+                  <a-textarea v-model:value="inbound.stream.hysteria.masquerade.content"
+                    :auto-size="{ minRows: 2, maxRows: 6 }" />
+                </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>
+            </template>
+          </template>
         </a-form>
 
         <!-- ====== FinalMask (TCP/UDP masks + QUIC params) ====== -->

+ 3 - 0
sub/subJsonService.go

@@ -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 {