Files
3dp-manager/server/src/inbounds/inbound-builder.service.ts
T
Den Piligrim 21e4907aa2 update
2026-03-21 15:16:09 +03:00

521 lines
15 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import * as crypto from 'crypto';
import { v4 as uuidv4 } from 'uuid';
import * as fs from 'fs';
@Injectable()
export class InboundBuilderService {
private flag = process.env.COUNTRY_FLAG ?? '%F0%9F%92%AF';
buildVlessRealityTcp(params: { port: number; uuid: string; sni: string; privateKey: string; publicKey: string }) {
const { port, uuid, sni, privateKey, publicKey } = params;
return {
enable: true,
port,
protocol: 'vless',
remark: `vless-tcp-reality`,
settings: JSON.stringify({
clients: [{ id: uuid, flow: 'xtls-rprx-vision', email: uuid, enable: true, limitIp: 0, totalGB: 0, expiryTime: 0, tgId: '', subId: '', reset: 0 }],
decryption: 'none',
encryption: 'none',
fallbacks: []
}),
streamSettings: JSON.stringify({
network: 'tcp',
security: 'reality',
externalProxy: [],
realitySettings: {
show: false,
xver: 0,
target: `${sni}:443`,
dest: `${sni}:443`,
serverNames: [sni],
privateKey: privateKey,
shortIds: [crypto.randomBytes(4).toString('hex'), crypto.randomBytes(4).toString('hex')],
settings: { publicKey: publicKey, fingerprint: 'random', serverName: '', spiderX: '/' }
},
tcpSettings: { acceptProxyProtocol: false, header: { type: 'none' } }
}),
sniffing: JSON.stringify({ enabled: false, destOverride: ['http', 'tls', 'quic', 'fakedns'], metadataOnly: false, routeOnly: false })
};
}
buildVlessRealityXhttp(params: { port: number; uuid: string; sni: string; privateKey: string; publicKey: string }) {
const { port, uuid, sni, privateKey, publicKey } = params;
return {
enable: true,
port,
protocol: 'vless',
remark: `vless-xhttp-reality`,
settings: JSON.stringify({
clients: [{ id: uuid, flow: '', email: uuid, enable: true, limitIp: 0, totalGB: 0, expiryTime: 0, tgId: '', subId: '', reset: 0 }],
decryption: 'none',
encryption: 'none',
fallbacks: []
}),
streamSettings: JSON.stringify({
network: 'xhttp',
security: 'reality',
externalProxy: [],
realitySettings: {
show: false,
xver: 0,
target: `${sni}:443`,
dest: `${sni}:443`,
serverNames: [sni],
privateKey: privateKey,
shortIds: [crypto.randomBytes(4).toString('hex'), crypto.randomBytes(4).toString('hex')],
settings: { publicKey: publicKey, fingerprint: 'random', serverName: '', spiderX: '/' }
},
xhttpSettings: {
host: sni,
path: "/",
mode: "auto",
noSSEHeader: false,
scMaxBufferedPosts: 30,
scMaxEachPostBytes: "1000000",
scStreamUpServerSecs: "20-80",
xPaddingBytes: "100-1000"
}
}),
sniffing: JSON.stringify({
enabled: false,
destOverride: ["http", "tls", "quic", "fakedns"],
metadataOnly: false,
routeOnly: false
})
};
}
buildVlessRealityGrpc(params: { port: number; uuid: string; sni: string; privateKey: string; publicKey: string }) {
const { port, uuid, sni, privateKey, publicKey } = params;
return {
enable: true,
port,
protocol: "vless",
remark: "vless-grpc-reality",
settings: JSON.stringify({
clients: [{
id: uuid,
email: uuid,
enable: true,
flow: "",
limitIp: 0,
totalGB: 0,
expiryTime: 0,
tgId: "",
subId: "",
reset: 0
}],
decryption: "none",
encryption: "none",
fallbacks: []
}),
streamSettings: JSON.stringify({
network: "grpc",
security: "reality",
externalProxy: [],
realitySettings: {
show: false,
xver: 0,
target: `${sni}:443`,
dest: `${sni}:443`,
serverNames: [sni],
privateKey: privateKey,
shortIds: [crypto.randomBytes(4).toString('hex')],
settings: { publicKey: publicKey, fingerprint: 'random', serverName: '', spiderX: '/' }
},
grpcSettings: {
serviceName: "myservice",
authority: sni,
multiMode: false,
}
}),
sniffing: JSON.stringify({
enabled: false,
destOverride: ["http", "tls", "quic", "fakedns"],
metadataOnly: false,
routeOnly: false
})
};
}
buildVlessWs(params: { port: number; uuid: string; sni: string }) {
const { port, uuid, sni } = params;
return {
enable: true,
port,
protocol: 'vless',
remark: `vless-ws`,
settings: JSON.stringify({
clients: [{
id: uuid,
email: uuid,
enable: true,
flow: "",
limitIp: 0,
totalGB: 0,
expiryTime: 0,
tgId: "",
subId: "",
reset: 0
}],
decryption: "none",
encryption: "none",
fallbacks: []
}),
streamSettings: JSON.stringify({
network: "ws",
security: "none",
externalProxy: [],
wsSettings: {
host: sni,
path: "/",
acceptProxyProtocol: false,
heartbeatPeriod: 0,
}
}),
sniffing: JSON.stringify({
enabled: false,
destOverride: ["http", "tls", "quic", "fakedns"],
metadataOnly: false,
routeOnly: false
})
};
}
buildVmessTcp(params: { port: number; uuid: string }) {
const { port, uuid } = params;
return {
enable: true,
port,
protocol: 'vmess',
remark: 'vmess-tcp',
settings: JSON.stringify({
clients: [{
id: uuid,
flow: "",
email: uuid,
enable: true,
limitIp: 0,
totalGB: 0,
expiryTime: 0,
tgId: "",
subId: "0",
alterId: "0",
reset: 0
}],
}),
streamSettings: JSON.stringify({
network: "tcp",
security: "none",
tcpSettings: {
acceptProxyProtocol: false,
header: { type: "none" }
}
}),
sniffing: JSON.stringify({
enabled: false,
destOverride: ["http", "tls", "quic", "fakedns"],
metadataOnly: false,
routeOnly: false
})
};
}
buildShadowsocksTcp(params: { port: number; uuid: string }) {
const { port, uuid } = params;
return {
enable: true,
port,
protocol: 'shadowsocks',
remark: 'shadowsocks-tcp',
settings: JSON.stringify({
clients: [{
id: "",
flow: "",
email: uuid,
password: crypto.randomBytes(32).toString("base64"),
enable: true,
limitIp: 0,
totalGB: 0,
expiryTime: 0,
tgId: "",
subId: "",
reset: 0
}],
ivCheck: false,
method: "2022-blake3-aes-256-gcm",
network: "tcp",
password: crypto.randomBytes(32).toString("base64")
}),
streamSettings: JSON.stringify({
network: "tcp",
security: "none",
tcpSettings: {
acceptProxyProtocol: false,
header: { type: "none" }
}
}),
sniffing: JSON.stringify({
enabled: false,
destOverride: ["http", "tls", "quic", "fakedns"],
metadataOnly: false,
routeOnly: false
})
};
}
buildTrojanRealityTcp(params: { port: number; uuid: string; sni: string; privateKey: string; publicKey: string }) {
const { port, uuid, sni, privateKey, publicKey } = params;
return {
enable: true,
port,
protocol: 'trojan',
remark: `trojan-tcp-reality`,
settings: JSON.stringify({
clients: [{
id: uuid,
email: uuid,
password: crypto.randomBytes(8).toString("hex"),
enable: true,
flow: "",
limitIp: 0,
totalGB: 0,
expiryTime: 0,
tgId: "",
subId: "",
reset: 0
}],
fallbacks: []
}),
streamSettings: JSON.stringify({
network: "tcp",
security: "reality",
externalProxy: [],
realitySettings: {
show: false,
xver: 0,
target: `${sni}:443`,
dest: `${sni}:443`,
serverNames: [sni],
privateKey: privateKey,
shortIds: [
crypto.randomBytes(4).toString("hex"),
crypto.randomBytes(3).toString("hex"),
crypto.randomBytes(8).toString("hex"),
crypto.randomBytes(2).toString("hex"),
crypto.randomBytes(2).toString("hex"),
crypto.randomBytes(2).toString("hex"),
crypto.randomBytes(2).toString("hex"),
crypto.randomBytes(4).toString("hex")
],
settings: {
publicKey: publicKey,
fingerprint: "random",
serverName: "",
spiderX: "/"
}
},
tcpSettings: {
acceptProxyProtocol: false,
header: { type: "none" }
}
}),
sniffing: JSON.stringify({
enabled: false,
destOverride: ["http", "tls", "quic", "fakedns"],
metadataOnly: false,
routeOnly: false
})
};
}
generateUuid() {
return uuidv4();
}
buildInboundLink(inbound: any, sni: string, idOrPass: string, flagEmoji: string): string {
this.flag = flagEmoji;
let link = "";
switch (inbound.protocol) {
case "vless":
link = this.buildVlessLink(inbound, sni, idOrPass);
break;
case "vmess":
link = this.buildVmessLink(inbound, sni, idOrPass);
break;
case "shadowsocks":
link = this.buildSsLink(inbound, sni, idOrPass);
break;
case "trojan":
link = this.buildTrojanLink(inbound, sni, idOrPass);
break;
}
return link;
}
private buildVlessLink(inbound: any, sni: string, uuid: string) {
const stream = JSON.parse(inbound.streamSettings);
const settings = JSON.parse(inbound.settings);
const network = stream.network;
const security = stream.security || "none";
const params = new URLSearchParams();
params.set("type", network);
params.set("encryption", "none");
params.set("security", security);
if (security === "reality") {
const r = stream.realitySettings;
params.set("pbk", r.settings.publicKey);
params.set("fp", r.settings.fingerprint || "random");
params.set("sni", r.serverNames?.[0] || "");
params.set("sid", r.shortIds?.[0] || "");
params.set("spx", '/');
if (network === "tcp") {
const client = settings.clients?.[0];
if (client?.flow) {
params.set("flow", client.flow);
}
}
if (network === "xhttp") {
const x = stream.xhttpSettings || {};
params.set("path", x.path || "/");
params.set("host", x.host || r.serverNames?.[0]);
params.set("mode", x.mode || "auto");
}
if (network === "grpc") {
const g = stream.grpcSettings || {};
params.set("serviceName", g.serviceName || "grpc");
params.set("authority", g.authority || r.serverNames?.[0]);
}
}
if (network === "ws") {
const ws = stream.wsSettings || {};
params.set("path", ws.path || "/");
if (ws.headers?.Host) {
params.set("host", ws.headers.Host);
}
}
return (
`vless://${uuid}@${sni}:${inbound.port}` +
`?${params.toString()}` +
`#${this.flag}%20${encodeURIComponent(inbound.remark)}`
);
}
private buildVmessLink(inbound: any, sni: string, uuid: string) {
const stream = JSON.parse(inbound.streamSettings);
const vmessObj = {
add: sni,
aid: '0',
alpn: "",
fp: "",
host: "",
id: uuid,
net: stream.network || "tcp",
path: "/",
port: inbound.port.toString(),
ps: decodeURIComponent(this.flag) + ' ' + inbound.remark,
scy: "",
sni: "",
tls: stream.security || "none",
type: "none",
v: "2"
};
const base64 = Buffer
.from(JSON.stringify(vmessObj), "utf8")
.toString("base64");
return `vmess://${base64}`;
}
private buildSsLink(inbound: any, sni: string, idOrPass: string) {
const settings = JSON.parse(inbound.settings);
const method = settings.method;
const serverPassword = settings.password;
const clientPassword = settings.clients[0].password;
const userInfo = `${method}:${serverPassword}:${clientPassword}`;
const base64 = Buffer
.from(userInfo, "utf8")
.toString("base64");
return `ss://${base64}@${sni}:${inbound.port}?type=tcp#${this.flag}%20${inbound.remark}`;
}
private buildTrojanLink(inbound: any, sni: string, password: string) {
const stream = JSON.parse(inbound.streamSettings);
const reality = stream.realitySettings;
const pbk = reality.settings.publicKey;
const SNI = reality.serverNames?.[0] || sni;
const sid = reality.shortIds?.[0] || "";
const spx = '%2F';
return (
`trojan://${password}@${SNI}:${inbound.port}` +
`?type=tcp` +
`&security=reality` +
`&pbk=${pbk}` +
`&fp=random` +
`&sni=${SNI}` +
`&sid=${sid}` +
`&spx=${spx}` +
`#${this.flag}%20${inbound.remark}`
);
}
buildHysteria2Link(serverAddress: string, sni: string, remark: string): string {
let auth = 'YOUR_AUTH';
let obfs = 'salamander';
let obfsPass = 'YOUR_PASS';
let port = 443;
try {
const configPath = '/etc/hysteria/config.yaml';
if (fs.existsSync(configPath)) {
const fileContent = fs.readFileSync(configPath, 'utf8');
const authMatch = fileContent.match(/password:\s*['"]?([^'"\n]+)['"]?/);
if (authMatch) auth = authMatch[1];
const obfsMatch = fileContent.match(/type:\s*['"]?(salamander)['"]?/);
if (obfsMatch) obfs = obfsMatch[1];
const passMatch = fileContent.match(/salamander:[\s\S]*?password:\s*['"]?([^'"\n]+)['"]?/);
if (passMatch) obfsPass = passMatch[1];
const listenMatch = fileContent.match(/listen:\s*['"]?:(\d+)['"]?/);
if (listenMatch) port = parseInt(listenMatch[1], 10);
} else {
console.warn(`Конфиг Hysteria2 не найден по пути: ${configPath}`);
}
} catch (e) {
console.error('Ошибка чтения конфига Hysteria2', e);
}
const params = new URLSearchParams();
params.set('insecure', '0');
params.set('sni', serverAddress);
params.set('obfs', obfs);
params.set('obfs-password', obfsPass);
return `hy2://${auth}@${serverAddress}:${port}/?${params.toString()}#${remark}`;
}
}