Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
lingling | a54e9402ac | |
lingling | d7f4630047 | |
lingling | c8aeaad963 | |
lingling | 4cc3befc07 | |
lingling | 9c65fb2491 | |
lingling | 6f551540f0 | |
lingling | 771d4223bc | |
lingling | a51faba9af |
File diff suppressed because it is too large
Load Diff
|
@ -14,14 +14,19 @@
|
|||
"@quasar/extras": "^1.0.0",
|
||||
"@waiting/base64": "^4.2.9",
|
||||
"axios": "^1.2.1",
|
||||
"chai": "^5.1.2",
|
||||
"js-base64": "^3.7.5",
|
||||
"link-chang": "file:",
|
||||
"mocha": "^10.8.2",
|
||||
"quasar": "^2.6.0",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0",
|
||||
"vuex": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@quasar/app-vite": "^1.0.0",
|
||||
"@quasar/quasar-app-extension-testing": "^2.2.0",
|
||||
"@types/node": "^12.20.21",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
|
@ -33,7 +38,7 @@
|
|||
"typescript": "^4.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || ^16 || ^14.19",
|
||||
"node": "^22 || ^18 || ^16 || ^14.19",
|
||||
"npm": ">= 6.13.4",
|
||||
"yarn": ">= 1.21.1"
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ module.exports = configure(function (/* ctx */) {
|
|||
node: 'node16',
|
||||
},
|
||||
gzip: true,
|
||||
vueRouterMode: 'history', // available values: 'hash', 'history'
|
||||
vueRouterMode: 'hash', // available values: 'hash', 'history'
|
||||
// vueRouterBase,
|
||||
// vueDevtools,
|
||||
// vueOptionsAPI: false,
|
||||
|
@ -80,6 +80,7 @@ module.exports = configure(function (/* ctx */) {
|
|||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
|
||||
devServer: {
|
||||
// https: true
|
||||
port: 9090,
|
||||
open: true, // opens browser window automatically
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"@quasar/testing": {
|
||||
"harnesses": [
|
||||
"unit-jest@beta"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
import { decode, encode } from 'js-base64'
|
||||
import { getdata } from 'src/api/api';
|
||||
import { VlessLink, vmess } from 'src/models/models';
|
||||
import { is_ip } from './comm';
|
||||
import { vmessDefault } from 'src/config';
|
||||
|
||||
/**
|
||||
* 创建直连vmess
|
||||
* @param ip 服务器IP地址
|
||||
* @returns vmess链接
|
||||
*/
|
||||
async function CreatVmessDirect(ip: string): Promise<string> {
|
||||
let tmp = ''
|
||||
const api = new getdata;
|
||||
const obj = JSON.parse(decode(vmessDefault))
|
||||
const array = ip.split(/[\s\n]/)
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
if (!is_ip(array[index])) {
|
||||
return ''
|
||||
}
|
||||
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
const name = await api.get_country(array[index])
|
||||
obj.ps = name + array[index]
|
||||
obj.add = array[index]
|
||||
tmp += 'vmess://' + encode(JSON.stringify(obj)) + '\n'
|
||||
}
|
||||
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
function ChangVmessServer() {
|
||||
return 0
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param type 生成链接的类型 vmess |vless
|
||||
*/
|
||||
function CreateLink(type: 'vmess' | 'vless', linkinfo: vmess | VlessLink) {
|
||||
let tmp = ''
|
||||
switch (true) {
|
||||
case type === 'vless':
|
||||
if ('uuid' in linkinfo) {
|
||||
tmp = createVlessLink(linkinfo)
|
||||
}
|
||||
break;
|
||||
case type === 'vmess':
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
function parseVlessLink(link: string): VlessLink | null {
|
||||
const regex = /^vless:/;
|
||||
const match = link.match(regex);
|
||||
|
||||
if (!match) {
|
||||
console.error('Invalid VLESS link format.');
|
||||
return null;
|
||||
}
|
||||
|
||||
const uuid = match[1];
|
||||
const host = match[2];
|
||||
const port = parseInt(match[3], 10);
|
||||
const params = parseParams(match[4]); // 解析参数
|
||||
const name = match[5] ? decodeURIComponent(match[5].slice(1)) : undefined; // 去掉 "#" 并解码
|
||||
|
||||
return {
|
||||
uuid,
|
||||
host,
|
||||
port,
|
||||
params,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
function parseParams(paramsString?: string): Record<string, string> | undefined {
|
||||
if (!paramsString || !paramsString.startsWith('?')) return undefined;
|
||||
|
||||
return paramsString
|
||||
.slice(1) // 去掉开头的 "?"
|
||||
.split('&') // 分割每个键值对
|
||||
.reduce<Record<string, string>>((acc, pair) => {
|
||||
const [key, value] = pair.split('=');
|
||||
if (key) acc[key] = value || ''; // 处理可能的无值参数
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建vless链接
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
function createVlessLink({ uuid, host, port, params, name }: VlessLink): string {
|
||||
// 基本的 VLESS 链接格式
|
||||
let link = `vless://${uuid}@${host}:${port}`;
|
||||
|
||||
// 如果有查询参数,则拼接它们
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
const queryString = new URLSearchParams(params).toString();
|
||||
link += `?${queryString}`;
|
||||
}
|
||||
|
||||
// 如果有名称字段,将名称放在 # 后面
|
||||
if (name) {
|
||||
link += `#${encodeURIComponent(name)}`;
|
||||
}
|
||||
|
||||
return link + '\n';
|
||||
}
|
||||
|
||||
export { CreatVmessDirect, ChangVmessServer, CreateLink, parseVlessLink }
|
|
@ -0,0 +1,82 @@
|
|||
import { copyToClipboard, QVueGlobals } from 'quasar'
|
||||
|
||||
|
||||
/**
|
||||
* 复制文本
|
||||
* @param string
|
||||
*/
|
||||
function copy(string: string, $q: QVueGlobals) {
|
||||
|
||||
copyToClipboard(string)
|
||||
.then(() => {
|
||||
|
||||
// success!
|
||||
$q.notify({
|
||||
message: '复制成功',
|
||||
color: 'positive',
|
||||
position: 'top'
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
// fail
|
||||
$q.notify({
|
||||
message: '复制失败',
|
||||
color: 'negative'
|
||||
})
|
||||
})
|
||||
}
|
||||
/**
|
||||
*根据延迟计算颜色
|
||||
* @param bili 数值 延迟单位ms
|
||||
* @returns
|
||||
*/
|
||||
function getColorForDelay(delay: number): string {
|
||||
// 限制延迟在200到2000之间
|
||||
const clampedDelay = Math.min(Math.max(delay, 200), 2000);
|
||||
|
||||
// 将延迟从[200, 2000]映射到[0, 1]
|
||||
const normalizedDelay = (clampedDelay - 200) / (2000 - 200);
|
||||
|
||||
// 计算从绿色到红色的颜色变化
|
||||
const green = 255 - normalizedDelay * 255;
|
||||
const red = normalizedDelay * 255;
|
||||
|
||||
// 返回 RGB 颜色格式
|
||||
return `rgb(${Math.round(red)}, ${Math.round(green)}, 0)`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据延迟计算颜色 就三种颜色
|
||||
* @param bili
|
||||
* @returns
|
||||
*/
|
||||
function signal_style(bili: number) {
|
||||
let tmp = ''
|
||||
if (bili >= 0 && bili <= 700) {
|
||||
tmp = 'green'
|
||||
} else if (bili > 700 && bili < 1400) {
|
||||
tmp = '#FF9800'
|
||||
} else {
|
||||
tmp = 'red'
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
const is_ip = (ip: string) => {
|
||||
const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
|
||||
return reg.test(ip);
|
||||
}
|
||||
function extractIPv4(ip: string): string | null {
|
||||
const ipv4Regex = /(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)/;
|
||||
const matches = ip.match(ipv4Regex);
|
||||
|
||||
// 如果没有匹配到任何IPv4地址,返回 null
|
||||
if (!matches) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 返回第一个匹配到的IPv4地址
|
||||
return matches[0];
|
||||
}
|
||||
|
||||
export { copy, getColorForDelay, signal_style, is_ip, extractIPv4 }
|
|
@ -0,0 +1,152 @@
|
|||
import { Base64 } from 'js-base64';
|
||||
|
||||
class VLESSQuery {
|
||||
constructor(
|
||||
public security: string,
|
||||
public alpn: string[],
|
||||
public sni: string,
|
||||
public fp: string,
|
||||
public sid: string,
|
||||
public pbk: string,
|
||||
public flow: string,
|
||||
public encryption: string,
|
||||
public type: string,
|
||||
public headerType: string,
|
||||
public path: string,
|
||||
public host: string
|
||||
) { }
|
||||
}
|
||||
|
||||
class VLESS {
|
||||
constructor(
|
||||
public name: string,
|
||||
public uuid: string,
|
||||
public server: string,
|
||||
public port: number,
|
||||
public query: VLESSQuery
|
||||
) { }
|
||||
|
||||
// Base64 解码函数
|
||||
// private static base64Decode(str: string): string {
|
||||
// return Buffer.from(str, 'base64').toString('utf-8');
|
||||
// }
|
||||
|
||||
// 编码 VLESS URL
|
||||
encodeURL(): string {
|
||||
const url = new URL(`vless://${this.uuid}@${this.server}:${this.port}`);
|
||||
|
||||
// 设置 Query 参数
|
||||
const query = url.searchParams;
|
||||
query.set('security', this.query.security);
|
||||
query.set('sni', this.query.sni);
|
||||
query.set('fp', this.query.fp);
|
||||
query.set('sid', this.query.sid);
|
||||
query.set('pbk', this.query.pbk);
|
||||
query.set('flow', this.query.flow);
|
||||
query.set('encryption', this.query.encryption);
|
||||
query.set('type', this.query.type);
|
||||
query.set('headerType', this.query.headerType);
|
||||
query.set('path', this.query.path);
|
||||
query.set('host', this.query.host);
|
||||
|
||||
// 移除空值参数
|
||||
for (const [key, value] of query) {
|
||||
if (value === '') {
|
||||
query.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有 name,设置为 Fragment
|
||||
if (this.name) {
|
||||
url.hash = this.name;
|
||||
} else {
|
||||
url.hash = `${this.server}:${this.port}`;
|
||||
}
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
// 解码 VLESS URL
|
||||
static decodeURL(urlStr: string): VLESS {
|
||||
if (!urlStr.startsWith('vless://')) {
|
||||
throw new Error(`Invalid vless URL: ${urlStr}`);
|
||||
}
|
||||
|
||||
const decodedUrl = urlStr.split('@');
|
||||
const url = new URL(`http://${decodedUrl[1]}`);
|
||||
const regex = /[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}/;
|
||||
const match = urlStr.match(regex);
|
||||
const uuid = match[0];
|
||||
const hostname = url.hostname;
|
||||
const port = parseInt(url.port, 10);
|
||||
|
||||
const query = url.searchParams;
|
||||
const encryption = query.get('encryption') || '';
|
||||
const security = query.get('security') || '';
|
||||
const type = query.get('type') || '';
|
||||
const flow = query.get('flow') || '';
|
||||
const headerType = query.get('headerType') || '';
|
||||
const pbk = query.get('pbk') || '';
|
||||
const sid = query.get('sid') || '';
|
||||
const fp = query.get('fp') || '';
|
||||
const alpns = query.get('alpn') || '';
|
||||
const alpn = alpns ? alpns.split(',') : [];
|
||||
const sni = query.get('sni') || '';
|
||||
const path = query.get('path') || '';
|
||||
const host = query.get('host') || '';
|
||||
|
||||
// 获取 name,若为空则使用 `hostname:port`
|
||||
const name = url.hash ? decodeURIComponent(url.hash.slice(1)) : `${hostname}:${port}`;
|
||||
|
||||
const queryObj = new VLESSQuery(
|
||||
security,
|
||||
alpn,
|
||||
sni,
|
||||
fp,
|
||||
sid,
|
||||
pbk,
|
||||
flow,
|
||||
encryption,
|
||||
type,
|
||||
headerType,
|
||||
path,
|
||||
host
|
||||
);
|
||||
|
||||
return new VLESS(name, uuid, hostname, port, queryObj);
|
||||
}
|
||||
}
|
||||
|
||||
// // 示例用法
|
||||
// function callVLESS() {
|
||||
// const vless = new VLESS(
|
||||
// 'Sharon-香港',
|
||||
// '6adb4f43-9813-45f4-abf8-772be7db08sd',
|
||||
// 'ss.com',
|
||||
// 443,
|
||||
// new VLESSQuery(
|
||||
// 'reality',
|
||||
// ['http/1.1'],
|
||||
// 'ss.com',
|
||||
// 'chrome',
|
||||
// '',
|
||||
// 'g-oxbqigzCaXqARxuyD2_vbTYeMD9zn8wnTo02S69QM',
|
||||
// 'xtls-rprx-vision',
|
||||
// 'none',
|
||||
// 'tcp',
|
||||
// 'none',
|
||||
// '',
|
||||
// ''
|
||||
// )
|
||||
// );
|
||||
|
||||
// const encoded = vless.encodeURL();
|
||||
// console.log('Encoded VLESS URL:', encoded);
|
||||
|
||||
// const decoded = VLESS.decodeURL(encoded);
|
||||
// console.log('Decoded VLESS:', decoded);
|
||||
// }
|
||||
|
||||
// callVLESS();
|
||||
|
||||
export { VLESS }
|
|
@ -0,0 +1,122 @@
|
|||
class Vmess {
|
||||
add?: string;
|
||||
aid?: any; // This could be number or string depending on usage
|
||||
alpn?: string;
|
||||
fp?: string;
|
||||
host?: string;
|
||||
id?: string;
|
||||
net?: string;
|
||||
path?: string;
|
||||
port?: any; // This could be a number or string
|
||||
ps?: string;
|
||||
scy?: string;
|
||||
sni?: string;
|
||||
tls?: string;
|
||||
type?: string;
|
||||
v?: string;
|
||||
|
||||
constructor(data: {
|
||||
add?: string;
|
||||
aid?: any;
|
||||
alpn?: string;
|
||||
fp?: string;
|
||||
host?: string;
|
||||
id?: string;
|
||||
net?: string;
|
||||
path?: string;
|
||||
port?: any;
|
||||
ps?: string;
|
||||
scy?: string;
|
||||
sni?: string;
|
||||
tls?: string;
|
||||
type?: string;
|
||||
v?: string;
|
||||
}) {
|
||||
this.add = data.add;
|
||||
this.aid = data.aid;
|
||||
this.alpn = data.alpn;
|
||||
this.fp = data.fp;
|
||||
this.host = data.host;
|
||||
this.id = data.id;
|
||||
this.net = data.net;
|
||||
this.path = data.path;
|
||||
this.port = data.port;
|
||||
this.ps = data.ps;
|
||||
this.scy = data.scy;
|
||||
this.sni = data.sni;
|
||||
this.tls = data.tls;
|
||||
this.type = data.type;
|
||||
this.v = data.v;
|
||||
}
|
||||
|
||||
// 编码 Vmess URL
|
||||
encodeURL(): string {
|
||||
// 如果备注为空,则使用服务器地址 + 端口
|
||||
if (!this.ps) {
|
||||
this.ps = `${this.add}:${this.port}`;
|
||||
}
|
||||
// 如果版本为空,则默认为 2
|
||||
if (!this.v) {
|
||||
this.v = '2';
|
||||
}
|
||||
|
||||
const param = JSON.stringify(this);
|
||||
return 'vmess://' + this.base64Encode(param);
|
||||
}
|
||||
|
||||
// 解码 Vmess URL
|
||||
static decodeURL(url: string): Vmess {
|
||||
if (!url.startsWith('vmess://')) {
|
||||
throw new Error(`Invalid vmess URL: ${url}`);
|
||||
}
|
||||
const param = url.slice(8); // Remove "vmess://"
|
||||
const decoded = Vmess.base64Decode(param.trim());
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(decoded);
|
||||
if (parsed.scy === '') {
|
||||
parsed.scy = 'auto';
|
||||
}
|
||||
if (!parsed.ps) {
|
||||
parsed.ps = parsed.add + ':' + parsed.port;
|
||||
}
|
||||
return new Vmess(parsed);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse VMESS URL: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Base64 编码
|
||||
private base64Encode(str: string): string {
|
||||
return Buffer.from(str, 'utf-8').toString('base64');
|
||||
}
|
||||
|
||||
// Base64 解码
|
||||
private static base64Decode(str: string): string {
|
||||
return Buffer.from(str, 'base64').toString('utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
// 开发者测试
|
||||
function callVmessURL() {
|
||||
const vmess = new Vmess({
|
||||
add: 'xx.xxx.ru',
|
||||
port: '2095',
|
||||
aid: 0,
|
||||
scy: 'auto',
|
||||
net: 'ws',
|
||||
type: 'none',
|
||||
id: '7a737f41-b792-4260-94ff-3d864da67380',
|
||||
host: 'xx.xxx.ru',
|
||||
path: '/',
|
||||
tls: ''
|
||||
});
|
||||
|
||||
const encoded = vmess.encodeURL();
|
||||
console.log('Encoded VMESS URL:', encoded);
|
||||
|
||||
const decoded = Vmess.decodeURL(encoded);
|
||||
console.log('Decoded VMESS:', decoded);
|
||||
}
|
||||
|
||||
callVmessURL();
|
|
@ -1,13 +1,32 @@
|
|||
import { api } from 'src/boot/axios';
|
||||
class getdata {
|
||||
async get_server() {
|
||||
return await api.get('https://api.shagain.club/api.php?type=list');
|
||||
return await api.get('https://api.giaogiao.uk/server/getlist');
|
||||
}
|
||||
async text_server(url: string) {
|
||||
return await api.post('https://api.shagain.club/textserver', { url });
|
||||
const res = await api.post(
|
||||
'https://service-7hslob28-1258902677.nj.apigw.tencentcs.com/release/',
|
||||
{ url }
|
||||
);
|
||||
|
||||
const tmp = { 'is_online': res.data.sataus == 400, 'time': res.data.time }
|
||||
return tmp
|
||||
}
|
||||
async get_country(ip: string) {
|
||||
return await api.post('https://api.shagain.club/api.php?type=searchip', { ip });
|
||||
const res = await api.post('https://api.giaogiao.uk/server/searchip', {
|
||||
ip,
|
||||
});
|
||||
// res = res.data.country
|
||||
// if (typeof (res) == 'string') { return res }
|
||||
// console.log(res.data.data.country)
|
||||
return res.data.data.country
|
||||
|
||||
}
|
||||
async get_server_ms(ip: string, port: number, to_ip: string, istls: number) {
|
||||
const http = istls == 0 ? 'http' : 'https'
|
||||
return this.text_server(`${http}://${ip}:${port}/${to_ip}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { getdata };
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
<template>
|
||||
<q-item
|
||||
clickable
|
||||
tag="a"
|
||||
target="_blank"
|
||||
:href="link"
|
||||
>
|
||||
<q-item-section
|
||||
v-if="icon"
|
||||
avatar
|
||||
>
|
||||
<q-icon :name="icon" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>
|
||||
<q-item-label>{{ title }}</q-item-label>
|
||||
<q-item-label caption>{{ caption }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EssentialLink',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
|
||||
caption: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
link: {
|
||||
type: String,
|
||||
default: '#'
|
||||
},
|
||||
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -1,64 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<p>{{ title }}</p>
|
||||
<ul>
|
||||
<li v-for="todo in todos" :key="todo.id" @click="increment">
|
||||
{{ todo.id }} - {{ todo.content }}
|
||||
</li>
|
||||
</ul>
|
||||
<p>Count: {{ todoCount }} / {{ meta.totalCount }}</p>
|
||||
<p>Active: {{ active ? 'yes' : 'no' }}</p>
|
||||
<p>Clicks on todos: {{ clickCount }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
PropType,
|
||||
computed,
|
||||
ref,
|
||||
toRef,
|
||||
Ref,
|
||||
} from 'vue';
|
||||
import { Todo, Meta } from './models';
|
||||
|
||||
function useClickCount() {
|
||||
const clickCount = ref(0);
|
||||
function increment() {
|
||||
clickCount.value += 1
|
||||
return clickCount.value;
|
||||
}
|
||||
|
||||
return { clickCount, increment };
|
||||
}
|
||||
|
||||
function useDisplayTodo(todos: Ref<Todo[]>) {
|
||||
const todoCount = computed(() => todos.value.length);
|
||||
return { todoCount };
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExampleComponent',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
todos: {
|
||||
type: Array as PropType<Todo[]>,
|
||||
default: () => []
|
||||
},
|
||||
meta: {
|
||||
type: Object as PropType<Meta>,
|
||||
required: true
|
||||
},
|
||||
active: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
return { ...useClickCount(), ...useDisplayTodo(toRef(props, 'todos')) };
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -8,8 +8,7 @@
|
|||
:style="{ color: signal_style }" /><span>{{ time }}ms</span></div>
|
||||
<q-input v-model="outtext" filled autogrow readonly />
|
||||
<div style="text-align: center;margin-top: 1rem;">
|
||||
<q-btn color="white" text-color="black" @click="copy('link')" label="复制" />
|
||||
<!-- <q-btn color="white" text-color="black" @click="copy('sub')" label="复制订阅" /> -->
|
||||
<q-btn color="white" text-color="black" @click="copy_link()" label="复制" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,11 +19,13 @@ import {
|
|||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
onMounted,
|
||||
} from 'vue';
|
||||
import { copyToClipboard } from 'quasar'
|
||||
import { encode, decode } from 'js-base64';
|
||||
import { useQuasar } from 'quasar'
|
||||
import { getdata } from 'src/api/api'
|
||||
import { copy, extractIPv4, getColorForDelay } from 'src/Util/comm';
|
||||
import { vlessLink } from 'src/config';
|
||||
import { getdata } from 'src/api/api';
|
||||
import { CreateLink } from 'src/Util/Link';
|
||||
export default defineComponent({
|
||||
name: 'LinkItem',
|
||||
props: {
|
||||
|
@ -32,88 +33,46 @@ export default defineComponent({
|
|||
type: Object,
|
||||
required: true
|
||||
},
|
||||
text: {
|
||||
type: String
|
||||
names: {
|
||||
type: Array as () => string[],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const online = ref(false);
|
||||
const isonline = (ip: string) => {
|
||||
let http = props.serve.istls == 0 ? 'http' : 'https'
|
||||
api.text_server(`${http}://${props.serve.ip}:${props.serve.port}/${ip}`).then(res => {
|
||||
online.value = res.data.sataus == 400
|
||||
time.value = res.data.time
|
||||
})
|
||||
}
|
||||
const api = new getdata;
|
||||
const $q = useQuasar()
|
||||
const time = ref(0)
|
||||
const outtext = computed(() => {
|
||||
let tmp = ''
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
let text = props.text!.replace(/\ +/g, '');
|
||||
text = text.replace(/[\r\n]/g, '');
|
||||
let arr = text.split('vmess://');
|
||||
for (let iterator of arr) {
|
||||
if (!(iterator.length > 0)) continue
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
online.value = false
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
let obj = JSON.parse(decode(iterator));
|
||||
obj.add = props.serve.ip
|
||||
obj.port = props.serve.port
|
||||
obj.host = props.serve.host
|
||||
obj.tls = props.serve.istls == 0 ? '' : 'tls'
|
||||
let reg = new RegExp(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
|
||||
obj.path = '/' + obj.ps.match(reg)[0];
|
||||
let ip = obj.ps.match(reg)[0]
|
||||
isonline(ip)
|
||||
tmp += 'vmess://' + encode(JSON.stringify(obj)) + '\n'
|
||||
}
|
||||
|
||||
return tmp
|
||||
})
|
||||
const outtext = ref('')
|
||||
const api = new getdata;
|
||||
const signal_style = computed(() => {
|
||||
let tmp = ''
|
||||
if (time.value >= 0 && time.value <= 700) {
|
||||
tmp = 'green'
|
||||
} else if (time.value > 700 && time.value < 1400) {
|
||||
tmp = '#FF9800'
|
||||
} else {
|
||||
tmp = 'red'
|
||||
return getColorForDelay(time.value)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
for (let index = 0; index < props.names.length; index++) {
|
||||
let ip = extractIPv4(props.names[index])
|
||||
if (ip == null) { continue }
|
||||
let vless = vlessLink
|
||||
vless.name = props.names[index]
|
||||
vless.host = props.serve.host
|
||||
vless.port = props.serve.port
|
||||
if (props.serve.istls == 1 && vless.params) {
|
||||
vless.params.security = 'tls'
|
||||
vless.params.path = `/${ip}`
|
||||
vless.params.host = props.serve.host
|
||||
vless.params.sni = props.serve.host
|
||||
}
|
||||
return tmp
|
||||
api.get_server_ms(props.serve.host, props.serve.port, ip, props.serve.istls).then((res) => {
|
||||
console.log(res)
|
||||
time.value = res.time
|
||||
online.value = res.is_online
|
||||
})
|
||||
const copy = (type: string) => {
|
||||
let tmp = ''
|
||||
switch (type) {
|
||||
case 'sub':
|
||||
tmp = `http://149.129.107.38/link.php?link=${outtext.value}`
|
||||
break;
|
||||
case 'link':
|
||||
tmp = outtext.value
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
outtext.value += CreateLink('vless', vless)
|
||||
}
|
||||
copyToClipboard(tmp)
|
||||
.then(() => {
|
||||
// success!
|
||||
$q.notify({
|
||||
message: '复制成功',
|
||||
color: 'positive',
|
||||
position: 'top'
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// fail
|
||||
$q.notify({
|
||||
message: '复制失败',
|
||||
color: 'negative'
|
||||
})
|
||||
})
|
||||
}
|
||||
return { props, outtext, copy, online, time, signal_style };
|
||||
const copy_link = () => { copy(outtext.value, $q) }
|
||||
return { props, online, time, signal_style, outtext, copy_link };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
<div v-show="online" style="text-align: center;"><q-icon title="在线" name="signal_cellular_alt"
|
||||
:style="{ color: signal_style }" /><span>{{ time }}ms</span></div>
|
||||
<q-input v-model="outtext" filled autogrow readonly />
|
||||
<div style="text-align: center;margin-top: 1rem;display: flex;justify-content: space-around;">
|
||||
<q-btn color="white" text-color="black" @click="copy('link')" label="复制" />
|
||||
<!-- <q-btn color="white" text-color="black" @click="copy('sub')" label="复制订阅" /> -->
|
||||
<div style="text-align: center;margin-top: 1rem;">
|
||||
<q-btn color="white" text-color="black" @click="copy_link()" label="复制" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,99 +18,54 @@ import {
|
|||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
onMounted,
|
||||
} from 'vue';
|
||||
import { copyToClipboard } from 'quasar'
|
||||
import { encode, decode } from 'js-base64';
|
||||
import { useQuasar } from 'quasar'
|
||||
import { getdata } from 'src/api/api'
|
||||
import { vlessLink } from 'src/config';
|
||||
import { copy, extractIPv4, getColorForDelay } from 'src/Util/comm';
|
||||
import { CreateLink } from 'src/Util/Link';
|
||||
export default defineComponent({
|
||||
name: 'LinkItem',
|
||||
props: {
|
||||
text: {
|
||||
type: String
|
||||
names: {
|
||||
type: Array as () => string[],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const time = ref(0);
|
||||
const online = ref(false);
|
||||
const isonline = (ip: string) => {
|
||||
let http = 'http'
|
||||
api.text_server(`${http}://${ip}:9000/`).then(res => {
|
||||
online.value = res.data.sataus == 400
|
||||
time.value = res.data.time
|
||||
})
|
||||
}
|
||||
const def_link = 'ew0KICAidiI6ICIyIiwNCiAgInBzIjogIjAiLA0KICAiYWRkIjogIjE4NS4yMTguNi4xMDgiLA0KICAicG9ydCI6ICI5MDAwIiwNCiAgImlkIjogIjJlZTU3ODA2LWY2ZTQtNDgyYS1lZjA4LTczNjBjMDRjZDNlNSIsDQogICJhaWQiOiAiMCIsDQogICJzY3kiOiAiYXV0byIsDQogICJuZXQiOiAid3MiLA0KICAidHlwZSI6ICJub25lIiwNCiAgImhvc3QiOiAiIiwNCiAgInBhdGgiOiAiLyIsDQogICJ0bHMiOiAiIiwNCiAgInNuaSI6ICIiLA0KICAiYWxwbiI6ICIiDQp9'
|
||||
const api = new getdata;
|
||||
const $q = useQuasar()
|
||||
const outtext = computed(() => {
|
||||
let tmp = ''
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
let text = props.text!.replace(/\ +/g, '');
|
||||
text = text.replace(/[\r\n]/g, '');
|
||||
let arr = text.split('vmess://');
|
||||
for (let iterator of arr) {
|
||||
if (!(iterator.length > 0)) continue
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
online.value = false
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
let obj = JSON.parse(decode(def_link));
|
||||
let obj_old = JSON.parse(decode(iterator));
|
||||
let reg = new RegExp(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
|
||||
let ip = obj_old.ps.match(reg)[0]
|
||||
obj.ps = obj_old.ps
|
||||
obj.add = ip
|
||||
obj.port = 9000
|
||||
obj.host = ''
|
||||
obj.tls = ''
|
||||
obj.path = '/';
|
||||
isonline(ip)
|
||||
tmp += 'vmess://' + encode(JSON.stringify(obj)) + '\n'
|
||||
}
|
||||
const time = ref(0)
|
||||
const outtext = ref('')
|
||||
|
||||
return tmp
|
||||
})
|
||||
const signal_style = computed(() => {
|
||||
let tmp = ''
|
||||
if (time.value >= 0 && time.value <= 700) {
|
||||
tmp = 'green'
|
||||
} else if (time.value > 700 && time.value < 1400) {
|
||||
tmp = '#FF9800'
|
||||
} else {
|
||||
tmp = 'red'
|
||||
return getColorForDelay(time.value)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
for (let index = 0; index < props.names.length; index++) {
|
||||
let ip = extractIPv4(props.names[index])
|
||||
if (ip == null) { continue }
|
||||
let vless = vlessLink
|
||||
|
||||
vless.name = props.names[index]
|
||||
vless.host = ip
|
||||
vless.port = 9000
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
vless.params!.path = '/'
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
vless.params!.security = 'none'
|
||||
api.get_server_ms(ip, 9000, '', 0).then((res) => {
|
||||
time.value = res.time
|
||||
online.value = res.is_online
|
||||
})
|
||||
outtext.value += CreateLink('vless', vless)
|
||||
}
|
||||
return tmp
|
||||
})
|
||||
const copy = (type: string) => {
|
||||
let tmp = ''
|
||||
switch (type) {
|
||||
case 'sub':
|
||||
tmp = `http://149.129.107.38/link.php?link=${outtext.value}`
|
||||
break;
|
||||
case 'link':
|
||||
tmp = outtext.value
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
copyToClipboard(tmp)
|
||||
.then(() => {
|
||||
// success!
|
||||
$q.notify({
|
||||
message: '复制成功',
|
||||
color: 'positive',
|
||||
position: 'top'
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// fail
|
||||
$q.notify({
|
||||
message: '复制失败',
|
||||
color: 'negative'
|
||||
})
|
||||
})
|
||||
}
|
||||
return { props, outtext, copy, online, signal_style, time };
|
||||
const copy_link = () => { copy(outtext.value, $q) }
|
||||
return { props, online, time, signal_style, outtext, copy_link };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
export interface Todo {
|
||||
id: number;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
totalCount: number;
|
||||
}
|
||||
export interface server {
|
||||
host: string;
|
||||
id: number;
|
||||
ip: string;
|
||||
istls: number;
|
||||
port: number;
|
||||
tips: string;
|
||||
}
|
||||
|
||||
export interface vmess {
|
||||
v: string;
|
||||
ps: string;
|
||||
add: string;
|
||||
port: number;
|
||||
id: string;
|
||||
aid: string;
|
||||
scy: string;
|
||||
net: string;
|
||||
type: string;
|
||||
host: string;
|
||||
path: string;
|
||||
tls: string;
|
||||
sni: string;
|
||||
alpn: string
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { VlessLink } from 'src/models/models';
|
||||
|
||||
/**
|
||||
* vmess模板链接
|
||||
*/
|
||||
const vmessDefault = 'ew0KICAidiI6ICIyIiwNCiAgInBzIjogIjAiLA0KICAiYWRkIjogIjE4NS4yMTguNi4xMDgiLA0KICAicG9ydCI6ICI5MDAwIiwNCiAgImlkIjogIjJlZTU3ODA2LWY2ZTQtNDgyYS1lZjA4LTczNjBjMDRjZDNlNSIsDQogICJhaWQiOiAiMCIsDQogICJzY3kiOiAiYXV0byIsDQogICJuZXQiOiAid3MiLA0KICAidHlwZSI6ICJub25lIiwNCiAgImhvc3QiOiAiIiwNCiAgInBhdGgiOiAiLyIsDQogICJ0bHMiOiAiIiwNCiAgInNuaSI6ICIiLA0KICAiYWxwbiI6ICIiDQp9'
|
||||
const vlessDefault = 'vless://2ee57806-f6e4-482a-ef08-7360c04cd3e5@1.1.1.1:9000?encryption=none&security=none&type=ws&path=%2F#%E9%9F%A9%E5%9B%BD1.1.1.1'
|
||||
/**
|
||||
* vless对象模板
|
||||
*/
|
||||
const vlessLink: VlessLink = {
|
||||
uuid: '2ee57806-f6e4-482a-ef08-7360c04cd3e5',
|
||||
host: '150.109.81.208',
|
||||
port: 9000,
|
||||
params: {
|
||||
encryption: 'none',
|
||||
security: 'none',
|
||||
type: 'ws',
|
||||
path: '/'
|
||||
},
|
||||
name: '韩国150.109.81.208'
|
||||
};
|
||||
export { vmessDefault, vlessDefault, vlessLink }
|
|
@ -0,0 +1,67 @@
|
|||
export interface Todo {
|
||||
id: number;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
totalCount: number;
|
||||
}
|
||||
/**
|
||||
* 服务器结构体
|
||||
*/
|
||||
export interface server {
|
||||
host: string;
|
||||
id: number;
|
||||
ip: string;
|
||||
istls: number;
|
||||
port: number;
|
||||
tips: string;
|
||||
}
|
||||
/**
|
||||
* vmess结构体
|
||||
*/
|
||||
export interface vmess {
|
||||
v: string;
|
||||
ps: string;
|
||||
add: string;
|
||||
port: number;
|
||||
id: string;
|
||||
aid: string;
|
||||
scy: string;
|
||||
net: string;
|
||||
type: string;
|
||||
host: string;
|
||||
path: string;
|
||||
tls: string;
|
||||
sni: string;
|
||||
alpn: string
|
||||
}
|
||||
/**
|
||||
* vless结构体
|
||||
*/
|
||||
export interface vless {
|
||||
add: string;
|
||||
port: number;
|
||||
uuid: string;
|
||||
encryption: string;
|
||||
/**传输协议 */
|
||||
net: string;
|
||||
/**伪装类型 */
|
||||
type: string;
|
||||
/**地址 */
|
||||
host: string;
|
||||
/**路径 */
|
||||
path: string;
|
||||
/**tls */
|
||||
tls: string;
|
||||
/**sni */
|
||||
sni: string;
|
||||
name: string
|
||||
}
|
||||
export interface VlessLink {
|
||||
uuid: string;
|
||||
host: string;
|
||||
port: number;
|
||||
params?: Record<string, string>;
|
||||
name?: string; // 允许传入名称
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
<q-input v-model="text" filled autogrow placeholder="IP" />
|
||||
<div style="height:1rem"></div>
|
||||
<q-input v-model="outlink" filled autogrow />
|
||||
<q-btn color="white" text-color="black" @click="copy" label="复制" />
|
||||
<q-btn color="white" text-color="black" @click="copy_link" label="复制" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,69 +19,46 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
import { getdata } from 'src/api/api'
|
||||
import { decode, encode } from 'js-base64';
|
||||
import { copyToClipboard, useQuasar } from 'quasar';
|
||||
import { copy, is_ip } from 'src/Util/comm';
|
||||
import { CreateLink, CreatVmessDirect } from 'src/Util/Link';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { vlessLink } from 'src/config';
|
||||
import { getdata } from 'src/api/api';
|
||||
import { VLESS } from 'src/Util/node/Vless';
|
||||
export default defineComponent({
|
||||
name: 'CreateLink',
|
||||
setup() {
|
||||
const $q = useQuasar()
|
||||
const api = new getdata;
|
||||
const text = ref('');
|
||||
const outlink = ref('');
|
||||
const def_link = 'ew0KICAidiI6ICIyIiwNCiAgInBzIjogIjAiLA0KICAiYWRkIjogIjE4NS4yMTguNi4xMDgiLA0KICAicG9ydCI6ICI5MDAwIiwNCiAgImlkIjogIjJlZTU3ODA2LWY2ZTQtNDgyYS1lZjA4LTczNjBjMDRjZDNlNSIsDQogICJhaWQiOiAiMCIsDQogICJzY3kiOiAiYXV0byIsDQogICJuZXQiOiAid3MiLA0KICAidHlwZSI6ICJub25lIiwNCiAgImhvc3QiOiAiIiwNCiAgInBhdGgiOiAiLyIsDQogICJ0bHMiOiAiIiwNCiAgInNuaSI6ICIiLA0KICAiYWxwbiI6ICIiDQp9'
|
||||
const is_ip = (ip: string) => {
|
||||
var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
|
||||
return reg.test(ip);
|
||||
}
|
||||
|
||||
const $q = useQuasar()
|
||||
const api = new getdata();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
watch(() => text.value, (newValue, oldValue) => { //直接监听
|
||||
let obj = JSON.parse(decode(def_link))
|
||||
watch(() => text.value, async (newValue, oldValue) => { //直接监听
|
||||
let array = newValue.split(/[\s\n]/)
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
if (!is_ip(array[index])) {
|
||||
outlink.value = 'erroe ip'
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
outlink.value = ''
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
api.get_country(array[index]).then(res => {
|
||||
// let name = res.data.country == '中国' ? res.data.province : res.data.country
|
||||
let name = res.data.country
|
||||
obj.ps = name + array[index]
|
||||
obj.add = array[index]
|
||||
outlink.value += 'vmess://' + encode(JSON.stringify(obj)) + '\n'
|
||||
})
|
||||
|
||||
// outlink.value += await CreatVmessDirect(array[index])
|
||||
let vless = vlessLink
|
||||
let country = await api.get_country(array[index])
|
||||
console.log(country)
|
||||
vless.name = country + array[index]
|
||||
vless.host = array[index]
|
||||
outlink.value += CreateLink('vless', vless) + '\n'
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
const copy_link = () => { copy(outlink.value, $q) }
|
||||
return {
|
||||
|
||||
text,
|
||||
outlink,
|
||||
copy() {
|
||||
copyToClipboard(outlink.value)
|
||||
.then(() => {
|
||||
// success!
|
||||
$q.notify({
|
||||
message: '复制成功',
|
||||
color: 'positive',
|
||||
position: 'top'
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// fail
|
||||
$q.notify({
|
||||
message: '复制失败',
|
||||
color: 'negative'
|
||||
})
|
||||
})
|
||||
}
|
||||
copy_link
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -24,30 +24,6 @@
|
|||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
<!-- <div class="col-sm-8 col-md-4 col-lg-2 col-xl-1 q-pa-md">
|
||||
<q-card class="my-card">
|
||||
<img class="rounded-full q-pa-md" src="img/v2rayng.png">
|
||||
<q-card-section class="text-center">
|
||||
<div class="text-h6 text-center">v2ray</div>
|
||||
<q-btn color="positive">
|
||||
<q-icon left name="file_download" />
|
||||
<div>安卓手机</div>
|
||||
</q-btn>
|
||||
<q-btn class="q-ma-md" color="positive">
|
||||
<q-icon left name="file_download" />
|
||||
<div>模拟器</div>
|
||||
</q-btn>
|
||||
<q-btn color="positive">
|
||||
<q-icon left name="file_download" />
|
||||
<div>PC</div>
|
||||
</q-btn>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -72,9 +48,6 @@ export default defineComponent({
|
|||
// eslint-disable-next-line vue/multi-word-component-names
|
||||
name: 'Download',
|
||||
setup() {
|
||||
|
||||
|
||||
|
||||
return {
|
||||
array,
|
||||
dow(url: string) {
|
||||
|
|
|
@ -17,67 +17,12 @@
|
|||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
import { getdata } from 'src/api/api'
|
||||
import { decode, encode } from 'js-base64';
|
||||
import { copyToClipboard, useQuasar } from 'quasar';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
// eslint-disable-next-line vue/multi-word-component-names
|
||||
name: 'Help',
|
||||
setup() {
|
||||
const $q = useQuasar()
|
||||
const api = new getdata;
|
||||
const text = ref('');
|
||||
const outlink = ref('');
|
||||
const def_link = 'ew0KICAidiI6ICIyIiwNCiAgInBzIjogIjAiLA0KICAiYWRkIjogIjE4NS4yMTguNi4xMDgiLA0KICAicG9ydCI6ICI5MDAwIiwNCiAgImlkIjogIjJlZTU3ODA2LWY2ZTQtNDgyYS1lZjA4LTczNjBjMDRjZDNlNSIsDQogICJhaWQiOiAiMCIsDQogICJzY3kiOiAiYXV0byIsDQogICJuZXQiOiAid3MiLA0KICAidHlwZSI6ICJub25lIiwNCiAgImhvc3QiOiAiIiwNCiAgInBhdGgiOiAiLyIsDQogICJ0bHMiOiAiIiwNCiAgInNuaSI6ICIiLA0KICAiYWxwbiI6ICIiDQp9'
|
||||
const is_ip = (ip: string) => {
|
||||
var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
|
||||
return reg.test(ip);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
watch(() => text.value, (newValue, oldValue) => { //直接监听
|
||||
let obj = JSON.parse(decode(def_link))
|
||||
console.log(newValue)
|
||||
if (!is_ip(newValue)) {
|
||||
outlink.value = 'erroe ip'
|
||||
return
|
||||
}
|
||||
|
||||
api.get_country(newValue).then(res => {
|
||||
let name = res.data.country == '中国' ? res.data.province : res.data.country
|
||||
obj.ps = name + newValue
|
||||
obj.add = newValue
|
||||
outlink.value = 'vmess://' + encode(JSON.stringify(obj))
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
text,
|
||||
outlink,
|
||||
copy() {
|
||||
copyToClipboard(outlink.value)
|
||||
.then(() => {
|
||||
// success!
|
||||
$q.notify({
|
||||
message: '复制成功',
|
||||
color: 'positive',
|
||||
position: 'top'
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// fail
|
||||
$q.notify({
|
||||
message: '复制失败',
|
||||
color: 'negative'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<div class="col-xs-1 col-md-4"></div>
|
||||
<div class="col-xs-10 col-md-4">
|
||||
<h6 style="text-align: center;margin: 1rem;">节点<q-icon v-show="online" title="在线" name="check_circle_outline"
|
||||
style="color: green;" /> <q-icon v-show="!online" title="离线" name="highlight_off" style="color: red;" /></h6>
|
||||
style="color: green;" /> <q-icon v-show="!online" title="离线" name="highlight_off" style="color: red;" />
|
||||
</h6>
|
||||
<q-input v-model="text" filled autogrow />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,13 +13,13 @@
|
|||
<div class="row">
|
||||
<div class="col-xs-1 col-md-4"></div>
|
||||
<div class="col-xs-10 col-md-4">
|
||||
<OriginalLinkItem v-model:text="text"></OriginalLinkItem>
|
||||
<OriginalLinkItem :names="names"></OriginalLinkItem>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-for="(item, index) in serve" :key="index">
|
||||
<div class="row" v-for="(item, index) in serves" :key="index">
|
||||
<div class="col-xs-1 col-md-4"></div>
|
||||
<div class="col-xs-10 col-md-4">
|
||||
<LinkItem :serve="item" v-model:text="text"></LinkItem>
|
||||
<LinkItem :serve="item" v-model:names="names"></LinkItem>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -28,47 +29,56 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { server } from 'components/models';
|
||||
import { server } from 'src/models/models';
|
||||
import { computed, defineComponent, onMounted, ref } from 'vue';
|
||||
import { getdata } from 'src/api/api'
|
||||
import LinkItem from 'components/LinkItem.vue';
|
||||
import OriginalLinkItem from 'components/OriginalLinkItem.vue';
|
||||
import { decode } from 'js-base64';
|
||||
import { VLESS } from 'src/Util/node/Vless';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IndexPage',
|
||||
components: { LinkItem, OriginalLinkItem },
|
||||
setup() {
|
||||
const serve = ref(<server[]>[])
|
||||
const serves = ref(<server[]>[])
|
||||
const api = new getdata;
|
||||
const text = ref('');
|
||||
const online = ref(false);
|
||||
const names = ref(<string[]>[])
|
||||
onMounted(() => {
|
||||
api.get_server().then((res) => {
|
||||
for (let index = 0; index < res.data.length; index++) {
|
||||
serve.value.push(res.data[index]);
|
||||
for (let index = 0; index < res.data.data.length; index++) {
|
||||
serves.value.push(res.data.data[index]);
|
||||
}
|
||||
})
|
||||
})
|
||||
const isobj = computed(() => {
|
||||
|
||||
try {
|
||||
if (text.value.length <= 0) {
|
||||
return false
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
let texttmp = text.value!.replace(/\ +/g, '');
|
||||
texttmp = texttmp.replace(/[\r\n]/g, '');
|
||||
let arr = texttmp.split('vmess://');
|
||||
let arr = texttmp.split('vless://');
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
names.value = [];
|
||||
for (let iterator of arr) {
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
online.value = false
|
||||
if (iterator.length > 0) {
|
||||
let obj = JSON.parse(decode(iterator));
|
||||
if (obj.id != '2ee57806-f6e4-482a-ef08-7360c04cd3e5' || obj.net != 'ws') {
|
||||
// let tmp = parseVlessLink(iterator)
|
||||
let tmp = VLESS.decodeURL('vless://' + iterator);
|
||||
if (!(tmp != null && tmp.uuid == '2ee57806-f6e4-482a-ef08-7360c04cd3e5')) {
|
||||
return false
|
||||
}
|
||||
let reg = new RegExp(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
|
||||
let ip = obj.ps.match(reg)[0];
|
||||
isonline(ip)
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
if (tmp.name) { names.value.push(tmp.name) }
|
||||
// eslint-disable-next-line vue/no-async-in-computed-properties
|
||||
api.get_server_ms(tmp.server, tmp.port, tmp.query.path, 0).then((res) => {
|
||||
console.log(res)
|
||||
online.value = res.is_online
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -79,16 +89,12 @@ export default defineComponent({
|
|||
|
||||
return true
|
||||
})
|
||||
const isonline = (ip: string) => {
|
||||
api.text_server(`http://${ip}:9000/`).then(res => {
|
||||
online.value = res.data.sataus == 400
|
||||
})
|
||||
}
|
||||
return {
|
||||
serve,
|
||||
serves,
|
||||
text,
|
||||
isobj,
|
||||
online
|
||||
online,
|
||||
names
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,9 +2,13 @@ import { MutationTree } from 'vuex';
|
|||
import { ExampleStateInterface } from './state';
|
||||
|
||||
const mutation: MutationTree<ExampleStateInterface> = {
|
||||
someMutation (/* state: ExampleStateInterface */) {
|
||||
someMutation(/* state: ExampleStateInterface */) {
|
||||
// your code
|
||||
}
|
||||
},
|
||||
//改动url
|
||||
set_text_server_url(state, url) {
|
||||
state.text_server_url = url;
|
||||
},
|
||||
};
|
||||
|
||||
export default mutation;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
export interface ExampleStateInterface {
|
||||
prop: boolean;
|
||||
text_server_url: string;
|
||||
}
|
||||
|
||||
function state(): ExampleStateInterface {
|
||||
return {
|
||||
prop: false
|
||||
}
|
||||
prop: false,
|
||||
text_server_url: '',
|
||||
};
|
||||
}
|
||||
|
||||
export default state;
|
||||
|
|
Loading…
Reference in New Issue