[outbound] add json mode and link convertor

This commit is contained in:
Alireza Ahmadi
2023-11-26 13:32:02 +01:00
parent 1f4ff4b985
commit 5f9ae30b71
3 changed files with 126 additions and 18 deletions

View File

@@ -470,6 +470,61 @@ class Outbound extends CommonClass {
streamSettings: this.canEnableStream() ? this.stream.toJson() : undefined,
};
}
static fromLink(link) {
data = link.split('://');
if(data.length !=2) return null;
switch(data[0].toLowerCase()){
case Protocols.VMess:
return this.fromVmessLink(JSON.parse(atob(data[1])));
case Protocols.VLESS:
case Protocols.Trojan:
case 'ss':
default:
return null;
}
}
static fromVmessLink(json={}){
let stream = new StreamSettings(json.net, json.tls);
let network = json.net;
if (network === 'tcp') {
stream.tcp = new TcpStreamSettings(
json.type,
json.host ? json.host.split(','): [],
json.path ? json.path.split(','): []);
} else if (network === 'kcp') {
stream.kcp = new KcpStreamSettings();
stream.type = json.type;
stream.seed = json.path;
} else if (network === 'ws') {
stream.ws = new WsStreamSettings(json.path,json.host);
} else if (network === 'http' || network == 'h2') {
stream.network = 'http'
stream.http = new HttpStreamSettings(
json.path,
json.host ? json.host.split(',') : []);
} else if (network === 'quic') {
strem.quic = new QuicStreamSettings(
json.host ? json.host : 'none',
json.path,
json.type ? json.type : 'none');
} else if (network === 'grpc') {
stream.grpc = new GrpcStreamSettings(json.path, json.type == 'multi');
}
if(json.tls && json.tls == 'tls'){
stream.tls = new TlsStreamSettings(
json.sni,
json.alpn ? json.alpn.split(',') : [],
json.fp,
json.allowInsecure);
}
return new Outbound(json.ps, Protocols.VMess, new Outbound.VmessSettings(json.add, json.port, json.id), stream);
}
}
Outbound.Settings = class extends CommonClass {
@@ -512,7 +567,6 @@ Outbound.Settings = class extends CommonClass {
return {};
}
};
Outbound.FreedomSettings = class extends CommonClass {
constructor(domainStrategy='', fragment={}) {
super();

View File

@@ -1,5 +1,7 @@
{{define "form/outbound"}}
<!-- base -->
<a-tabs :active-key="outModal.activeKey" style="padding: 0; background-color: transparent;" @change="(activeKey) => {outModal.toggleJson(activeKey == '2'); }">
<a-tab-pane key="1" tab="Form">
<a-form layout="inline">
<table width="100%" class="ant-table-tbody">
<tr>
@@ -16,7 +18,7 @@
<td>{{ i18n "pages.xray.outbound.tag" }}</td>
<td>
<a-form-item>
<a-input v-model.trim="outbound.tag" style="width: 250px" @change="check" :style="duplicateTag? 'border-color: red;' : ''"></a-input>
<a-input v-model.trim="outbound.tag" style="width: 250px" @change="outModal.check()" :style="outModal.duplicateTag? 'border-color: red;' : ''"></a-input>
</a-form-item>
</td>
</tr>
@@ -527,7 +529,17 @@
</td>
</tr>
</template>
</template>
</template>
</table>
</a-form>
</a-tab-pane>
<a-tab-pane key="2" tab="JSON" force-render="true">
<a-form-item style="margin: 10px 0">
Link: <a-input v-model.trim="outModal.link" style="width: 300px; margin-right: 5px;" placeholder="vmess:// vless:// trojan:// ss://"></a-input>
<a-button @click="convertLink" type="primary"><a-icon type="form"></a-icon></a-button>
</a-form-item>
<textarea style="position:absolute; left: -800px;" id="outboundJson"></textarea>
</a-tab-pane>
</a-tabs>
{{end}}

View File

@@ -1,9 +1,8 @@
{{define "outModal"}}
<a-modal id="out-modal" v-model="outModal.visible" :title="outModal.title" @ok="outModal.ok"
:confirm-loading="outModal.confirmLoading" :closable="true" :mask-closable="false"
:ok-button-props="{ props: { disabled: !isValid } }"
:ok-button-props="{ props: { disabled: !outModal.isValid } }" style="overflow: hidden;"
:ok-text="outModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
<pre>[[ outModal.outbound ]]</pre>
{{template "form/outbound"}}
</a-modal>
<script>
@@ -16,7 +15,12 @@
isEdit: false,
confirm: null,
outbound: new Outbound(),
outboundTags: [],
jsonMode: false,
link: '',
cm: null,
duplicateTag: false,
isValid: true,
activeKey: '1',
ok() {
ObjectUtil.execute(outModal.confirm, outModal.outbound.toJson());
},
@@ -24,11 +28,13 @@
this.title = title;
this.okText = okText;
this.confirm = confirm;
this.jsonMode = false;
this.link = '';
this.activeKey = '1';
this.visible = true;
this.outbound = isEdit ? Outbound.fromJson(outbound) : new Outbound();
this.isEdit = isEdit;
this.outboundTags = app.templateSettings.outbounds.map(obj => obj.tag);
this.check()
},
close() {
outModal.visible = false;
@@ -36,7 +42,49 @@
},
loading(loading) {
outModal.confirmLoading = loading;
}
},
check(){
if(outModal.outbound.tag == ''){
this.duplicateTag = true;
this.isValid = false;
} else {
this.duplicateTag = false;
this.isValid = true;
}
},
toggleJson(jsonTab) {
textAreaObj = document.getElementById('outboundJson');
if(jsonTab){
if(this.cm != null) {
this.cm.toTextArea();
this.cm=null;
}
textAreaObj.value = JSON.stringify(this.outbound.toJson(), null, 2);
this.cm = CodeMirror.fromTextArea(textAreaObj, app.cmOptions);
this.cm.on('change',editor => {
value = editor.getValue();
if(this.isJsonString(value)){
this.outbound = Outbound.fromJson(JSON.parse(value));
this.check();
}
});
this.activeKey = '2';
} else {
if(this.cm != null) {
this.cm.toTextArea();
this.cm=null;
}
this.activeKey = '1';
}
},
isJsonString(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
},
};
new Vue({
@@ -44,8 +92,6 @@
el: '#out-modal',
data: {
outModal: outModal,
duplicateTag: false,
isValid: false,
get outbound() {
return outModal.outbound;
},
@@ -59,14 +105,10 @@
canEnableTls() {
return this.outModal.outbound.canEnableTls();
},
check(){
if(outModal.outbound.tag == '' || this.outModal.outboundTags.includes(outModal.outbound.tag)){
this.duplicateTag = true;
this.isValid = false;
} else {
this.duplicateTag = false;
this.isValid = true;
}
convertLink(){
this.outModal.outbound = Outbound.fromLink(outModal.link);
this.outModal.toggleJson(true);
this.outModal.check();
},
},
});