Skip to content

Commit

Permalink
feat: support mannually create and modify server
Browse files Browse the repository at this point in the history
  • Loading branch information
mzz2017 committed Nov 23, 2019
1 parent 7fbaf3f commit 1a4d6a7
Show file tree
Hide file tree
Showing 16 changed files with 498 additions and 28 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ V2RayA 是 V2Ray 的一个 Web 客户端。
- [x] 全局透明代理(不支持 docker 模式)
- [x] V2Ray 服务控制
- [x] 导入 vmess、ss、订阅地址
- [x] 手动添加/修改节点
- [x] websocket、kcp、tcp、http、tls、shadowsocks 全支持
- [x] 测试节点 Ping 时延
- [x] 二维码、地址分享
Expand All @@ -36,10 +37,9 @@ V2RayA 是 V2Ray 的一个 Web 客户端。

待开发:

- [ ] 手动添加/修改节点
- [ ] 测试节点 HTTP 时延
- [ ] 自定义 PAC 路由规则
- [ ] QUIC 协议支持
- [ ] QUIC、SSR 协议支持
- [ ] 服务端端口号配置、前端可指定服务端地址
- [ ] 登陆与安全

Expand Down
2 changes: 1 addition & 1 deletion gui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export default {
parent: this,
component: ModalSetting,
hasModalCard: true,
canCancel: false
canCancel: true
});
},
handleClickAbout() {
Expand Down
367 changes: 367 additions & 0 deletions gui/src/components/ModalServer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
<template>
<div class="modal-card" style="max-width: 520px;margin:auto">
<header class="modal-card-head">
<p class="modal-card-title">节点配置</p>
</header>
<section :class="{ 'modal-card-body': true, readonly: readonly }">
<b-tabs
v-model="tabChoice"
position="is-centered"
class="block"
type="is-boxed is-twitter same-width-5"
>
<b-tab-item label="VMESS">
<b-field label="Name" label-position="on-border">
<b-input
v-model="vmess.ps"
placeholder="节点名称"
expanded
></b-input>
</b-field>
<b-field label="Address" label-position="on-border">
<b-input
ref="vmess_add"
v-model="vmess.add"
required
placeholder="IP / HOST"
expanded
></b-input>
</b-field>
<b-field label="Port" label-position="on-border">
<b-input
ref="vmess_port"
v-model="vmess.port"
required
placeholder="端口号"
type="number"
expanded
></b-input>
</b-field>
<b-field label="ID" label-position="on-border">
<b-input
ref="vmess_id"
v-model="vmess.id"
required
placeholder="UserID"
expanded
></b-input>
</b-field>
<b-field label="AID" label-position="on-border">
<b-input
ref="vmess_aid"
v-model="vmess.aid"
placeholder="AlterID"
type="number"
min="0"
max="65535"
expanded
></b-input>
</b-field>
<b-field
v-show="vmess.type !== 'dtls'"
label="TLS"
label-position="on-border"
>
<b-select v-model="vmess.tls" expanded @input="handleNetworkChange">
<option value="none">关闭</option>
<option value="tls">开启</option>
</b-select>
</b-field>
<b-field label="Network" label-position="on-border">
<b-select
ref="vmess_net"
v-model="vmess.net"
expanded
required
@input="handleNetworkChange"
>
<option value="tcp">TCP</option>
<option value="kcp">mKCP</option>
<option value="ws">WebSocket</option>
<option value="h2">HTTP/2</option>
</b-select>
</b-field>
<b-field
v-show="vmess.net === 'tcp'"
label="Type"
label-position="on-border"
>
<b-select v-model="vmess.type" expanded>
<option value="none">不伪装</option>
<option value="http">伪装http</option>
</b-select>
</b-field>
<b-field
v-show="vmess.net === 'kcp'"
label="Type"
label-position="on-border"
>
<b-select v-model="vmess.type" expanded>
<option value="none">不伪装</option>
<option value="srtp">伪装视频通话(srtp)</option>
<option value="utp">伪装BT下载(uTP)</option>
<option value="wechat-video">伪装微信视频通话</option>
<option value="dtls">伪装DTLS1.2数据包(将强制开启TLS)</option>
<option value="wireguard">伪装WireGuard数据包</option>
</b-select>
</b-field>
<b-field
v-show="vmess.net === 'ws' || vmess.net === 'h2'"
label="Host"
label-position="on-border"
>
<b-input
v-model="vmess.host"
placeholder="伪装域名(host)"
expanded
></b-input>
</b-field>
<b-field
v-show="vmess.net === 'ws' || vmess.net === 'h2'"
label="Path"
label-position="on-border"
>
<b-input
v-model="vmess.path"
placeholder="路径(path)"
expanded
></b-input>
</b-field>
</b-tab-item>
<b-tab-item label="SS">
<b-field label="Name" label-position="on-border">
<b-input
v-model="ss.name"
placeholder="节点名称"
expanded
></b-input>
</b-field>
<b-field label="Address" label-position="on-border">
<b-input
ref="ss_server"
v-model="ss.server"
required
placeholder="IP / HOST"
expanded
></b-input>
</b-field>
<b-field label="Port" label-position="on-border">
<b-input
ref="ss_port"
v-model="ss.port"
required
placeholder="端口号"
type="number"
expanded
></b-input>
</b-field>
<b-field label="Password" label-position="on-border">
<b-input
ref="ss_password"
v-model="ss.password"
required
placeholder="密码"
expanded
></b-input>
</b-field>
<b-field label="Method" label-position="on-border">
<b-select
ref="ss_net"
v-model="ss.method"
expanded
required
@input="handleNetworkChange"
>
<option value="aes-256-cfb">aes-256-cfb</option>
<option value="aes-128-cfb">aes-128-cfb</option>
<option value="chacha20">chacha20</option>
<option value="chacha20-ietf">chacha20-ietf</option>
<option value="chacha20-poly1305">chacha20-poly1305</option>
<option value="aes-256-gcm">aes-256-gcm</option>
<option value="aes-128-gcm">aes-128-gcm</option>
</b-select>
</b-field>
</b-tab-item>
</b-tabs>
</section>
<footer v-if="!readonly" class="modal-card-foot flex-end">
<button class="button" type="button" @click="$parent.close()">
取消
</button>
<button class="button is-primary" @click="handleClickSubmit">
保存
</button>
</footer>
</div>
</template>

<script>
import { handleResponse } from "@/assets/js/utils";
import { Base64 } from "js-base64";
export default {
name: "ModalSetting",
props: {
which: {
type: Object,
default() {
return null;
}
},
readonly: {
type: Boolean,
default: false
}
},
data: () => ({
vmess: {
ps: "",
add: "",
port: "",
id: "",
aid: "0",
net: "tcp",
type: "none",
host: "",
path: "",
tls: "none",
v: "",
protocol: "vmess"
},
ss: {
method: "",
password: "",
server: "",
port: "",
name: "",
protocol: "ss"
},
tabChoice: 0
}),
mounted() {
if (this.which !== null) {
this.$axios({
url: apiRoot + "/sharingAddress",
method: "get",
params: {
touch: this.which
}
}).then(res => {
handleResponse(res, this, () => {
if (
res.data.data.sharingAddress.toLowerCase().startsWith("vmess://")
) {
this.vmess = this.resolveURL(res.data.data.sharingAddress);
this.tabChoice = 0;
} else if (
res.data.data.sharingAddress.toLowerCase().startsWith("ss://")
) {
this.ss = this.resolveURL(res.data.data.sharingAddress);
this.tabChoice = 1;
}
});
});
}
},
methods: {
resolveURL(url) {
if (url.toLowerCase().indexOf("vmess://") >= 0) {
let obj = JSON.parse(
Base64.decode(url.substring(url.indexOf("://") + 3))
);
obj.ps = unescape(obj.ps);
obj.tls = obj.tls || "none";
obj.type = obj.type || "none";
obj.protocol = "vmess";
return obj;
} else if (url.toLowerCase().indexOf("ss://") >= 0) {
const regexp = /ss:\/\/(.+)@(.+):(.+)#(.*)/;
let arr = regexp.exec(url);
arr[1] = Base64.decode(arr[1]);
let mp = arr[1].split(":");
return {
method: mp[0],
password: mp[1],
server: arr[2],
port: arr[3],
name: arr[4],
protocol: "ss"
};
}
return null;
},
generateURL(srcObj) {
let obj = {};
switch (srcObj.protocol) {
case "vmess":
//尽量减少生成的链接长度
obj = Object.assign({}, srcObj);
switch (obj.net) {
case "kcp":
case "tcp":
obj.path = "";
obj.host = "";
break;
default:
obj.type = "";
}
return "vmess://" + Base64.encode(JSON.stringify(obj));
case "ss":
/* ss://BASE64(method:password)@server:port#name */
return `ss://${Base64.encode(
`${srcObj.method}:${srcObj.password}`
)}@${srcObj.server}:${srcObj.port}#${srcObj.name}`;
}
return null;
},
handleNetworkChange() {
this.vmess.type = "none";
},
handleClickSubmit() {
let valid = true;
for (let k in this.$refs) {
if (!this.$refs.hasOwnProperty(k)) {
continue;
}
if (this.tabChoice === 0 && !k.startsWith("vmess")) {
continue;
}
if (this.tabChoice === 1 && !k.startsWith("ss")) {
continue;
}
let x = this.$refs[k];
if (
x.hasOwnProperty("checkHtml5Validity") &&
typeof x.checkHtml5Validity === "function" &&
!x.checkHtml5Validity()
) {
valid = false;
}
}
if (!valid) {
return;
}
let coded = "";
if (this.tabChoice === 0) {
coded = this.generateURL(this.vmess);
} else if (this.tabChoice === 1) {
coded = this.generateURL(this.ss);
}
this.$emit("submit", coded);
console.log(coded);
}
}
};
</script>

<style lang="scss">
.is-twitter .is-active a {
color: #4099ff !important;
}
.readonly {
pointer-events: none;
}
.same-width-5 li {
width: 5em;
}
</style>
Loading

0 comments on commit 1a4d6a7

Please sign in to comment.