mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
feat(clash): add routing rules and enable routing option for Clash/Mihomo subscriptions
Allows adding custom YAML blocks and placeholders to Clash exports. Why: Shifting routing to the client prevents server IP exposure for DIRECT traffic and reduces unnecessary server bandwidth/CPU usage.
This commit is contained in:
parent
1a64d7e9de
commit
8f48f5dd1c
24 changed files with 220 additions and 15 deletions
|
|
@ -34,7 +34,9 @@ export interface AllSetting {
|
||||||
subAnnounce: string;
|
subAnnounce: string;
|
||||||
subCertFile: string;
|
subCertFile: string;
|
||||||
subClashEnable: boolean;
|
subClashEnable: boolean;
|
||||||
|
subClashEnableRouting: boolean;
|
||||||
subClashPath: string;
|
subClashPath: string;
|
||||||
|
subClashRules: string;
|
||||||
subClashURI: string;
|
subClashURI: string;
|
||||||
subDomain: string;
|
subDomain: string;
|
||||||
subEmailInRemark: boolean;
|
subEmailInRemark: boolean;
|
||||||
|
|
@ -121,7 +123,9 @@ export interface AllSettingView {
|
||||||
subAnnounce: string;
|
subAnnounce: string;
|
||||||
subCertFile: string;
|
subCertFile: string;
|
||||||
subClashEnable: boolean;
|
subClashEnable: boolean;
|
||||||
|
subClashEnableRouting: boolean;
|
||||||
subClashPath: string;
|
subClashPath: string;
|
||||||
|
subClashRules: string;
|
||||||
subClashURI: string;
|
subClashURI: string;
|
||||||
subDomain: string;
|
subDomain: string;
|
||||||
subEmailInRemark: boolean;
|
subEmailInRemark: boolean;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ export const AllSettingSchema = z.object({
|
||||||
subAnnounce: z.string(),
|
subAnnounce: z.string(),
|
||||||
subCertFile: z.string(),
|
subCertFile: z.string(),
|
||||||
subClashEnable: z.boolean(),
|
subClashEnable: z.boolean(),
|
||||||
|
subClashEnableRouting: z.boolean(),
|
||||||
subClashPath: z.string(),
|
subClashPath: z.string(),
|
||||||
|
subClashRules: z.string(),
|
||||||
subClashURI: z.string(),
|
subClashURI: z.string(),
|
||||||
subDomain: z.string(),
|
subDomain: z.string(),
|
||||||
subEmailInRemark: z.boolean(),
|
subEmailInRemark: z.boolean(),
|
||||||
|
|
@ -124,7 +126,9 @@ export const AllSettingViewSchema = z.object({
|
||||||
subAnnounce: z.string(),
|
subAnnounce: z.string(),
|
||||||
subCertFile: z.string(),
|
subCertFile: z.string(),
|
||||||
subClashEnable: z.boolean(),
|
subClashEnable: z.boolean(),
|
||||||
|
subClashEnableRouting: z.boolean(),
|
||||||
subClashPath: z.string(),
|
subClashPath: z.string(),
|
||||||
|
subClashRules: z.string(),
|
||||||
subClashURI: z.string(),
|
subClashURI: z.string(),
|
||||||
subDomain: z.string(),
|
subDomain: z.string(),
|
||||||
subEmailInRemark: z.boolean(),
|
subEmailInRemark: z.boolean(),
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ export class AllSetting {
|
||||||
subURI = '';
|
subURI = '';
|
||||||
subJsonURI = '';
|
subJsonURI = '';
|
||||||
subClashURI = '';
|
subClashURI = '';
|
||||||
|
subClashEnableRouting = true;
|
||||||
|
subClashRules = '';
|
||||||
subJsonFragment = '';
|
subJsonFragment = '';
|
||||||
subJsonNoises = '';
|
subJsonNoises = '';
|
||||||
subJsonMux = '';
|
subJsonMux = '';
|
||||||
|
|
|
||||||
|
|
@ -1109,7 +1109,7 @@ export const sections: readonly Section[] = [
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/{clashPath}:subid',
|
path: '/{clashPath}:subid',
|
||||||
summary: 'Return subscription as a Clash/Mihomo-compatible YAML config. Only when Clash subscription is enabled in settings. Default path: /clash/:subid.',
|
summary: 'Return subscription as a Clash/Mihomo-compatible YAML config, including configured global Clash routing rules. Only when Clash subscription is enabled in settings. Default path: /clash/:subid.',
|
||||||
params: [
|
params: [
|
||||||
{ name: 'subid', in: 'path', type: 'string', desc: 'Client subscription ID.' },
|
{ name: 'subid', in: 'path', type: 'string', desc: 'Client subscription ID.' },
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,20 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su
|
||||||
<Input.TextArea value={allSetting.subRoutingRules} placeholder="happ://routing/add/..."
|
<Input.TextArea value={allSetting.subRoutingRules} placeholder="happ://routing/add/..."
|
||||||
onChange={(e) => updateSetting({ subRoutingRules: e.target.value })} />
|
onChange={(e) => updateSetting({ subRoutingRules: e.target.value })} />
|
||||||
</SettingListItem>
|
</SettingListItem>
|
||||||
|
|
||||||
|
<Divider>Clash / Mihomo</Divider>
|
||||||
|
|
||||||
|
<SettingListItem paddings="small" title={t('pages.settings.subClashEnableRouting')} description={t('pages.settings.subClashEnableRoutingDesc')}>
|
||||||
|
<Switch checked={allSetting.subClashEnableRouting} onChange={(v) => updateSetting({ subClashEnableRouting: v })} />
|
||||||
|
</SettingListItem>
|
||||||
|
<SettingListItem paddings="small" title={t('pages.settings.subClashRoutingRules')} description={t('pages.settings.subClashRoutingRulesDesc')}>
|
||||||
|
<Input.TextArea
|
||||||
|
value={allSetting.subClashRules}
|
||||||
|
rows={8}
|
||||||
|
placeholder={'GEOSITE,category-ir,DIRECT\nGEOIP,private,DIRECT'}
|
||||||
|
onChange={(e) => updateSetting({ subClashRules: e.target.value })}
|
||||||
|
/>
|
||||||
|
</SettingListItem>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ export const AllSettingSchema = z.object({
|
||||||
subURI: z.string().optional(),
|
subURI: z.string().optional(),
|
||||||
subJsonURI: z.string().optional(),
|
subJsonURI: z.string().optional(),
|
||||||
subClashURI: z.string().optional(),
|
subClashURI: z.string().optional(),
|
||||||
|
subClashEnableRouting: z.boolean().optional(),
|
||||||
|
subClashRules: z.string().optional(),
|
||||||
subJsonFragment: z.string().optional(),
|
subJsonFragment: z.string().optional(),
|
||||||
subJsonNoises: z.string().optional(),
|
subJsonNoises: z.string().optional(),
|
||||||
subJsonMux: z.string().optional(),
|
subJsonMux: z.string().optional(),
|
||||||
|
|
|
||||||
13
sub/sub.go
13
sub/sub.go
|
|
@ -140,6 +140,17 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||||
SubJsonRules = ""
|
SubJsonRules = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubClashEnableRouting, err := s.settingService.GetSubClashEnableRouting()
|
||||||
|
if err != nil {
|
||||||
|
SubClashEnableRouting = true
|
||||||
|
}
|
||||||
|
|
||||||
|
SubClashRules, err := s.settingService.GetSubClashRules()
|
||||||
|
if err != nil {
|
||||||
|
SubClashRules = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SubTitle, err := s.settingService.GetSubTitle()
|
SubTitle, err := s.settingService.GetSubTitle()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SubTitle = ""
|
SubTitle = ""
|
||||||
|
|
@ -226,7 +237,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||||
|
|
||||||
s.sub = NewSUBController(
|
s.sub = NewSUBController(
|
||||||
g, LinksPath, JsonPath, ClashPath, subJsonEnable, subClashEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
|
g, LinksPath, JsonPath, ClashPath, subJsonEnable, subClashEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
|
||||||
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle, SubSupportUrl,
|
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubClashEnableRouting, SubClashRules, SubTitle, SubSupportUrl,
|
||||||
SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules)
|
SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules)
|
||||||
|
|
||||||
return engine, nil
|
return engine, nil
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,13 @@ import (
|
||||||
|
|
||||||
type SubClashService struct {
|
type SubClashService struct {
|
||||||
inboundService service.InboundService
|
inboundService service.InboundService
|
||||||
|
enableRouting bool
|
||||||
|
clashRules string
|
||||||
SubService *SubService
|
SubService *SubService
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClashConfig struct {
|
func NewSubClashService(enableRouting bool, clashRules string, subService *SubService) *SubClashService {
|
||||||
Proxies []map[string]any `yaml:"proxies"`
|
return &SubClashService{enableRouting: enableRouting, clashRules: clashRules, SubService: subService}
|
||||||
ProxyGroups []map[string]any `yaml:"proxy-groups"`
|
|
||||||
Rules []string `yaml:"rules"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSubClashService(subService *SubService) *SubClashService {
|
|
||||||
return &SubClashService{SubService: subService}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubClashService) GetClash(subId string, host string) (string, string, error) {
|
func (s *SubClashService) GetClash(subId string, host string) (string, string, error) {
|
||||||
|
|
@ -76,14 +72,20 @@ func (s *SubClashService) GetClash(subId string, host string) (string, string, e
|
||||||
}
|
}
|
||||||
proxyNames = append(proxyNames, "DIRECT")
|
proxyNames = append(proxyNames, "DIRECT")
|
||||||
|
|
||||||
config := ClashConfig{
|
config := map[string]any{
|
||||||
Proxies: proxies,
|
"proxies": proxies,
|
||||||
ProxyGroups: []map[string]any{{
|
"proxy-groups": []map[string]any{{
|
||||||
"name": "PROXY",
|
"name": "PROXY",
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"proxies": proxyNames,
|
"proxies": proxyNames,
|
||||||
}},
|
}},
|
||||||
Rules: []string{"MATCH,PROXY"},
|
"rules": []string{"MATCH,PROXY"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.enableRouting {
|
||||||
|
if err := mergeClashRulesYAML(config, s.clashRules); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finalYAML, err := yaml.Marshal(config)
|
finalYAML, err := yaml.Marshal(config)
|
||||||
|
|
@ -554,3 +556,103 @@ func cloneMap(src map[string]any) map[string]any {
|
||||||
maps.Copy(dst, src)
|
maps.Copy(dst, src)
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func mergeClashRulesYAML(base map[string]any, raw string) error {
|
||||||
|
raw = strings.TrimSpace(raw)
|
||||||
|
if raw == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var custom any
|
||||||
|
if err := yaml.Unmarshal([]byte(raw), &custom); err != nil {
|
||||||
|
mergeClashRules(base, linesToClashRules(raw))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typed := custom.(type) {
|
||||||
|
case []any:
|
||||||
|
mergeClashRules(base, typed)
|
||||||
|
case []string:
|
||||||
|
rules := make([]any, 0, len(typed))
|
||||||
|
for _, rule := range typed {
|
||||||
|
rules = append(rules, rule)
|
||||||
|
}
|
||||||
|
mergeClashRules(base, rules)
|
||||||
|
case map[string]any:
|
||||||
|
if rules, ok := typed["rules"]; ok {
|
||||||
|
if ruleList, ok := asAnySlice(rules); ok {
|
||||||
|
mergeClashRules(base, ruleList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
mergeClashRules(base, linesToClashRules(typed))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeClashRules(base map[string]any, customRules []any) {
|
||||||
|
if len(customRules) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
baseRules, _ := asAnySlice(base["rules"])
|
||||||
|
if hasClashMatchRule(customRules) {
|
||||||
|
base["rules"] = customRules
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
merged := make([]any, 0, len(customRules)+len(baseRules))
|
||||||
|
merged = append(merged, customRules...)
|
||||||
|
merged = append(merged, baseRules...)
|
||||||
|
base["rules"] = merged
|
||||||
|
}
|
||||||
|
|
||||||
|
func asAnySlice(value any) ([]any, bool) {
|
||||||
|
switch typed := value.(type) {
|
||||||
|
case []any:
|
||||||
|
return typed, true
|
||||||
|
case []string:
|
||||||
|
out := make([]any, 0, len(typed))
|
||||||
|
for _, item := range typed {
|
||||||
|
out = append(out, item)
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
case []map[string]any:
|
||||||
|
out := make([]any, 0, len(typed))
|
||||||
|
for _, item := range typed {
|
||||||
|
out = append(out, item)
|
||||||
|
}
|
||||||
|
return out, true
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasClashMatchRule(rules []any) bool {
|
||||||
|
for _, rule := range rules {
|
||||||
|
ruleText, ok := rule.(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(ruleText, ",", 2)
|
||||||
|
if strings.EqualFold(strings.TrimSpace(parts[0]), "MATCH") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func linesToClashRules(raw string) []any {
|
||||||
|
lines := strings.Split(raw, "\n")
|
||||||
|
rules := make([]any, 0, len(lines))
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" || strings.HasPrefix(line, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rules = append(rules, line)
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
@ -66,6 +66,8 @@ func NewSUBController(
|
||||||
jsonNoise string,
|
jsonNoise string,
|
||||||
jsonMux string,
|
jsonMux string,
|
||||||
jsonRules string,
|
jsonRules string,
|
||||||
|
clashEnableRouting bool,
|
||||||
|
clashRules string,
|
||||||
subTitle string,
|
subTitle string,
|
||||||
subSupportUrl string,
|
subSupportUrl string,
|
||||||
subProfileUrl string,
|
subProfileUrl string,
|
||||||
|
|
@ -91,7 +93,7 @@ func NewSUBController(
|
||||||
|
|
||||||
subService: sub,
|
subService: sub,
|
||||||
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
|
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
|
||||||
subClashService: NewSubClashService(sub),
|
subClashService: NewSubClashService(clashEnableRouting, clashRules, sub),
|
||||||
}
|
}
|
||||||
a.initRouter(g)
|
a.initRouter(g)
|
||||||
return a
|
return a
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ type AllSetting struct {
|
||||||
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint
|
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint
|
||||||
SubClashPath string `json:"subClashPath" form:"subClashPath"` // Path for Clash/Mihomo subscription endpoint
|
SubClashPath string `json:"subClashPath" form:"subClashPath"` // Path for Clash/Mihomo subscription endpoint
|
||||||
SubClashURI string `json:"subClashURI" form:"subClashURI"` // Clash/Mihomo subscription server URI
|
SubClashURI string `json:"subClashURI" form:"subClashURI"` // Clash/Mihomo subscription server URI
|
||||||
|
SubClashEnableRouting bool `json:"subClashEnableRouting" form:"subClashEnableRouting"` // Enable global routing rules for Clash/Mihomo
|
||||||
|
SubClashRules string `json:"subClashRules" form:"subClashRules"` // Clash/Mihomo global routing rules
|
||||||
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` // JSON subscription fragment configuration
|
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` // JSON subscription fragment configuration
|
||||||
SubJsonNoises string `json:"subJsonNoises" form:"subJsonNoises"` // JSON subscription noise configuration
|
SubJsonNoises string `json:"subJsonNoises" form:"subJsonNoises"` // JSON subscription noise configuration
|
||||||
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"` // JSON subscription mux configuration
|
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"` // JSON subscription mux configuration
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ var defaultValueMap = map[string]string{
|
||||||
"subClashEnable": "false",
|
"subClashEnable": "false",
|
||||||
"subClashPath": "/clash/",
|
"subClashPath": "/clash/",
|
||||||
"subClashURI": "",
|
"subClashURI": "",
|
||||||
|
"subClashEnableRouting": "true",
|
||||||
|
"subClashRules": "",
|
||||||
"subJsonFragment": "",
|
"subJsonFragment": "",
|
||||||
"subJsonNoises": "",
|
"subJsonNoises": "",
|
||||||
"subJsonMux": "",
|
"subJsonMux": "",
|
||||||
|
|
@ -658,6 +660,14 @@ func (s *SettingService) GetSubClashURI() (string, error) {
|
||||||
return s.getString("subClashURI")
|
return s.getString("subClashURI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubClashEnableRouting() (bool, error) {
|
||||||
|
return s.getBool("subClashEnableRouting")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubClashRules() (string, error) {
|
||||||
|
return s.getString("subClashRules")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetSubJsonFragment() (string, error) {
|
func (s *SettingService) GetSubJsonFragment() (string, error) {
|
||||||
return s.getString("subJsonFragment")
|
return s.getString("subJsonFragment")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)",
|
"subEnableRoutingDesc": "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)",
|
||||||
"subRoutingRules": "قواعد التوجيه",
|
"subRoutingRules": "قواعد التوجيه",
|
||||||
"subRoutingRulesDesc": "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)",
|
"subRoutingRulesDesc": "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)",
|
||||||
|
"subClashEnableRouting": "تفعيل التوجيه",
|
||||||
|
"subClashEnableRoutingDesc": "تضمين قواعد توجيه Clash/Mihomo العامة في اشتراكات YAML المُنشأة.",
|
||||||
|
"subClashRoutingRules": "قواعد التوجيه العامة",
|
||||||
|
"subClashRoutingRulesDesc": "قواعد Clash/Mihomo التي تُضاف في بداية كل اشتراك YAML قبل MATCH,PROXY.",
|
||||||
"subListen": "IP الاستماع",
|
"subListen": "IP الاستماع",
|
||||||
"subListenDesc": "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)",
|
"subListenDesc": "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)",
|
||||||
"subPort": "بورت الاستماع",
|
"subPort": "بورت الاستماع",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "Global setting to enable routing in the VPN client. (Only for Happ)",
|
"subEnableRoutingDesc": "Global setting to enable routing in the VPN client. (Only for Happ)",
|
||||||
"subRoutingRules": "Routing rules",
|
"subRoutingRules": "Routing rules",
|
||||||
"subRoutingRulesDesc": "Global routing rules for the VPN client. (Only for Happ)",
|
"subRoutingRulesDesc": "Global routing rules for the VPN client. (Only for Happ)",
|
||||||
|
"subClashEnableRouting": "Enable routing",
|
||||||
|
"subClashEnableRoutingDesc": "Include global Clash/Mihomo routing rules in generated YAML subscriptions.",
|
||||||
|
"subClashRoutingRules": "Global routing rules",
|
||||||
|
"subClashRoutingRulesDesc": "Default Clash/Mihomo rules prepended to every generated YAML subscription before MATCH,PROXY.",
|
||||||
"subListen": "Listen IP",
|
"subListen": "Listen IP",
|
||||||
"subListenDesc": "The IP address for the subscription service. (leave blank to listen on all IPs)",
|
"subListenDesc": "The IP address for the subscription service. (leave blank to listen on all IPs)",
|
||||||
"subPort": "Listen Port",
|
"subPort": "Listen Port",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "Configuración global para habilitar el enrutamiento en el cliente VPN. (Solo para Happ)",
|
"subEnableRoutingDesc": "Configuración global para habilitar el enrutamiento en el cliente VPN. (Solo para Happ)",
|
||||||
"subRoutingRules": "Reglas de enrutamiento",
|
"subRoutingRules": "Reglas de enrutamiento",
|
||||||
"subRoutingRulesDesc": "Reglas de enrutamiento globales para el cliente VPN. (Solo para Happ)",
|
"subRoutingRulesDesc": "Reglas de enrutamiento globales para el cliente VPN. (Solo para Happ)",
|
||||||
|
"subClashEnableRouting": "Habilitar enrutamiento",
|
||||||
|
"subClashEnableRoutingDesc": "Incluir reglas globales de enrutamiento Clash/Mihomo en las suscripciones YAML generadas.",
|
||||||
|
"subClashRoutingRules": "Reglas globales de enrutamiento",
|
||||||
|
"subClashRoutingRulesDesc": "Reglas Clash/Mihomo agregadas al inicio de cada suscripción YAML antes de MATCH,PROXY.",
|
||||||
"subListen": "Listening IP",
|
"subListen": "Listening IP",
|
||||||
"subListenDesc": "Dejar en blanco por defecto para monitorear todas las IPs.",
|
"subListenDesc": "Dejar en blanco por defecto para monitorear todas las IPs.",
|
||||||
"subPort": "Puerto de Suscripción",
|
"subPort": "Puerto de Suscripción",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "تنظیمات سراسری برای فعالسازی مسیریابی در کلاینت VPN. (فقط برای Happ)",
|
"subEnableRoutingDesc": "تنظیمات سراسری برای فعالسازی مسیریابی در کلاینت VPN. (فقط برای Happ)",
|
||||||
"subRoutingRules": "قوانین مسیریابی",
|
"subRoutingRules": "قوانین مسیریابی",
|
||||||
"subRoutingRulesDesc": "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)",
|
"subRoutingRulesDesc": "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)",
|
||||||
|
"subClashEnableRouting": "فعالسازی مسیریابی",
|
||||||
|
"subClashEnableRoutingDesc": "قوانین مسیریابی سراسری Clash/Mihomo را در اشتراکهای YAML تولیدشده وارد کن.",
|
||||||
|
"subClashRoutingRules": "قوانین مسیریابی سراسری",
|
||||||
|
"subClashRoutingRulesDesc": "قوانین Clash/Mihomo که پیش از MATCH,PROXY به ابتدای هر اشتراک YAML افزوده میشوند.",
|
||||||
"subListen": "آدرس آیپی",
|
"subListen": "آدرس آیپی",
|
||||||
"subListenDesc": "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید",
|
"subListenDesc": "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید",
|
||||||
"subPort": "پورت",
|
"subPort": "پورت",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "Pengaturan global untuk mengaktifkan perutean (routing) di klien VPN. (Hanya untuk Happ)",
|
"subEnableRoutingDesc": "Pengaturan global untuk mengaktifkan perutean (routing) di klien VPN. (Hanya untuk Happ)",
|
||||||
"subRoutingRules": "Aturan routing",
|
"subRoutingRules": "Aturan routing",
|
||||||
"subRoutingRulesDesc": "Aturan routing global untuk klien VPN. (Hanya untuk Happ)",
|
"subRoutingRulesDesc": "Aturan routing global untuk klien VPN. (Hanya untuk Happ)",
|
||||||
|
"subClashEnableRouting": "Aktifkan routing",
|
||||||
|
"subClashEnableRoutingDesc": "Sertakan aturan routing global Clash/Mihomo dalam langganan YAML yang dibuat.",
|
||||||
|
"subClashRoutingRules": "Aturan routing global",
|
||||||
|
"subClashRoutingRulesDesc": "Aturan Clash/Mihomo yang ditambahkan di awal setiap langganan YAML sebelum MATCH,PROXY.",
|
||||||
"subListen": "IP Pendengar",
|
"subListen": "IP Pendengar",
|
||||||
"subListenDesc": "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)",
|
"subListenDesc": "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)",
|
||||||
"subPort": "Port Pendengar",
|
"subPort": "Port Pendengar",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)",
|
"subEnableRoutingDesc": "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)",
|
||||||
"subRoutingRules": "ルーティングルール",
|
"subRoutingRules": "ルーティングルール",
|
||||||
"subRoutingRulesDesc": "VPNクライアントのグローバルルーティングルール。(Happのみ)",
|
"subRoutingRulesDesc": "VPNクライアントのグローバルルーティングルール。(Happのみ)",
|
||||||
|
"subClashEnableRouting": "ルーティングを有効化",
|
||||||
|
"subClashEnableRoutingDesc": "生成されたYAMLサブスクリプションにClash/Mihomoのグローバルルーティングルールを含めます。",
|
||||||
|
"subClashRoutingRules": "グローバルルーティングルール",
|
||||||
|
"subClashRoutingRulesDesc": "各YAMLサブスクリプションのMATCH,PROXYより前に追加されるClash/Mihomoルール。",
|
||||||
"subListen": "監視IP",
|
"subListen": "監視IP",
|
||||||
"subListenDesc": "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)",
|
"subListenDesc": "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)",
|
||||||
"subPort": "監視ポート",
|
"subPort": "監視ポート",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "Configuração global para habilitar o roteamento no cliente VPN. (Apenas para Happ)",
|
"subEnableRoutingDesc": "Configuração global para habilitar o roteamento no cliente VPN. (Apenas para Happ)",
|
||||||
"subRoutingRules": "Regras de roteamento",
|
"subRoutingRules": "Regras de roteamento",
|
||||||
"subRoutingRulesDesc": "Regras de roteamento globais para o cliente VPN. (Apenas para Happ)",
|
"subRoutingRulesDesc": "Regras de roteamento globais para o cliente VPN. (Apenas para Happ)",
|
||||||
|
"subClashEnableRouting": "Ativar roteamento",
|
||||||
|
"subClashEnableRoutingDesc": "Incluir regras globais de roteamento Clash/Mihomo nas assinaturas YAML geradas.",
|
||||||
|
"subClashRoutingRules": "Regras globais de roteamento",
|
||||||
|
"subClashRoutingRulesDesc": "Regras Clash/Mihomo adicionadas ao início de cada assinatura YAML antes de MATCH,PROXY.",
|
||||||
"subListen": "IP de Escuta",
|
"subListen": "IP de Escuta",
|
||||||
"subListenDesc": "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)",
|
"subListenDesc": "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)",
|
||||||
"subPort": "Porta de Escuta",
|
"subPort": "Porta de Escuta",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "Глобальная настройка для включения маршрутизации в VPN-клиенте. (Только для Happ)",
|
"subEnableRoutingDesc": "Глобальная настройка для включения маршрутизации в VPN-клиенте. (Только для Happ)",
|
||||||
"subRoutingRules": "Правила маршрутизации",
|
"subRoutingRules": "Правила маршрутизации",
|
||||||
"subRoutingRulesDesc": "Глобальные правила маршрутизации для VPN-клиента. (Только для Happ)",
|
"subRoutingRulesDesc": "Глобальные правила маршрутизации для VPN-клиента. (Только для Happ)",
|
||||||
|
"subClashEnableRouting": "Включить маршрутизацию",
|
||||||
|
"subClashEnableRoutingDesc": "Добавлять глобальные правила маршрутизации Clash/Mihomo в сгенерированные YAML-подписки.",
|
||||||
|
"subClashRoutingRules": "Глобальные правила маршрутизации",
|
||||||
|
"subClashRoutingRulesDesc": "Правила Clash/Mihomo, добавляемые в начало каждой YAML-подписки перед MATCH,PROXY.",
|
||||||
"subListen": "Прослушивание IP",
|
"subListen": "Прослушивание IP",
|
||||||
"subListenDesc": "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса",
|
"subListenDesc": "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса",
|
||||||
"subPort": "Порт подписки",
|
"subPort": "Порт подписки",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "VPN istemcisinde yönlendirmeyi etkinleştirmek için genel ayar. (Yalnızca Happ için)",
|
"subEnableRoutingDesc": "VPN istemcisinde yönlendirmeyi etkinleştirmek için genel ayar. (Yalnızca Happ için)",
|
||||||
"subRoutingRules": "Yönlendirme kuralları",
|
"subRoutingRules": "Yönlendirme kuralları",
|
||||||
"subRoutingRulesDesc": "VPN istemcisi için genel yönlendirme kuralları. (Yalnızca Happ için)",
|
"subRoutingRulesDesc": "VPN istemcisi için genel yönlendirme kuralları. (Yalnızca Happ için)",
|
||||||
|
"subClashEnableRouting": "Yönlendirmeyi etkinleştir",
|
||||||
|
"subClashEnableRoutingDesc": "Oluşturulan YAML aboneliklerine genel Clash/Mihomo yönlendirme kurallarını ekle.",
|
||||||
|
"subClashRoutingRules": "Genel yönlendirme kuralları",
|
||||||
|
"subClashRoutingRulesDesc": "Her YAML aboneliğinin başına MATCH,PROXY öncesinde eklenen Clash/Mihomo kuralları.",
|
||||||
"subListen": "Dinleme IP",
|
"subListen": "Dinleme IP",
|
||||||
"subListenDesc": "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)",
|
"subListenDesc": "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)",
|
||||||
"subPort": "Dinleme Portu",
|
"subPort": "Dinleme Portu",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)",
|
"subEnableRoutingDesc": "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)",
|
||||||
"subRoutingRules": "Правила маршрутизації",
|
"subRoutingRules": "Правила маршрутизації",
|
||||||
"subRoutingRulesDesc": "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)",
|
"subRoutingRulesDesc": "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)",
|
||||||
|
"subClashEnableRouting": "Увімкнути маршрутизацію",
|
||||||
|
"subClashEnableRoutingDesc": "Додавати глобальні правила маршрутизації Clash/Mihomo до згенерованих YAML-підписок.",
|
||||||
|
"subClashRoutingRules": "Глобальні правила маршрутизації",
|
||||||
|
"subClashRoutingRulesDesc": "Правила Clash/Mihomo, що додаються на початок кожної YAML-підписки перед MATCH,PROXY.",
|
||||||
"subListen": "Слухати IP",
|
"subListen": "Слухати IP",
|
||||||
"subListenDesc": "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)",
|
"subListenDesc": "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)",
|
||||||
"subPort": "Слухати порт",
|
"subPort": "Слухати порт",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "Cài đặt toàn cục để bật định tuyến trong ứng dụng khách VPN. (Chỉ dành cho Happ)",
|
"subEnableRoutingDesc": "Cài đặt toàn cục để bật định tuyến trong ứng dụng khách VPN. (Chỉ dành cho Happ)",
|
||||||
"subRoutingRules": "Quy tắc định tuyến",
|
"subRoutingRules": "Quy tắc định tuyến",
|
||||||
"subRoutingRulesDesc": "Quy tắc định tuyến toàn cầu cho client VPN. (Chỉ dành cho Happ)",
|
"subRoutingRulesDesc": "Quy tắc định tuyến toàn cầu cho client VPN. (Chỉ dành cho Happ)",
|
||||||
|
"subClashEnableRouting": "Bật định tuyến",
|
||||||
|
"subClashEnableRoutingDesc": "Bao gồm quy tắc định tuyến Clash/Mihomo toàn cầu trong các đăng ký YAML được tạo.",
|
||||||
|
"subClashRoutingRules": "Quy tắc định tuyến toàn cầu",
|
||||||
|
"subClashRoutingRulesDesc": "Quy tắc Clash/Mihomo được thêm vào đầu mỗi đăng ký YAML trước MATCH,PROXY.",
|
||||||
"subListen": "Listening IP",
|
"subListen": "Listening IP",
|
||||||
"subListenDesc": "Mặc định để trống để nghe tất cả các IP",
|
"subListenDesc": "Mặc định để trống để nghe tất cả các IP",
|
||||||
"subPort": "Cổng gói đăng ký",
|
"subPort": "Cổng gói đăng ký",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "在 VPN 客户端中启用路由的全局设置。(僅限 Happ)",
|
"subEnableRoutingDesc": "在 VPN 客户端中启用路由的全局设置。(僅限 Happ)",
|
||||||
"subRoutingRules": "路由規則",
|
"subRoutingRules": "路由規則",
|
||||||
"subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)",
|
"subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)",
|
||||||
|
"subClashEnableRouting": "启用路由",
|
||||||
|
"subClashEnableRoutingDesc": "在生成的 YAML 订阅中包含 Clash/Mihomo 全局路由规则。",
|
||||||
|
"subClashRoutingRules": "全局路由规则",
|
||||||
|
"subClashRoutingRulesDesc": "添加到每个 YAML 订阅开头、MATCH,PROXY 之前的 Clash/Mihomo 规则。",
|
||||||
"subListen": "监听 IP",
|
"subListen": "监听 IP",
|
||||||
"subListenDesc": "订阅服务监听的 IP 地址(留空表示监听所有 IP)",
|
"subListenDesc": "订阅服务监听的 IP 地址(留空表示监听所有 IP)",
|
||||||
"subPort": "监听端口",
|
"subPort": "监听端口",
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,10 @@
|
||||||
"subEnableRoutingDesc": "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)",
|
"subEnableRoutingDesc": "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)",
|
||||||
"subRoutingRules": "路由規則",
|
"subRoutingRules": "路由規則",
|
||||||
"subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)",
|
"subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)",
|
||||||
|
"subClashEnableRouting": "啟用路由",
|
||||||
|
"subClashEnableRoutingDesc": "在產生的 YAML 訂閱中包含 Clash/Mihomo 全域路由規則。",
|
||||||
|
"subClashRoutingRules": "全域路由規則",
|
||||||
|
"subClashRoutingRulesDesc": "加入到每個 YAML 訂閱開頭、MATCH,PROXY 之前的 Clash/Mihomo 規則。",
|
||||||
"subListen": "監聽 IP",
|
"subListen": "監聽 IP",
|
||||||
"subListenDesc": "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)",
|
"subListenDesc": "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)",
|
||||||
"subPort": "監聽埠",
|
"subPort": "監聽埠",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue