parent
							
								
									be03878e94
								
							
						
					
					
						commit
						ea314be257
					
				| @ -0,0 +1,36 @@ | |||||||
|  | <component name="InspectionProjectProfileManager"> | ||||||
|  |   <profile version="1.0"> | ||||||
|  |     <option name="myName" value="Project Default" /> | ||||||
|  |     <inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoredUrls"> | ||||||
|  |         <list> | ||||||
|  |           <option value="http://localhost" /> | ||||||
|  |           <option value="http://127.0.0.1" /> | ||||||
|  |           <option value="http://0.0.0.0" /> | ||||||
|  |           <option value="http://www.w3.org/" /> | ||||||
|  |           <option value="http://json-schema.org/draft" /> | ||||||
|  |           <option value="http://java.sun.com/" /> | ||||||
|  |           <option value="http://xmlns.jcp.org/" /> | ||||||
|  |           <option value="http://javafx.com/javafx/" /> | ||||||
|  |           <option value="http://javafx.com/fxml" /> | ||||||
|  |           <option value="http://maven.apache.org/xsd/" /> | ||||||
|  |           <option value="http://maven.apache.org/POM/" /> | ||||||
|  |           <option value="http://www.springframework.org/schema/" /> | ||||||
|  |           <option value="http://www.springframework.org/tags" /> | ||||||
|  |           <option value="http://www.springframework.org/security/tags" /> | ||||||
|  |           <option value="http://www.thymeleaf.org" /> | ||||||
|  |           <option value="http://www.jboss.org/j2ee/schema/" /> | ||||||
|  |           <option value="http://www.jboss.com/xml/ns/" /> | ||||||
|  |           <option value="http://www.ibm.com/webservices/xsd" /> | ||||||
|  |           <option value="http://activemq.apache.org/schema/" /> | ||||||
|  |           <option value="http://schema.cloudfoundry.org/spring/" /> | ||||||
|  |           <option value="http://schemas.xmlsoap.org/" /> | ||||||
|  |           <option value="http://cxf.apache.org/schemas/" /> | ||||||
|  |           <option value="http://primefaces.org/ui" /> | ||||||
|  |           <option value="http://tiles.apache.org/" /> | ||||||
|  |           <option value="http://" /> | ||||||
|  |         </list> | ||||||
|  |       </option> | ||||||
|  |     </inspection_tool> | ||||||
|  |   </profile> | ||||||
|  | </component> | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.data; | package cn.wustlinghang.main.data; | ||||||
| 
 | 
 | ||||||
| public class Main { | public class Main { | ||||||
|     public static void main(String[] args) { |     public static void main(String[] args) { | ||||||
| @ -0,0 +1,20 @@ | |||||||
|  | package cn.wustlinghang.internal.api.v1; | ||||||
|  | 
 | ||||||
|  | import cn.wustlinghang.internal.api.v1.response.FrpPluginResponse; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.web.bind.annotation.RequestMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RestController; | ||||||
|  | 
 | ||||||
|  | @Slf4j | ||||||
|  | @RestController | ||||||
|  | @RequestMapping("/internal/frp") | ||||||
|  | public class FrpPluginHandler { | ||||||
|  |     @RequestMapping("/handler") | ||||||
|  |     public FrpPluginResponse handle(String request) { | ||||||
|  |         log.info(request); | ||||||
|  |         return FrpPluginResponse.builder() | ||||||
|  |                 .reject(false) | ||||||
|  |                 .unchange(true) | ||||||
|  |                 .build(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | package cn.wustlinghang.internal.api.v1.response; | ||||||
|  | 
 | ||||||
|  | import lombok.Builder; | ||||||
|  | import lombok.Data; | ||||||
|  | 
 | ||||||
|  | @Data | ||||||
|  | @Builder | ||||||
|  | public class FrpPluginResponse { | ||||||
|  |     private boolean reject; | ||||||
|  | 
 | ||||||
|  |     private boolean unchange; | ||||||
|  | 
 | ||||||
|  | //    private String rejectReason;
 | ||||||
|  | } | ||||||
| @ -0,0 +1,15 @@ | |||||||
|  | package cn.wustlinghang.main.web; | ||||||
|  | 
 | ||||||
|  | import org.springframework.boot.SpringApplication; | ||||||
|  | import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||||
|  | import org.springframework.scheduling.annotation.EnableAsync; | ||||||
|  | import org.springframework.scheduling.annotation.EnableScheduling; | ||||||
|  | 
 | ||||||
|  | @EnableAsync | ||||||
|  | @EnableScheduling | ||||||
|  | @SpringBootApplication | ||||||
|  | public class Main { | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |         SpringApplication.run(Main.class); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | package cn.wustlinghang.main.web.api.v2.undergrade; | ||||||
|  | 
 | ||||||
|  | import cn.wustlinghang.wusthelper.main.response.Response; | ||||||
|  | import org.springframework.web.bind.annotation.RequestMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RestController; | ||||||
|  | 
 | ||||||
|  | @RestController | ||||||
|  | @RequestMapping("/jwc") | ||||||
|  | public class UndergradController { | ||||||
|  |     public Response<String> login() { | ||||||
|  |         return Response.success(""); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | # 服务端配置 | ||||||
|  | # 密码/密钥/内部地址『禁止』写在此处或其他git能检测到的地方 | ||||||
|  | # 具体数值由运维填写在.env文件中,不能添加到git仓库中 | ||||||
|  | 
 | ||||||
|  | spring: | ||||||
|  |   config: | ||||||
|  |     import: optional:file:.env[.properties] | ||||||
|  | 
 | ||||||
|  | server: | ||||||
|  |   port: ${RUN_PORT} | ||||||
| @ -0,0 +1,28 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.main.response; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 通用的响应 | ||||||
|  |  * | ||||||
|  |  * @param code 响应码 | ||||||
|  |  * @param msg  响应信息 | ||||||
|  |  * @param data 响应数据 | ||||||
|  |  * @param <T>  data的类型 | ||||||
|  |  */ | ||||||
|  | public record Response<T>(int code, String msg, T data) { | ||||||
|  |     public static <T> Response<T> success(T data) { | ||||||
|  |         return new Response<>(ResponseCode.SUCCESS.getCode(), "ok", data); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static <T> Response<T> success() { | ||||||
|  |         return success(null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static <T> Response<T> error(int code, String message) { | ||||||
|  |         return new Response<>(code, message, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static <T> Response<T> error(ResponseCode code) { | ||||||
|  |         return error(code.getCode(), code.getMessage()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,28 @@ | |||||||
|  | /* | ||||||
|  |  * Class created by lensfrex. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package cn.wustlinghang.wusthelper.main.response; | ||||||
|  | 
 | ||||||
|  | public enum ResponseCode { | ||||||
|  |     SUCCESS(0, "成功"), | ||||||
|  |     SERVER_INTERNAL_ERROR(-2, "服务器内部错误"), | ||||||
|  |     API_NOT_IMPLEMENT(-1, "接口未实现"); | ||||||
|  | 
 | ||||||
|  |     private final int code; | ||||||
|  | 
 | ||||||
|  |     private final String message; | ||||||
|  | 
 | ||||||
|  |     ResponseCode(int code, String message) { | ||||||
|  |         this.code = code; | ||||||
|  |         this.message = message; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int getCode() { | ||||||
|  |         return code; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getMessage() { | ||||||
|  |         return message; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1 +1 @@ | |||||||
| Subproject commit 965562b69e35d445fb5c58b2e2afd9cdf27d3146 | Subproject commit 736dc78d1b9e64124474cee98c5db6e8212fa096 | ||||||
| @ -0,0 +1,38 @@ | |||||||
|  | target/ | ||||||
|  | !.mvn/wrapper/maven-wrapper.jar | ||||||
|  | !**/src/main/**/target/ | ||||||
|  | !**/src/test/**/target/ | ||||||
|  | 
 | ||||||
|  | ### IntelliJ IDEA ### | ||||||
|  | .idea/modules.xml | ||||||
|  | .idea/jarRepositories.xml | ||||||
|  | .idea/compiler.xml | ||||||
|  | .idea/libraries/ | ||||||
|  | *.iws | ||||||
|  | *.iml | ||||||
|  | *.ipr | ||||||
|  | 
 | ||||||
|  | ### Eclipse ### | ||||||
|  | .apt_generated | ||||||
|  | .classpath | ||||||
|  | .factorypath | ||||||
|  | .project | ||||||
|  | .settings | ||||||
|  | .springBeans | ||||||
|  | .sts4-cache | ||||||
|  | 
 | ||||||
|  | ### NetBeans ### | ||||||
|  | /nbproject/private/ | ||||||
|  | /nbbuild/ | ||||||
|  | /dist/ | ||||||
|  | /nbdist/ | ||||||
|  | /.nb-gradle/ | ||||||
|  | build/ | ||||||
|  | !**/src/main/**/build/ | ||||||
|  | !**/src/test/**/build/ | ||||||
|  | 
 | ||||||
|  | ### VS Code ### | ||||||
|  | .vscode/ | ||||||
|  | 
 | ||||||
|  | ### Mac OS ### | ||||||
|  | .DS_Store | ||||||
| @ -0,0 +1,42 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||||
|  |          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||||
|  |          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||||
|  |     <modelVersion>4.0.0</modelVersion> | ||||||
|  |     <parent> | ||||||
|  |         <groupId>cn.wustlinghang.wusthelper</groupId> | ||||||
|  |         <artifactId>external-library</artifactId> | ||||||
|  |         <version>0.0.1-SNAPSHOT</version> | ||||||
|  |     </parent> | ||||||
|  | 
 | ||||||
|  |     <artifactId>rpc-frp-consul</artifactId> | ||||||
|  | 
 | ||||||
|  |     <properties> | ||||||
|  |         <maven.compiler.source>17</maven.compiler.source> | ||||||
|  |         <maven.compiler.target>17</maven.compiler.target> | ||||||
|  |         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||||
|  | 
 | ||||||
|  |         <jackson.version>2.15.2</jackson.version> | ||||||
|  |         <ini4j.version>0.5.4</ini4j.version> | ||||||
|  |     </properties> | ||||||
|  | 
 | ||||||
|  |     <dependencies> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>com.fasterxml.jackson.core</groupId> | ||||||
|  |             <artifactId>jackson-databind</artifactId> | ||||||
|  |             <version>${jackson.version}</version> | ||||||
|  |             <scope>compile</scope> | ||||||
|  |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>cn.wustlinghang.mywust</groupId> | ||||||
|  |             <artifactId>mywust-network-okhttp</artifactId> | ||||||
|  |             <version>${mywust.version}</version> | ||||||
|  |         </dependency> | ||||||
|  | 
 | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.ini4j</groupId> | ||||||
|  |             <artifactId>ini4j</artifactId> | ||||||
|  |             <version>${ini4j.version}</version> | ||||||
|  |         </dependency> | ||||||
|  |     </dependencies> | ||||||
|  | </project> | ||||||
| @ -0,0 +1,92 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.rpc; | ||||||
|  | 
 | ||||||
|  | import cn.hutool.core.thread.ThreadUtil; | ||||||
|  | import cn.hutool.core.util.RandomUtil; | ||||||
|  | import cn.wustlinghang.mywust.network.Requester; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.rpc.client.ConsulClient; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.rpc.client.FrpcClient; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.rpc.config.FrpConfig; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.rpc.config.RegisterConfig; | ||||||
|  | import com.fasterxml.jackson.databind.JsonNode; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | 
 | ||||||
|  | @Slf4j | ||||||
|  | public class FrpConsulRegister { | ||||||
|  | 
 | ||||||
|  |     private final String serviceId; | ||||||
|  | 
 | ||||||
|  |     private final RegisterConfig registerConfig; | ||||||
|  | 
 | ||||||
|  |     private final FrpcClient frpcClientClient; | ||||||
|  | 
 | ||||||
|  |     private final ConsulClient consulClient; | ||||||
|  | 
 | ||||||
|  |     private boolean registered = false; | ||||||
|  | 
 | ||||||
|  |     public FrpConsulRegister(RegisterConfig registerConfig, FrpConfig frpConfig, | ||||||
|  |                              Requester requester, ObjectMapper objectMapper) { | ||||||
|  | 
 | ||||||
|  |         this.serviceId = RandomUtil.randomString(8); | ||||||
|  |         this.registerConfig = registerConfig; | ||||||
|  | 
 | ||||||
|  |         this.frpcClientClient = new FrpcClient(frpConfig, requester, objectMapper); | ||||||
|  |         this.consulClient = new ConsulClient(this.serviceId, registerConfig, objectMapper, requester); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void register() { | ||||||
|  |         ThreadUtil.execute(() -> { | ||||||
|  |             try { | ||||||
|  |                 log.info("连接frp并注册consul..."); | ||||||
|  |                 this.doRegister(); | ||||||
|  |                 log.info("注册完毕"); | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 log.warn("注册服务时发生异常:", e); | ||||||
|  |                 log.warn("不进行转发注册,直接服务"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Runtime.getRuntime().addShutdownHook(new Thread(this::onShutdown, "ShutdownHookThread")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void onShutdown() { | ||||||
|  |         try { | ||||||
|  |             if (this.registered) { | ||||||
|  |                 log.info("注销服务..."); | ||||||
|  |                 this.consulClient.deregister(); | ||||||
|  |                 this.frpcClientClient.removeProxy(); | ||||||
|  |                 this.registered = false; | ||||||
|  |                 log.info("服务注销完毕"); | ||||||
|  |             } | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             log.warn("注销服务时发生异常:", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void doRegister() throws Exception { | ||||||
|  |         frpcClientClient.addFrpProxy( | ||||||
|  |                 registerConfig.getServiceName(), serviceId, | ||||||
|  |                 "127.0.0.1", registerConfig.getLocalServicePort() | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         String remoteAddress; | ||||||
|  |         String[] remote; | ||||||
|  |         int retry = 0; | ||||||
|  |         do { | ||||||
|  |             // 先睡个0.5秒等待连接成功再获取状态读端口
 | ||||||
|  |             ThreadUtil.sleep(500); | ||||||
|  | 
 | ||||||
|  |             JsonNode proxy = frpcClientClient.getProxyStatus(registerConfig.getServiceName(), serviceId); | ||||||
|  |             remoteAddress = proxy.path("remote_addr") | ||||||
|  |                     .asText("127.0.0.1:" + registerConfig.getLocalServicePort()); | ||||||
|  |             remote = remoteAddress.split(":"); | ||||||
|  |             retry++; | ||||||
|  |         } while (retry < 3 && remote.length < 2); | ||||||
|  |         if (retry == 3) { | ||||||
|  |             throw new Exception("获取frp隧道信息重试次数过多,请手动添加隧道和注册中心"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.consulClient.register(remoteAddress, Integer.parseInt(remote[1])); | ||||||
|  |         this.registered = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,63 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.rpc.client; | ||||||
|  | 
 | ||||||
|  | import cn.wustlinghang.mywust.network.Requester; | ||||||
|  | import cn.wustlinghang.mywust.network.entitys.HttpResponse; | ||||||
|  | import cn.wustlinghang.mywust.network.request.RequestFactory; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.rpc.config.RegisterConfig; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.rpc.entity.RegisterRequestBody; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import com.google.common.collect.Lists; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | 
 | ||||||
|  | @Slf4j | ||||||
|  | public class ConsulClient { | ||||||
|  |     private final String serviceId; | ||||||
|  |     private final String serviceName; | ||||||
|  |     private final String consulAddress; | ||||||
|  | 
 | ||||||
|  |     private final ObjectMapper objectMapper; | ||||||
|  |     private final Requester requester; | ||||||
|  | 
 | ||||||
|  |     public ConsulClient(String serviceId, RegisterConfig registerConfig, ObjectMapper objectMapper, Requester requester) { | ||||||
|  |         this.serviceId = serviceId; | ||||||
|  |         this.serviceName = registerConfig.getServiceName(); | ||||||
|  |         this.consulAddress = registerConfig.getConsulAddress(); | ||||||
|  | 
 | ||||||
|  |         this.objectMapper = objectMapper; | ||||||
|  |         this.requester = requester; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void register(String remoteAddress, int remotePort, | ||||||
|  |                          RegisterRequestBody.HealthCheckOption... healthCheckOption | ||||||
|  |     ) throws IOException { | ||||||
|  |         var registerRequestBody = RegisterRequestBody.builder() | ||||||
|  |                 .id(serviceId) | ||||||
|  |                 .name(serviceName) | ||||||
|  |                 .address(remoteAddress) | ||||||
|  |                 .port(remotePort) | ||||||
|  |                 .checks(Lists.newArrayList(healthCheckOption)) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         byte[] registerRequestData = objectMapper.writeValueAsBytes(registerRequestBody); | ||||||
|  |         var url = consulAddress + "/v1/agent/service/register"; | ||||||
|  | 
 | ||||||
|  |         var registerRequest = RequestFactory.makeHttpRequest(url, registerRequestData); | ||||||
|  |         var registerResponse = requester.put(registerRequest); | ||||||
|  | 
 | ||||||
|  |         if (registerResponse.getStatusCode() != HttpResponse.HTTP_OK) { | ||||||
|  |             log.warn("注册中心注册不成功,请手动注册"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void deregister() throws IOException { | ||||||
|  |         var url = String.format("%s/v1/agent/service/deregister/%s", consulAddress, serviceId); | ||||||
|  |         var deregisterRequest = RequestFactory.makeHttpRequest(url); | ||||||
|  |         var deregisterResponse = requester.put(deregisterRequest); | ||||||
|  | 
 | ||||||
|  |         if (deregisterResponse.getStatusCode() != HttpResponse.HTTP_OK) { | ||||||
|  |             log.warn("服务注销不成功,请手动注册"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,96 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.rpc.client; | ||||||
|  | 
 | ||||||
|  | import cn.hutool.core.codec.Base64; | ||||||
|  | import cn.wustlinghang.mywust.network.request.RequestFactory; | ||||||
|  | import cn.wustlinghang.mywust.network.Requester; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.rpc.config.FrpConfig; | ||||||
|  | import com.fasterxml.jackson.databind.JsonNode; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import com.fasterxml.jackson.databind.node.MissingNode; | ||||||
|  | import org.ini4j.Ini; | ||||||
|  | 
 | ||||||
|  | import java.io.ByteArrayInputStream; | ||||||
|  | import java.io.ByteArrayOutputStream; | ||||||
|  | import java.io.IOException; | ||||||
|  | 
 | ||||||
|  | public class FrpcClient { | ||||||
|  |     private final String frpcAdminAddress; | ||||||
|  | 
 | ||||||
|  |     private final String frpAuthHeaderValue; | ||||||
|  | 
 | ||||||
|  |     private final Requester requester; | ||||||
|  | 
 | ||||||
|  |     private final ObjectMapper objectMapper; | ||||||
|  | 
 | ||||||
|  |     public FrpcClient(FrpConfig config, Requester requester, ObjectMapper objectMapper) { | ||||||
|  | 
 | ||||||
|  |         this.frpcAdminAddress = config.getFrpcAdminAddress(); | ||||||
|  | 
 | ||||||
|  |         this.requester = requester; | ||||||
|  |         this.objectMapper = objectMapper; | ||||||
|  | 
 | ||||||
|  |         String username = config.getFrpcAdminUsername(); | ||||||
|  |         String password = config.getFrpcAdminPassword(); | ||||||
|  |         this.frpAuthHeaderValue = "Basic " + Base64.encode(username + ":" + password); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private String frpProxyName; | ||||||
|  | 
 | ||||||
|  |     public void addFrpProxy(String serviceName, String serviceId, String localAddress, String localPort) | ||||||
|  |             throws IOException { | ||||||
|  |         this.frpProxyName = String.format("%s-%s", serviceName, serviceId); | ||||||
|  | 
 | ||||||
|  |         Ini ini = this.getFrpConfig(); | ||||||
|  |         ini.put(frpProxyName, "type", "tcp"); | ||||||
|  |         ini.put(frpProxyName, "local_address", localAddress); | ||||||
|  |         ini.put(frpProxyName, "local_port", localPort); | ||||||
|  |         ini.put(frpProxyName, "use_compression", "true"); | ||||||
|  | 
 | ||||||
|  |         var output = new ByteArrayOutputStream(); | ||||||
|  |         ini.store(output); | ||||||
|  | 
 | ||||||
|  |         this.reloadFrpConfig(ini); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void removeProxy() throws IOException { | ||||||
|  |         Ini ini = this.getFrpConfig(); | ||||||
|  |         ini.remove(this.frpProxyName); | ||||||
|  | 
 | ||||||
|  |         this.reloadFrpConfig(ini); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Ini getFrpConfig() throws IOException { | ||||||
|  |         var frpConfigRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/config"); | ||||||
|  |         frpConfigRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         var response = requester.get(frpConfigRequest); | ||||||
|  | 
 | ||||||
|  |         return new Ini(new ByteArrayInputStream(response.getBody())); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void reloadFrpConfig(Ini ini) throws IOException { | ||||||
|  |         var output = new ByteArrayOutputStream(); | ||||||
|  |         ini.store(output); | ||||||
|  | 
 | ||||||
|  |         var uploadRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/config", output.toByteArray()); | ||||||
|  |         uploadRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         requester.put(uploadRequest); | ||||||
|  |         var reloadRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/reload"); | ||||||
|  |         reloadRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         requester.get(reloadRequest); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public JsonNode getProxyStatus(String serviceName, String serviceId) throws IOException { | ||||||
|  |         var statusRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/status"); | ||||||
|  |         statusRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         var statusResponse = requester.get(statusRequest); | ||||||
|  | 
 | ||||||
|  |         JsonNode proxies = objectMapper.readTree(statusResponse.getBody()).path("tcp"); | ||||||
|  |         for (JsonNode proxy : proxies) { | ||||||
|  |             if (proxy.path("name").asText().equals(serviceName + "-" + serviceId)) { | ||||||
|  |                 return proxy; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return MissingNode.getInstance(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,12 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.rpc.config; | ||||||
|  | 
 | ||||||
|  | import lombok.Data; | ||||||
|  | 
 | ||||||
|  | @Data | ||||||
|  | public class FrpConfig { | ||||||
|  |     private String frpcAdminAddress; | ||||||
|  | 
 | ||||||
|  |     private String frpcAdminUsername; | ||||||
|  | 
 | ||||||
|  |     private String frpcAdminPassword; | ||||||
|  | } | ||||||
| @ -0,0 +1,12 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.rpc.config; | ||||||
|  | 
 | ||||||
|  | import lombok.Data; | ||||||
|  | 
 | ||||||
|  | @Data | ||||||
|  | public class RegisterConfig { | ||||||
|  |     private String localServicePort; | ||||||
|  | 
 | ||||||
|  |     private String serviceName; | ||||||
|  | 
 | ||||||
|  |     private String consulAddress; | ||||||
|  | } | ||||||
| @ -0,0 +1,12 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.rpc.entity; | ||||||
|  | 
 | ||||||
|  | import lombok.Builder; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | @Builder | ||||||
|  | public record RegisterRequestBody(String address, int port, List<HealthCheckOption> checks, String name, String id) { | ||||||
|  |     @Builder | ||||||
|  |     public record HealthCheckOption(String http, String interval, String timeout) { | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.graduate.services.LoginService; | import cn.wustlinghang.wusthelper.internal.graduate.services.LoginService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.global.Course; | import cn.wustlinghang.mywust.data.global.Course; | ||||||
| import cn.wustlinghang.wusthelper.internal.graduate.services.CourseTableService; | import cn.wustlinghang.wusthelper.internal.graduate.services.CourseTableService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.global.Score; | import cn.wustlinghang.mywust.data.global.Score; | ||||||
| import cn.wustlinghang.wusthelper.internal.graduate.services.ScoreService; | import cn.wustlinghang.wusthelper.internal.graduate.services.ScoreService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.global.StudentInfo; | import cn.wustlinghang.mywust.data.global.StudentInfo; | ||||||
| import cn.wustlinghang.wusthelper.internal.graduate.services.StudentInfoService; | import cn.wustlinghang.wusthelper.internal.graduate.services.StudentInfoService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.graduate.services.TrainingPlanService; | import cn.wustlinghang.wusthelper.internal.graduate.services.TrainingPlanService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http.handler; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| import jakarta.ws.rs.core.Response; | import jakarta.ws.rs.core.Response; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http.handler; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.GraduateRpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.GraduateRpcException; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http.interceptor; | package cn.wustlinghang.wusthelper.internal.graduate.api.http.v1.interceptor; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.api.http.handler.BaseExceptionHandler; | import cn.wustlinghang.wusthelper.internal.graduate.api.http.v1.handler.BaseExceptionHandler; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
| import jakarta.ws.rs.WebApplicationException; | import jakarta.ws.rs.WebApplicationException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.library.services.BookCoverImageUrlService; | import cn.wustlinghang.wusthelper.internal.library.services.BookCoverImageUrlService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.library.parsed.BookDetail; | import cn.wustlinghang.mywust.data.library.parsed.BookDetail; | ||||||
| import cn.wustlinghang.wusthelper.internal.library.services.BookDetailService; | import cn.wustlinghang.wusthelper.internal.library.services.BookDetailService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.library.parsed.BookHolding; | import cn.wustlinghang.mywust.data.library.parsed.BookHolding; | ||||||
| import cn.wustlinghang.wusthelper.internal.library.services.BookHoldingService; | import cn.wustlinghang.wusthelper.internal.library.services.BookHoldingService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.library.services.LoginService; | import cn.wustlinghang.wusthelper.internal.library.services.LoginService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.library.PagingResult; | import cn.wustlinghang.mywust.data.library.PagingResult; | ||||||
| import cn.wustlinghang.mywust.data.library.origin.CurrentLoanBook; | import cn.wustlinghang.mywust.data.library.origin.CurrentLoanBook; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.library.PagingResult; | import cn.wustlinghang.mywust.data.library.PagingResult; | ||||||
| import cn.wustlinghang.mywust.data.library.origin.HistoryLoanBook; | import cn.wustlinghang.mywust.data.library.origin.HistoryLoanBook; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.library.PagingResult; | import cn.wustlinghang.mywust.data.library.PagingResult; | ||||||
| import cn.wustlinghang.mywust.data.library.origin.BaseLoanBook; | import cn.wustlinghang.mywust.data.library.origin.BaseLoanBook; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.library.PagingResult; | import cn.wustlinghang.mywust.data.library.PagingResult; | ||||||
| import cn.wustlinghang.mywust.data.library.origin.BookSearchResult; | import cn.wustlinghang.mywust.data.library.origin.BookSearchResult; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.physics.api.http.handler; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| import jakarta.ws.rs.core.Response; | import jakarta.ws.rs.core.Response; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http.handler; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.LibraryRpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.LibraryRpcException; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.physics.api.http.interceptor; | package cn.wustlinghang.wusthelper.internal.library.api.http.v1.interceptor; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.physics.api.http.handler.BaseExceptionHandler; | import cn.wustlinghang.wusthelper.internal.library.api.http.v1.handler.BaseExceptionHandler; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
| import jakarta.ws.rs.WebApplicationException; | import jakarta.ws.rs.WebApplicationException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.physics.api.http; | package cn.wustlinghang.wusthelper.internal.physics.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.physics.services.LoginService; | import cn.wustlinghang.wusthelper.internal.physics.services.LoginService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.physics.api.http; | package cn.wustlinghang.wusthelper.internal.physics.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.physics.PhysicsCourse; | import cn.wustlinghang.mywust.data.physics.PhysicsCourse; | ||||||
| import cn.wustlinghang.wusthelper.internal.physics.services.CourseTableService; | import cn.wustlinghang.wusthelper.internal.physics.services.CourseTableService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.physics.api.http; | package cn.wustlinghang.wusthelper.internal.physics.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.global.Score; | import cn.wustlinghang.mywust.data.global.Score; | ||||||
| import cn.wustlinghang.wusthelper.internal.physics.services.ScoreService; | import cn.wustlinghang.wusthelper.internal.physics.services.ScoreService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http.handler; | package cn.wustlinghang.wusthelper.internal.physics.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| import jakarta.ws.rs.core.Response; | import jakarta.ws.rs.core.Response; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.physics.api.http.handler; | package cn.wustlinghang.wusthelper.internal.physics.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.PhysicsRpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.PhysicsRpcException; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.graduate.api.http.interceptor; | package cn.wustlinghang.wusthelper.internal.physics.api.http.v1.interceptor; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.graduate.api.http.handler.BaseExceptionHandler; | import cn.wustlinghang.wusthelper.internal.physics.api.http.v1.handler.BaseExceptionHandler; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
| import jakarta.ws.rs.WebApplicationException; | import jakarta.ws.rs.WebApplicationException; | ||||||
| @ -1,16 +0,0 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad; |  | ||||||
| 
 |  | ||||||
| import io.quarkus.runtime.Startup; |  | ||||||
| import jakarta.annotation.PostConstruct; |  | ||||||
| import jakarta.inject.Singleton; |  | ||||||
| import lombok.extern.slf4j.Slf4j; |  | ||||||
| 
 |  | ||||||
| @Slf4j |  | ||||||
| @Startup |  | ||||||
| @Singleton |  | ||||||
| public class Main { |  | ||||||
|     @PostConstruct |  | ||||||
|     public void run() { |  | ||||||
|         log.info("Undergrad service started."); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.services.LoginService; | import cn.wustlinghang.wusthelper.internal.undergrad.services.LoginService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.global.Course; | import cn.wustlinghang.mywust.data.global.Course; | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.services.CourseTableService; | import cn.wustlinghang.wusthelper.internal.undergrad.services.CourseTableService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.services.CreditStatusService; | import cn.wustlinghang.wusthelper.internal.undergrad.services.CreditStatusService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.core.request.service.undergraduate.UndergradExamDelayApiService; | import cn.wustlinghang.mywust.core.request.service.undergraduate.UndergradExamDelayApiService; | ||||||
| import cn.wustlinghang.mywust.data.global.Score; | import cn.wustlinghang.mywust.data.global.Score; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.undergrad.ExamDelayApplication; | import cn.wustlinghang.mywust.data.undergrad.ExamDelayApplication; | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.services.ExamDelayApplicationService; | import cn.wustlinghang.wusthelper.internal.undergrad.services.ExamDelayApplicationService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.global.Score; | import cn.wustlinghang.mywust.data.global.Score; | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.services.ScoreService; | import cn.wustlinghang.wusthelper.internal.undergrad.services.ScoreService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.mywust.data.global.StudentInfo; | import cn.wustlinghang.mywust.data.global.StudentInfo; | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.services.StudentInfoService; | import cn.wustlinghang.wusthelper.internal.undergrad.services.StudentInfoService; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.undergrad.services.TrainingPlanService; | import cn.wustlinghang.wusthelper.internal.undergrad.services.TrainingPlanService; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http.handler; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| import jakarta.ws.rs.core.Response; | import jakarta.ws.rs.core.Response; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.undergrad.api.http.handler; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1.handler; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.UndergradRpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.UndergradRpcException; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | import cn.wustlinghang.wusthelper.internal.rpc.exception.RpcException; | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.wustlinghang.wusthelper.internal.library.api.http.interceptor; | package cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1.interceptor; | ||||||
| 
 | 
 | ||||||
| import cn.wustlinghang.wusthelper.internal.library.api.http.handler.BaseExceptionHandler; | import cn.wustlinghang.wusthelper.internal.undergrad.api.http.v1.handler.BaseExceptionHandler; | ||||||
| import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | import cn.wustlinghang.wusthelper.internal.rpc.response.RpcResponse; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
| import jakarta.ws.rs.WebApplicationException; | import jakarta.ws.rs.WebApplicationException; | ||||||
| @ -0,0 +1,107 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.undergrad.rpc; | ||||||
|  | 
 | ||||||
|  | import cn.hutool.core.thread.ThreadUtil; | ||||||
|  | import cn.hutool.core.util.RandomUtil; | ||||||
|  | import cn.wustlinghang.mywust.network.Requester; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.undergrad.rpc.client.ConsulClient; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.undergrad.rpc.client.FrpcClient; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.undergrad.rpc.config.RpcConfig; | ||||||
|  | import com.fasterxml.jackson.databind.JsonNode; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import io.quarkus.runtime.Startup; | ||||||
|  | import jakarta.annotation.PostConstruct; | ||||||
|  | import jakarta.annotation.PreDestroy; | ||||||
|  | import jakarta.enterprise.context.ApplicationScoped; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.eclipse.microprofile.config.inject.ConfigProperty; | ||||||
|  | 
 | ||||||
|  | @Slf4j | ||||||
|  | @Startup | ||||||
|  | @ApplicationScoped | ||||||
|  | public class FrpConsulRegister { | ||||||
|  | 
 | ||||||
|  |     @ConfigProperty(name = "quarkus.http.port") | ||||||
|  |     String localServicePort; | ||||||
|  | 
 | ||||||
|  |     private final String serviceId; | ||||||
|  | 
 | ||||||
|  |     private final RpcConfig rpcConfig; | ||||||
|  | 
 | ||||||
|  |     private final FrpcClient frpcClientClient; | ||||||
|  | 
 | ||||||
|  |     private final ConsulClient consulClient; | ||||||
|  | 
 | ||||||
|  |     private boolean registered = false; | ||||||
|  | 
 | ||||||
|  |     public FrpConsulRegister(RpcConfig rpcConfig, FrpcClient frpcClientClient, Requester requester, | ||||||
|  |                              ObjectMapper objectMapper) { | ||||||
|  |         this.rpcConfig = rpcConfig; | ||||||
|  | 
 | ||||||
|  |         this.frpcClientClient = frpcClientClient; | ||||||
|  | 
 | ||||||
|  |         this.serviceId = RandomUtil.randomString(8); | ||||||
|  | 
 | ||||||
|  |         this.consulClient = new ConsulClient( | ||||||
|  |                 rpcConfig.getServiceName(), this.serviceId, | ||||||
|  |                 rpcConfig.getConsulAddress(), | ||||||
|  |                 objectMapper, requester); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @PostConstruct | ||||||
|  |     public void onStartup() { | ||||||
|  |         ThreadUtil.execute(() -> { | ||||||
|  |             try { | ||||||
|  |                 log.info("连接frp并注册consul..."); | ||||||
|  |                 this.doRegister(); | ||||||
|  |                 log.info("注册完毕"); | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 log.warn("注册服务时发生异常:", e); | ||||||
|  |                 log.warn("不进行转发注册,直接服务"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Runtime.getRuntime().addShutdownHook(new Thread(this::onShutdown, "ShutdownHookThread")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @PreDestroy | ||||||
|  |     public void onShutdown() { | ||||||
|  |         try { | ||||||
|  |             if (this.registered) { | ||||||
|  |                 log.info("注销服务..."); | ||||||
|  |                 this.consulClient.deregister(); | ||||||
|  |                 this.frpcClientClient.removeProxy(); | ||||||
|  |                 log.info("服务注销完毕"); | ||||||
|  |                 this.registered = false; | ||||||
|  |             } | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             log.warn("注销服务时发生异常:", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void doRegister() throws Exception { | ||||||
|  |         frpcClientClient.addFrpProxy( | ||||||
|  |                 rpcConfig.getServiceName(), serviceId, | ||||||
|  |                 "127.0.0.1", localServicePort | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         String remoteAddress; | ||||||
|  |         String[] remote; | ||||||
|  |         int retry = 0; | ||||||
|  |         do { | ||||||
|  |             // 先睡个0.5秒等待连接成功再获取状态读端口
 | ||||||
|  |             ThreadUtil.sleep(500); | ||||||
|  | 
 | ||||||
|  |             JsonNode proxy = frpcClientClient.getProxyStatus(rpcConfig.getServiceName(), serviceId); | ||||||
|  |             remoteAddress = proxy.path("remote_addr") | ||||||
|  |                     .asText("127.0.0.1:" + localServicePort); | ||||||
|  |             remote = remoteAddress.split(":"); | ||||||
|  |             retry++; | ||||||
|  |         } while (retry < 3 && remote.length < 2); | ||||||
|  |         if (retry == 3) { | ||||||
|  |             throw new Exception("获取frp隧道信息重试次数过多,请手动添加隧道和注册中心"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.consulClient.register(remoteAddress, Integer.parseInt(remote[1])); | ||||||
|  |         this.registered = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,60 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.undergrad.rpc.client; | ||||||
|  | 
 | ||||||
|  | import cn.wustlinghang.mywust.network.request.RequestFactory; | ||||||
|  | import cn.wustlinghang.mywust.network.Requester; | ||||||
|  | import cn.wustlinghang.mywust.network.entitys.HttpResponse; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.undergrad.rpc.entity.RegisterRequestBody; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import com.google.common.collect.Lists; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | 
 | ||||||
|  | @Slf4j | ||||||
|  | public class ConsulClient { | ||||||
|  |     private final String serviceId; | ||||||
|  |     private final String serviceName; | ||||||
|  |     private final String consulAddress; | ||||||
|  |     private final ObjectMapper objectMapper; | ||||||
|  |     private final Requester requester; | ||||||
|  | 
 | ||||||
|  |     public ConsulClient(String serviceName, String serviceId, String consulAddress, | ||||||
|  |                         ObjectMapper objectMapper, Requester requester) { | ||||||
|  |         this.serviceId = serviceId; | ||||||
|  |         this.serviceName = serviceName; | ||||||
|  |         this.consulAddress = consulAddress; | ||||||
|  | 
 | ||||||
|  |         this.objectMapper = objectMapper; | ||||||
|  |         this.requester = requester; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void register(String remoteAddress, int remotePort) throws IOException { | ||||||
|  |         var checksItem = RegisterRequestBody.ChecksItem.builder() | ||||||
|  |                 .http("http://" + remoteAddress + "/health") | ||||||
|  |                 .interval("3s") | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         var registerRequestBody = RegisterRequestBody.builder() | ||||||
|  |                 .id(serviceId) | ||||||
|  |                 .name(serviceName) | ||||||
|  |                 .address("127.0.0.1") | ||||||
|  |                 .port(remotePort) | ||||||
|  |                 .checks(Lists.newArrayList(checksItem)) | ||||||
|  |                 .build(); | ||||||
|  | 
 | ||||||
|  |         byte[] registerRequestData = objectMapper.writeValueAsBytes(registerRequestBody); | ||||||
|  |         var registerRequest = RequestFactory.makeHttpRequest(consulAddress + "/v1/agent/service/register", registerRequestData); | ||||||
|  |         var registerResponse = requester.put(registerRequest); | ||||||
|  |         if (registerResponse.getStatusCode() != HttpResponse.HTTP_OK) { | ||||||
|  |             log.warn("注册中心注册不成功,请手动注册"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void deregister() throws IOException { | ||||||
|  |         var deregisterRequest = RequestFactory.makeHttpRequest(consulAddress + "/v1/agent/service/deregister/" + serviceId); | ||||||
|  |         var deregisterResponse = requester.put(deregisterRequest); | ||||||
|  |         if (deregisterResponse.getStatusCode() != HttpResponse.HTTP_OK) { | ||||||
|  |             log.warn("服务注销不成功,请手动注册"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,100 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.undergrad.rpc.client; | ||||||
|  | 
 | ||||||
|  | import cn.hutool.core.codec.Base64; | ||||||
|  | import cn.wustlinghang.mywust.network.request.RequestFactory; | ||||||
|  | import cn.wustlinghang.mywust.network.Requester; | ||||||
|  | import cn.wustlinghang.wusthelper.internal.undergrad.rpc.config.FrpConfig; | ||||||
|  | import com.fasterxml.jackson.databind.JsonNode; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import com.fasterxml.jackson.databind.node.MissingNode; | ||||||
|  | import jakarta.inject.Singleton; | ||||||
|  | import org.ini4j.Ini; | ||||||
|  | 
 | ||||||
|  | import java.io.ByteArrayInputStream; | ||||||
|  | import java.io.ByteArrayOutputStream; | ||||||
|  | import java.io.IOException; | ||||||
|  | 
 | ||||||
|  | @Singleton | ||||||
|  | public class FrpcClient { | ||||||
|  | 
 | ||||||
|  |     private final String frpcAdminAddress; | ||||||
|  | 
 | ||||||
|  |     private final String frpAuthHeaderValue; | ||||||
|  | 
 | ||||||
|  |     private final Requester requester; | ||||||
|  | 
 | ||||||
|  |     private final ObjectMapper objectMapper; | ||||||
|  | 
 | ||||||
|  |     private String frpProxyName; | ||||||
|  | 
 | ||||||
|  |     public FrpcClient(FrpConfig frpConfig, | ||||||
|  |                       Requester requester, ObjectMapper objectMapper) { | ||||||
|  | 
 | ||||||
|  |         this.frpcAdminAddress = frpConfig.getFrpcAdminAddress(); | ||||||
|  | 
 | ||||||
|  |         this.requester = requester; | ||||||
|  |         this.objectMapper = objectMapper; | ||||||
|  | 
 | ||||||
|  |         String username = frpConfig.getFrpcAdminUsername(); | ||||||
|  |         String password = frpConfig.getFrpcAdminPassword(); | ||||||
|  |         this.frpAuthHeaderValue = "Basic " + Base64.encode(username + ":" + password); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void addFrpProxy(String serviceName, String serviceId, String localAddress, String localPort) | ||||||
|  |             throws IOException { | ||||||
|  |         this.frpProxyName = String.format("%s-%s", serviceName, serviceId); | ||||||
|  | 
 | ||||||
|  |         Ini ini = this.getFrpConfig(); | ||||||
|  |         ini.put(frpProxyName, "type", "tcp"); | ||||||
|  |         ini.put(frpProxyName, "local_address", localAddress); | ||||||
|  |         ini.put(frpProxyName, "local_port", localPort); | ||||||
|  |         ini.put(frpProxyName, "use_compression", "true"); | ||||||
|  | 
 | ||||||
|  |         var output = new ByteArrayOutputStream(); | ||||||
|  |         ini.store(output); | ||||||
|  | 
 | ||||||
|  |         this.reloadFrpConfig(ini); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void removeProxy() throws IOException { | ||||||
|  |         Ini ini = this.getFrpConfig(); | ||||||
|  |         ini.remove(this.frpProxyName); | ||||||
|  | 
 | ||||||
|  |         this.reloadFrpConfig(ini); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Ini getFrpConfig() throws IOException { | ||||||
|  |         var frpConfigRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/config"); | ||||||
|  |         frpConfigRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         var response = requester.get(frpConfigRequest); | ||||||
|  | 
 | ||||||
|  |         return new Ini(new ByteArrayInputStream(response.getBody())); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void reloadFrpConfig(Ini ini) throws IOException { | ||||||
|  |         var output = new ByteArrayOutputStream(); | ||||||
|  |         ini.store(output); | ||||||
|  | 
 | ||||||
|  |         var uploadRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/config", output.toByteArray()); | ||||||
|  |         uploadRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         requester.put(uploadRequest); | ||||||
|  |         var reloadRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/reload"); | ||||||
|  |         reloadRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         requester.get(reloadRequest); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public JsonNode getProxyStatus(String serviceName, String serviceId) throws IOException { | ||||||
|  |         var statusRequest = RequestFactory.makeHttpRequest(frpcAdminAddress + "/api/status"); | ||||||
|  |         statusRequest.addHeaders("Authorization", frpAuthHeaderValue); | ||||||
|  |         var statusResponse = requester.get(statusRequest); | ||||||
|  | 
 | ||||||
|  |         JsonNode proxies = objectMapper.readTree(statusResponse.getBody()).path("tcp"); | ||||||
|  |         for (JsonNode proxy : proxies) { | ||||||
|  |             if (proxy.path("name").asText().equals(serviceName + "-" + serviceId)) { | ||||||
|  |                 return proxy; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return MissingNode.getInstance(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,22 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.undergrad.rpc.config; | ||||||
|  | 
 | ||||||
|  | import jakarta.inject.Singleton; | ||||||
|  | import jakarta.ws.rs.DefaultValue; | ||||||
|  | import lombok.Data; | ||||||
|  | import org.eclipse.microprofile.config.inject.ConfigProperty; | ||||||
|  | 
 | ||||||
|  | @Data | ||||||
|  | @Singleton | ||||||
|  | public class FrpConfig { | ||||||
|  |     @DefaultValue("http://127.0.0.1:7400") | ||||||
|  |     @ConfigProperty(name = "frpc.admin-api.address") | ||||||
|  |     String frpcAdminAddress; | ||||||
|  | 
 | ||||||
|  |     @DefaultValue("") | ||||||
|  |     @ConfigProperty(name = "frpc.admin.username") | ||||||
|  |     String frpcAdminUsername; | ||||||
|  | 
 | ||||||
|  |     @DefaultValue("") | ||||||
|  |     @ConfigProperty(name = "frpc.admin.password") | ||||||
|  |     String frpcAdminPassword; | ||||||
|  | } | ||||||
| @ -0,0 +1,18 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.undergrad.rpc.config; | ||||||
|  | 
 | ||||||
|  | import jakarta.inject.Singleton; | ||||||
|  | import jakarta.ws.rs.DefaultValue; | ||||||
|  | import lombok.Data; | ||||||
|  | import org.eclipse.microprofile.config.inject.ConfigProperty; | ||||||
|  | 
 | ||||||
|  | @Data | ||||||
|  | @Singleton | ||||||
|  | public class RpcConfig { | ||||||
|  |     @DefaultValue("default.service") | ||||||
|  |     @ConfigProperty(name = "rpc.service.name") | ||||||
|  |     String serviceName; | ||||||
|  | 
 | ||||||
|  |     @DefaultValue("http://127.0.0.1:8500") | ||||||
|  |     @ConfigProperty(name = "rpc.register.consul-api.address") | ||||||
|  |     String consulAddress; | ||||||
|  | } | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.undergrad.rpc.entity; | ||||||
|  | 
 | ||||||
|  | import lombok.Builder; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | @Builder | ||||||
|  | public record RegisterRequestBody(String address, int port, List<ChecksItem> checks, String name, String id) { | ||||||
|  | 
 | ||||||
|  |     @Builder | ||||||
|  |     public record ChecksItem(String http, String interval) { | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | package cn.wustlinghang.wusthelper.internal.undergrad.rpc.health; | ||||||
|  | 
 | ||||||
|  | import jakarta.ws.rs.GET; | ||||||
|  | import jakarta.ws.rs.Path; | ||||||
|  | import jakarta.ws.rs.core.Response; | ||||||
|  | 
 | ||||||
|  | @Path("/health") | ||||||
|  | public class HealthCheck { | ||||||
|  |     private static final Response successResponse =  Response.accepted().status(200).build(); | ||||||
|  | 
 | ||||||
|  |     @GET | ||||||
|  |     @Path("/") | ||||||
|  |     public Response response() {return successResponse;} | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue