[server] [func.xray] - 完成xray config的文件持久化内容

This commit is contained in:
zeaslity
2023-02-15 11:13:58 +08:00
parent 0b8e7fc69c
commit be7f1a9108
10 changed files with 245 additions and 547 deletions

View File

@@ -85,6 +85,16 @@ public class TimeUtils {
}
/**
* @return UTC+8 [ yyyy-MM-dd-HH-mm-ss ] Time String
*/
public static String currentFormatTimeString() {
return currentFormatTime()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"));
}
public static LocalDateTime cvFromDate(Date date) {
// fix bug

View File

@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
@RestController
@@ -104,7 +105,7 @@ public class OSSController {
@PostMapping("/object/create")
@ApiOperation("[对象] - 创建, 外部上传 [todo 分片上传]")
public R<ObjectMetadata> objectCreate(
public R<HashMap<String, String>> objectCreate(
@RequestParam(value = "BackendPrefixName", required = true)
@ApiParam(value = "BackendPrefixName") String BackendPrefixName,
@RequestParam(value = "bucketName", required = true)

View File

@@ -8,6 +8,7 @@ import io.wdd.func.oss.config.OssConfig;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.HashMap;
import java.util.List;
public interface OSSCoreService {
@@ -135,7 +136,7 @@ public interface OSSCoreService {
* @param objectName
* @return
*/
ObjectMetadata createObject(OssConfig ossConfig, String bucketName, String objectName, MultipartFile file);
HashMap<String, String> createObject(OssConfig ossConfig, String bucketName, String objectName, MultipartFile file);
/**
* Creates a new object or overwrites an existing one.

View File

@@ -12,6 +12,8 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
@@ -162,13 +164,18 @@ public class OSSCoreServiceImpl implements OSSCoreService {
}
@Override
public ObjectMetadata createObject(OssConfig ossConfig, String bucketName, String objectName, MultipartFile file) {
public HashMap<String, String> createObject(OssConfig ossConfig, String bucketName, String objectName, MultipartFile file) {
AmazonS3 client = ossConfig.getClient();
try {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(file.getSize());
long contentLength = Math.max(
file.getBytes().length,
file.getSize()
);
metadata.setContentLength(contentLength);
PutObjectResult putObjectResult = client.putObject(
bucketName,
@@ -177,10 +184,27 @@ public class OSSCoreServiceImpl implements OSSCoreService {
metadata
);
return putObjectResult.getMetadata();
HashMap<String, String> map = new HashMap<>();
// 需要返回下载地址
S3Object s3Object = this.getObject(
ossConfig,
bucketName,
objectName
);
URI uri = s3Object
.getObjectContent()
.getHttpRequest()
.getURI();
map.put("origin",
String.valueOf(uri));
return map;
} catch (IOException e) {
log.error("文件-创建一个对象失败,原因为 => {}", e.getMessage());
throw new RuntimeException(e);
}
}

View File

@@ -1,465 +0,0 @@
{
"log": {
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log",
"loglevel": "warning"
},
"inbounds": [
{
"protocol": "vmess",
"listen": "0.0.0.0",
"port": 19999,
"streamSettings": {
"tcpSettings": {
"header": {
"type": "http",
"response": {
"version": "1.1",
"status": "200",
"reason": "Accept",
"headers": {
"Content-Type": [
"application/octet-stream",
"video/mpeg"
],
"Transfer-Encoding": [
"chunked"
],
"Connection": [
"keep-alive"
],
"Pragma": "no-cache"
}
}
}
}
},
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2",
"settings": {
"clients": [
{
"id": "402df401-6a63-444b-9386-f3b6d8e1b769",
"email": "tc-sh->seoul-2->tokyo-2->phoenix-2@octopus.io",
"level": 0,
"alterId": 23
}
],
"disableInsecureEncryption": false
}
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "140.238.30.110",
"port": 19999,
"users": [
{
"id": "402df401-6a63-444b-9386-f3b6d8e1b769",
"email": "tc-sh->seoul-2->tokyo-2->phoenix-2@octopus.io",
"level": 0,
"alterId": 23
}
]
}
]
},
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2",
"streamSettings": {
"tcpSettings": {
"header": {
"type": "http",
"request": {
"version": "1.1",
"method": "GET",
"path": [
"/news/",
"/finance/",
"/sports/",
"weathers"
],
"headers": {
"Host": [
"www.baidu.com",
"www.google.com",
"www.bing.com",
"www.github.com"
],
"User-Agent": [
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
],
"Accept-Encoding": [
"gzip",
"deflate"
],
"Connection": [
"keep-alive"
],
"Pragma": "no-cache"
}
}
}
}
},
"mux": {
"enabled": false,
"concurrency": -1
}
},
{
"protocol": "freedom"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"inboundTag": [
"tc-sh->seoul-2->tokyo-2->phoenix-2"
],
"outboundTag": "tc-sh->seoul-2->tokyo-2->phoenix-2"
}
]
}
},
{
"log": {
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log",
"loglevel": "warning"
},
"inbounds": [
{
"protocol": "vmess",
"listen": "0.0.0.0",
"port": 19999,
"streamSettings": {
"tcpSettings": {
"header": {
"type": "http",
"response": {
"version": "1.1",
"status": "200",
"reason": "Accept",
"headers": {
"Content-Type": [
"application/octet-stream",
"video/mpeg"
],
"Transfer-Encoding": [
"chunked"
],
"Connection": [
"keep-alive"
],
"Pragma": "no-cache"
}
}
}
}
},
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2",
"settings": {
"clients": [
{
"id": "402df401-6a63-444b-9386-f3b6d8e1b769",
"email": "tc-sh->seoul-2->tokyo-2->phoenix-2@octopus.io",
"level": 0,
"alterId": 23
}
],
"disableInsecureEncryption": false
}
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "140.238.52.228",
"port": 19999,
"users": [
{
"id": "402df401-6a63-444b-9386-f3b6d8e1b769",
"email": "tc-sh->seoul-2->tokyo-2->phoenix-2@octopus.io",
"level": 0,
"alterId": 23
}
]
}
]
},
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2",
"streamSettings": {
"tcpSettings": {
"header": {
"type": "http",
"request": {
"version": "1.1",
"method": "GET",
"path": [
"/news/",
"/finance/",
"/sports/",
"weathers"
],
"headers": {
"Host": [
"www.baidu.com",
"www.google.com",
"www.bing.com",
"www.github.com"
],
"User-Agent": [
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
],
"Accept-Encoding": [
"gzip",
"deflate"
],
"Connection": [
"keep-alive"
],
"Pragma": "no-cache"
}
}
}
}
},
"mux": {
"enabled": false,
"concurrency": -1
}
},
{
"protocol": "freedom"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"inboundTag": [
"tc-sh->seoul-2->tokyo-2->phoenix-2"
],
"outboundTag": "tc-sh->seoul-2->tokyo-2->phoenix-2"
}
]
}
}
-----------------------------------
{
"log": {
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log",
"loglevel": "warning"
},
"inbounds": [
{
"protocol": "vmess",
"listen": "0.0.0.0",
"port": 19999,
"streamSettings": {
"tcpSettings": {
"header": {
"type": "http",
"response": {
"version": "1.1",
"status": "200",
"reason": "Accept",
"headers": {
"Content-Type": [
"application/octet-stream",
"video/mpeg"
],
"Transfer-Encoding": [
"chunked"
],
"Connection": [
"keep-alive"
],
"Pragma": "no-cache"
}
}
}
}
},
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2",
"settings": {
"clients": [
{
"id": "402df401-6a63-444b-9386-f3b6d8e1b769",
"email": "tc-sh->seoul-2->tokyo-2->phoenix-2@octopus.io",
"level": 0,
"alterId": 23
}
],
"disableInsecureEncryption": false
}
}
],
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "129.146.171.163",
"port": 19999,
"users": [
{
"id": "402df401-6a63-444b-9386-f3b6d8e1b769",
"email": "tc-sh->seoul-2->tokyo-2->phoenix-2@octopus.io",
"level": 0,
"alterId": 23
}
]
}
]
},
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2",
"streamSettings": {
"tcpSettings": {
"header": {
"type": "http",
"request": {
"version": "1.1",
"method": "GET",
"path": [
"/news/",
"/finance/",
"/sports/",
"weathers"
],
"headers": {
"Host": [
"www.baidu.com",
"www.google.com",
"www.bing.com",
"www.github.com"
],
"User-Agent": [
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
],
"Accept-Encoding": [
"gzip",
"deflate"
],
"Connection": [
"keep-alive"
],
"Pragma": "no-cache"
}
}
}
}
},
"mux": {
"enabled": false,
"concurrency": -1
}
},
{
"protocol": "freedom"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"inboundTag": [
"tc-sh->seoul-2->tokyo-2->phoenix-2"
],
"outboundTag": "tc-sh->seoul-2->tokyo-2->phoenix-2"
}
]
}
}
-----------------------------------
{
"inbounds": [
{
"protocol": "vmess",
"listen": "0.0.0.0",
"port": 19999,
"streamSettings": {
"tcpSettings": {
"header": {
"type": "http",
"response": {
"version": "1.1",
"status": "200",
"reason": "Accept",
"headers": {
"Content-Type": [
"application/octet-stream",
"video/mpeg"
],
"Transfer-Encoding": [
"chunked"
],
"Connection": [
"keep-alive"
],
"Pragma": "no-cache"
}
}
}
}
},
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2",
"settings": {
"clients": [
{
"id": "402df401-6a63-444b-9386-f3b6d8e1b769",
"email": "tc-sh->seoul-2->tokyo-2->phoenix-2@octopus.io",
"level": 0,
"alterId": 23
}
],
"disableInsecureEncryption": false
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "tc-sh->seoul-2->tokyo-2->phoenix-2"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"inboundTag": [
"tc-sh->seoul-2->tokyo-2->phoenix-2"
],
"outboundTag": "tc-sh->seoul-2->tokyo-2->phoenix-2"
}
]
}
}

View File

@@ -0,0 +1,9 @@
package io.wdd.func.xray.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class XrayConfigDistribute {
}

View File

@@ -0,0 +1,110 @@
package io.wdd.func.xray.service;
import io.wdd.common.handler.MyRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 获取rerouces目录 https://blog.csdn.net/pengpengpeng85/article/details/84785575
*
* 写入文件的教程 https://cloud.tencent.com/developer/article/1895274
*/
@Slf4j
@Service
public class XrayConfigPersistor {
private static String XrayResultPath = "xrayResult/";
public static AtomicInteger cleanVersion = new AtomicInteger(0);
public void write(String fileName , String content, int currentVersion) {
System.out.println("currentVersion = " + currentVersion);
if (cleanVersion.get() == currentVersion) {
// 清除旧的内容
clearOldRemainStaff();
// 自增1避免后续继续清除tmp目录
cleanVersion.incrementAndGet();
}
// 构造对象开始写入, 生成文件
File resultFile = getResultFile(fileName);
try {
log.debug("开始写入XrayConfig进入文件中文件名为 => {}",fileName);
FileWriter fileWriter = new FileWriter(
resultFile
);
BufferedWriter bufferedWriter = new BufferedWriter(
fileWriter
);
log.debug("文件内容为 => {}", content);
bufferedWriter.write(content);
// must close
bufferedWriter.close();
fileWriter.close();
} catch (IOException e) {
log.error("打开文件失败写入tmp文件失败 文件为 => {}", resultFile.getName());
throw new MyRuntimeException(e);
}
}
private void clearOldRemainStaff() {
try {
FileUtils.cleanDirectory(
new ClassPathResource(XrayResultPath).getFile()
);
} catch (IOException e) {
log.error("清楚旧的目录失败! 请检查问题!");
throw new MyRuntimeException(e);
}
}
/**
* 根据文件名,需要创建一个文件
* @param fileName 文件名,如 xxx.json
* @return
*/
private File getResultFile(String fileName ){
ClassPathResource classPathResource = new ClassPathResource(XrayResultPath);
try {
// 需要创建一个文件
return new File(
classPathResource.getFile(),
fileName
);
} catch (IOException e) {
log.error("获取文件失败请检查! fileName is => {}", fileName);
throw new MyRuntimeException(e);
}
}
}

View File

@@ -3,6 +3,7 @@ package io.wdd.func.xray.service;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.wdd.common.utils.TimeUtils;
import io.wdd.func.xray.beans.node.ProxyNode;
import io.wdd.func.xray.beans.xray.RoutingObject;
import io.wdd.func.xray.beans.xray.RuleObject;
@@ -26,6 +27,7 @@ import static io.wdd.func.xray.beans.config.InboundVmessHTTPTemplateClass.Listen
import static io.wdd.func.xray.beans.config.LogTemplateClass.LogTemplate;
import static io.wdd.func.xray.beans.config.OutboundVmessHTTPTemplateClass.*;
import static io.wdd.func.xray.beans.node.ProxyNodeSet.*;
import static io.wdd.func.xray.service.XrayConfigPersistor.cleanVersion;
@Service
@Slf4j
@@ -34,6 +36,9 @@ public class XrayCoreServiceImpl implements XrayCoreService {
@Resource
ObjectMapper objectMapper;
@Resource
XrayConfigPersistor xrayConfigPersistor;
@Override
public void generateXrayJsonFromNodeList(ArrayList<ArrayList<ProxyNode>> allNetworkPathList) {
@@ -113,7 +118,6 @@ public class XrayCoreServiceImpl implements XrayCoreService {
return;
}
// 忽略掉 null的字段
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
@@ -137,55 +141,68 @@ public class XrayCoreServiceImpl implements XrayCoreService {
.email(tag + "@octopus.io")
.build();
Integer port = 19999;
int port = 19999;
// 清楚tmp目录时候使用的一个锁
int currentVersion = cleanVersion.get();
for (int pos = 0; pos < pathLength; pos++) {
XrayConfig xrayConfig = new XrayConfig();
ProxyNode proxyNode = networkPathList.get(pos);
if (pos == pathLength - 1) {
// 最后一个节点,形式不一样
buildInbound(
xrayConfig,
boolean isOutBoundFree = pos == pathLength - 1;
XrayConfig xrayConfig = doBuildXrayConfig(
isOutBoundFree,
tag,
clientObject,
port,
tag
);
// 设置FreeOut
buildOutboundFree(
xrayConfig,
tag
);
// 设置 路由信息
buildRouting(
xrayConfig,
tag
networkPathList,
pos
);
// 添加到临时缓存中
tmpXrayConfigList.add(xrayConfig);
// 调试
// 持久化
try {
String s = objectMapper
String resultContent = objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(xrayConfig);
System.out.println(s);
System.out.println("-----------------------------------");
// 获得到文件名称
String timeString = TimeUtils.currentFormatTimeString();
String fileName = buildXrayConfigFileName(
proxyNode,
timeString
);
// 文件持久化!
xrayConfigPersistor.write(
fileName,
resultContent,
currentVersion
);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return;
}
}
private String buildXrayConfigFileName(ProxyNode proxyNode, String timeString) {
return proxyNode.getNum() + "-" + proxyNode.getAgentTopicName() + "-" + timeString + ".json";
}
private XrayConfig doBuildXrayConfig(boolean isOutBoundFree, String tag, ClientObject clientObject, int port, ArrayList<ProxyNode> networkPathList, int pos) {
XrayConfig xrayConfig = new XrayConfig();
// 设置Log属性
xrayConfig.setLog(LogTemplate);
@@ -197,6 +214,15 @@ public class XrayCoreServiceImpl implements XrayCoreService {
tag
);
if (isOutBoundFree) {
// 最后一个直接出去就行了
// 设置FreeOut
buildOutboundFree(
xrayConfig,
tag
);
} else {
// 中间节点,需要有特定的输出
// 设置 outbounds的信息
buildOutbound(
xrayConfig,
@@ -206,6 +232,8 @@ public class XrayCoreServiceImpl implements XrayCoreService {
tag,
port
);
}
// 设置 路由信息
buildRouting(
@@ -213,24 +241,7 @@ public class XrayCoreServiceImpl implements XrayCoreService {
tag
);
// 添加到临时缓存中
tmpXrayConfigList.add(xrayConfig);
// 调试
try {
String s = objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(xrayConfig);
System.out.println(s);
System.out.println("-----------------------------------");
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
return xrayConfig;
}
private void buildOutboundFree(XrayConfig xrayConfig, String tag) {