diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3c1f0c8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM java:21 + +ADD target/Jetbrains-Help.jar /Jetbrains-Help.jar + +RUN bash -c 'touch /Jetbrains-Help.jar' + +ENV TZ=Asia/Shanghai +RUN ln -sf /usr/share/zoneinfo/{TZ} /etc/localtime && echo "{TZ}" > /etc/timezone + + +EXPOSE 10768 + +ENTRYPOINT ["java", "-jar","/Jetbrains-Help.jar"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8aa7a53..4a7687b 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,7 @@ + ${artifactId} org.springframework.boot diff --git a/src/main/java/com/jetbrains/help/JetbrainsHelpApplication.java b/src/main/java/com/jetbrains/help/JetbrainsHelpApplication.java index 20310c3..8235b84 100644 --- a/src/main/java/com/jetbrains/help/JetbrainsHelpApplication.java +++ b/src/main/java/com/jetbrains/help/JetbrainsHelpApplication.java @@ -1,8 +1,13 @@ package com.jetbrains.help; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.net.Ipv4Util; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import com.jetbrains.help.context.*; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -11,9 +16,11 @@ import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; +import java.net.InetAddress; import java.util.Collection; import java.util.List; +@Slf4j @EnableScheduling @Import(SpringUtil.class) @SpringBootApplication @@ -23,12 +30,21 @@ public class JetbrainsHelpApplication { SpringApplication.run(JetbrainsHelpApplication.class, args); } + @SneakyThrows @EventListener(ApplicationReadyEvent.class) public void ready() { ProductsContextHolder.init(); PluginsContextHolder.init(); CertificateContextHolder.init(); AgentContextHolder.init(); + + InetAddress localHost = InetAddress.getLocalHost(); + String address = CharSequenceUtil.format("http://{}:{}", localHost.getHostAddress(), SpringUtil.getProperty("server.port")); + String runSuccessWarn = "\n====================================================================================\n" + + "= Jetbrains-Help Run Success~ =\n" + + "= address:" + address + " =\n" + + "====================================================================================\n"; + log.info(runSuccessWarn); } @Scheduled(cron = "0 0 12 * * ?") diff --git a/src/main/java/com/jetbrains/help/context/AgentContextHolder.java b/src/main/java/com/jetbrains/help/context/AgentContextHolder.java index dea6515..166f303 100644 --- a/src/main/java/com/jetbrains/help/context/AgentContextHolder.java +++ b/src/main/java/com/jetbrains/help/context/AgentContextHolder.java @@ -24,25 +24,33 @@ import java.util.concurrent.CompletableFuture; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class AgentContextHolder { - private static final String JA_NETFILTER_FILE_PATH = "static/agent/ja-netfilter"; -// private static final String JA_NETFILTER_FILE_PATH = "static/agent/ja"; + private static final String JA_NETFILTER_FILE_PATH = "external/agent/ja-netfilter"; private static final String POWER_CONF_FILE_NAME = JA_NETFILTER_FILE_PATH + "/config/power.conf"; private static File jaNetfilterFile; + private static File jaNetfilterZipFile; + public static void init() { log.info("Agent context init loading..."); - jaNetfilterFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH); - if (!powerConfHasInit()) { - log.info("Agent config init loading..."); - loadPowerConf(); - zipJaNetfilter(); - log.info("Agent config init success !"); + jaNetfilterZipFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH + ".zip"); + if (!FileTools.fileExists(JA_NETFILTER_FILE_PATH)) { + unzipJaNetfilter(); + if (!powerConfHasInit()) { + log.info("Agent config init loading..."); + loadPowerConf(); + zipJaNetfilter(); + log.info("Agent config init success !"); + } } log.info("Agent context init success !"); } + public static File jaNetfilterZipFile() { + return AgentContextHolder.jaNetfilterZipFile; + } + private static boolean powerConfHasInit() { File powerConfFile = FileTools.getFileOrCreat(POWER_CONF_FILE_NAME); String powerConfStr; @@ -90,7 +98,11 @@ public class AgentContextHolder { } } + private static void unzipJaNetfilter() { + jaNetfilterFile = ZipUtil.unzip(jaNetfilterZipFile); + } + private static void zipJaNetfilter() { - ZipUtil.zip(jaNetfilterFile); + jaNetfilterZipFile = ZipUtil.zip(jaNetfilterFile); } } diff --git a/src/main/java/com/jetbrains/help/context/CertificateContextHolder.java b/src/main/java/com/jetbrains/help/context/CertificateContextHolder.java index bb0c1c7..e0acbfa 100644 --- a/src/main/java/com/jetbrains/help/context/CertificateContextHolder.java +++ b/src/main/java/com/jetbrains/help/context/CertificateContextHolder.java @@ -31,10 +31,10 @@ import java.security.cert.CertificateException; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class CertificateContextHolder { - private static final String ROOT_KEY_FILE_NAME = "certificate/root.key"; - private static final String PRIVATE_KEY_FILE_NAME = "certificate/private.key"; - private static final String PUBLIC_KEY_FILE_NAME = "certificate/public.key"; - private static final String CET_FILE_NAME = "certificate/ca.crt"; + private static final String ROOT_KEY_FILE_NAME = "external/certificate/root.key"; + private static final String PRIVATE_KEY_FILE_NAME = "external/certificate/private.key"; + private static final String PUBLIC_KEY_FILE_NAME = "external/certificate/public.key"; + private static final String CET_FILE_NAME = "external/certificate/ca.crt"; private static File rootKeyFile; @@ -52,6 +52,7 @@ public class CertificateContextHolder { || !FileTools.fileExists(CET_FILE_NAME)) { log.info("certificate context generate loading..."); generateCertificate(); + log.info("certificate context generate success!"); } else { privateKeyFile = FileTools.getFileOrCreat(PRIVATE_KEY_FILE_NAME); publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME); diff --git a/src/main/java/com/jetbrains/help/context/PluginsContextHolder.java b/src/main/java/com/jetbrains/help/context/PluginsContextHolder.java index ee4e9da..4b25698 100644 --- a/src/main/java/com/jetbrains/help/context/PluginsContextHolder.java +++ b/src/main/java/com/jetbrains/help/context/PluginsContextHolder.java @@ -13,6 +13,7 @@ import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.List; @@ -28,7 +29,7 @@ public class PluginsContextHolder { private static final String PLUGIN_INFO_URL = PLUGIN_BASIC_URL + "/api/plugins/"; - private static final String PLUGIN_JSON_FILE_NAME = "plugin.json"; + private static final String PLUGIN_JSON_FILE_NAME = "external/data/plugin.json"; private static List pluginCacheList; @@ -84,11 +85,14 @@ public class PluginsContextHolder { public static PluginList pluginList() { return HttpUtil.createGet(PLUGIN_LIST_URL) .thenFunction(response -> { - InputStream is = response.bodyStream(); - if (!response.isOk()) { - throw new IllegalArgumentException(CharSequenceUtil.format("{} The request failed = {}", PLUGIN_LIST_URL, response)); + try (InputStream is = response.bodyStream()) { + if (!response.isOk()) { + throw new IllegalArgumentException(CharSequenceUtil.format("{} The request failed = {}", PLUGIN_LIST_URL, response)); + } + return IoUtil.readObj(is, PluginList.class); + } catch (IOException e) { + throw new IllegalArgumentException(CharSequenceUtil.format("{} The request io read failed", PLUGIN_LIST_URL), e); } - return IoUtil.readObj(is, PluginList.class); }); } @@ -119,11 +123,14 @@ public class PluginsContextHolder { public static PluginInfo pluginInfo(Long pluginId) { return HttpUtil.createGet(PLUGIN_INFO_URL + pluginId) .thenFunction(response -> { - InputStream is = response.bodyStream(); - if (!response.isOk()) { - throw new IllegalArgumentException(CharSequenceUtil.format("{} The request failed = {}", PLUGIN_INFO_URL, response)); + try (InputStream is = response.bodyStream()) { + if (!response.isOk()) { + throw new IllegalArgumentException(CharSequenceUtil.format("{} The request failed = {}", PLUGIN_INFO_URL, response)); + } + return IoUtil.readObj(is, PluginInfo.class); + } catch (IOException e) { + throw new IllegalArgumentException(CharSequenceUtil.format("{} The request io read failed", PLUGIN_LIST_URL), e); } - return IoUtil.readObj(is, PluginInfo.class); }); } diff --git a/src/main/java/com/jetbrains/help/context/ProductsContextHolder.java b/src/main/java/com/jetbrains/help/context/ProductsContextHolder.java index b7a8d55..a43066d 100644 --- a/src/main/java/com/jetbrains/help/context/ProductsContextHolder.java +++ b/src/main/java/com/jetbrains/help/context/ProductsContextHolder.java @@ -18,7 +18,7 @@ import java.util.List; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ProductsContextHolder { - private static final String PRODUCT_JSON_FILE_NAME = "product.json"; + private static final String PRODUCT_JSON_FILE_NAME = "external/data/product.json"; private static List productCacheList; diff --git a/src/main/java/com/jetbrains/help/route/IndexController.java b/src/main/java/com/jetbrains/help/route/IndexController.java index 5e3a0e8..984b6f2 100644 --- a/src/main/java/com/jetbrains/help/route/IndexController.java +++ b/src/main/java/com/jetbrains/help/route/IndexController.java @@ -1,20 +1,32 @@ package com.jetbrains.help.route; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.StrUtil; import com.jetbrains.help.JetbrainsHelpApplication; +import com.jetbrains.help.context.AgentContextHolder; import com.jetbrains.help.context.PluginsContextHolder; import com.jetbrains.help.context.ProductsContextHolder; import com.jetbrains.help.properties.JetbrainsHelpProperties; import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import java.io.File; import java.util.List; +import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION; +import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM; + @Controller @RequiredArgsConstructor public class IndexController { @@ -48,4 +60,14 @@ public class IndexController { model.addAttribute("defaults", jetbrainsHelpProperties); return "index::product-list"; } + + @GetMapping("ja-netfilter") + @ResponseBody + public ResponseEntity downloadJaNetfilter() { + File jaNetfilterZipFile = AgentContextHolder.jaNetfilterZipFile(); + return ResponseEntity.ok() + .header(CONTENT_DISPOSITION, "attachment;filename=" + jaNetfilterZipFile.getName()) + .contentType(APPLICATION_OCTET_STREAM) + .body(new InputStreamResource(FileUtil.getInputStream(jaNetfilterZipFile))); + } } diff --git a/src/main/java/com/jetbrains/help/util/FileTools.java b/src/main/java/com/jetbrains/help/util/FileTools.java index 0518368..5b78a16 100644 --- a/src/main/java/com/jetbrains/help/util/FileTools.java +++ b/src/main/java/com/jetbrains/help/util/FileTools.java @@ -2,6 +2,9 @@ package com.jetbrains.help.util; import cn.hutool.core.io.FileUtil; import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import org.springframework.boot.system.ApplicationHome; import org.springframework.core.io.ClassPathResource; import java.io.File; @@ -9,25 +12,35 @@ import java.io.IOException; public interface FileTools { - static boolean fileExists(String pathOrFile) { - return FileUtil.file(new ClassPathResource(pathOrFile).getPath()).exists(); + ApplicationHome application = new ApplicationHome(); + + + static boolean fileExists(String path) { + return getFile(path).exists(); } - static File getFileOrCreat(String pathOrFile) { - File file = FileUtil.file(new ClassPathResource(pathOrFile).getPath()); - if (!file.exists()) { - try { - File parentFile = file.getParentFile(); - if (!parentFile.exists() && !parentFile.mkdir()) { - throw new IllegalArgumentException(CharSequenceUtil.format("{} File directory create Failed", pathOrFile)); + static File getFile(String path) { + File homeDir = application.getDir(); + File source = application.getSource(); + ClassPathResource classPathResource = new ClassPathResource(path); + return ObjectUtil.isNull(source) ? FileUtil.file(classPathResource.getPath()) : FileUtil.file(homeDir, path); + } + + static File getFileOrCreat(String path) { + File file = getFile(path); + if (ObjectUtil.isNotNull(application.getSource())) { + ClassPathResource classPathResource = new ClassPathResource(path); + File classPathFile = FileUtil.file(classPathResource.getPath()); + if (classPathResource.exists() && !file.exists()) { + try { + FileUtil.writeFromStream(classPathResource.getInputStream(), classPathFile); + } catch (Exception e) { + throw new IllegalArgumentException(CharSequenceUtil.format("{} File read failed", classPathFile.getPath()), e); } - if (!file.createNewFile()) { - throw new IllegalArgumentException(CharSequenceUtil.format("{} File create failed", pathOrFile)); - } - } catch (IOException e) { - throw new IllegalArgumentException(CharSequenceUtil.format("{} File create failed", pathOrFile), e); + FileUtil.copy(classPathFile, file, true); } } return file; + } } diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..c019b0b --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + + ██╗███████╗████████╗██████╗ ██████╗ █████╗ ██╗███╗ ██╗███████╗ ██╗ ██╗███████╗██╗ ██████╗ + ██║██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔══██╗██║████╗ ██║██╔════╝ ██║ ██║██╔════╝██║ ██╔══██╗ + ██║█████╗ ██║ ██████╔╝██████╔╝███████║██║██╔██╗ ██║███████╗█████╗███████║█████╗ ██║ ██████╔╝ +██ ██║██╔══╝ ██║ ██╔══██╗██╔══██╗██╔══██║██║██║╚██╗██║╚════██║╚════╝██╔══██║██╔══╝ ██║ ██╔═══╝ +╚█████╔╝███████╗ ██║ ██████╔╝██║ ██║██║ ██║██║██║ ╚████║███████║ ██║ ██║███████╗███████╗██║ + ╚════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ + +${AnsiColor.BRIGHT_YELLOW} Spring Boot Version: ${spring-boot.version} + diff --git a/src/main/resources/certificate/root.key b/src/main/resources/external/certificate/root.key similarity index 100% rename from src/main/resources/certificate/root.key rename to src/main/resources/external/certificate/root.key diff --git a/src/main/resources/plugin.json b/src/main/resources/external/data/plugin.json similarity index 100% rename from src/main/resources/plugin.json rename to src/main/resources/external/data/plugin.json diff --git a/src/main/resources/product.json b/src/main/resources/external/data/product.json similarity index 100% rename from src/main/resources/product.json rename to src/main/resources/external/data/product.json diff --git a/src/main/resources/static/agent/ja-netfilter/config/dns.conf b/src/main/resources/static/agent/ja-netfilter/config/dns.conf deleted file mode 100644 index 54de89f..0000000 --- a/src/main/resources/static/agent/ja-netfilter/config/dns.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DNS] -; put dns filter rules here - diff --git a/src/main/resources/static/agent/ja-netfilter/config/power.conf b/src/main/resources/static/agent/ja-netfilter/config/power.conf deleted file mode 100644 index a245d18..0000000 --- a/src/main/resources/static/agent/ja-netfilter/config/power.conf +++ /dev/null @@ -1 +0,0 @@ -[Result] \ No newline at end of file diff --git a/src/main/resources/static/agent/ja-netfilter/config/url.conf b/src/main/resources/static/agent/ja-netfilter/config/url.conf deleted file mode 100644 index 898bdb2..0000000 --- a/src/main/resources/static/agent/ja-netfilter/config/url.conf +++ /dev/null @@ -1,4 +0,0 @@ -[URL] -; put url filter rules here -PREFIX,https://account.jetbrains.com/lservice/rpc/validateKey.action - diff --git a/src/main/resources/static/agent/ja-netfilter/ja-netfilter.jar b/src/main/resources/static/agent/ja-netfilter/ja-netfilter.jar deleted file mode 100644 index b72565a..0000000 Binary files a/src/main/resources/static/agent/ja-netfilter/ja-netfilter.jar and /dev/null differ diff --git a/src/main/resources/static/agent/ja-netfilter/plugins/dns.jar b/src/main/resources/static/agent/ja-netfilter/plugins/dns.jar deleted file mode 100644 index 113c699..0000000 Binary files a/src/main/resources/static/agent/ja-netfilter/plugins/dns.jar and /dev/null differ diff --git a/src/main/resources/static/agent/ja-netfilter/plugins/hideme.jar b/src/main/resources/static/agent/ja-netfilter/plugins/hideme.jar deleted file mode 100644 index a21ed52..0000000 Binary files a/src/main/resources/static/agent/ja-netfilter/plugins/hideme.jar and /dev/null differ diff --git a/src/main/resources/static/agent/ja-netfilter/plugins/power.jar b/src/main/resources/static/agent/ja-netfilter/plugins/power.jar deleted file mode 100644 index 1acc698..0000000 Binary files a/src/main/resources/static/agent/ja-netfilter/plugins/power.jar and /dev/null differ diff --git a/src/main/resources/static/agent/ja-netfilter/plugins/url.jar b/src/main/resources/static/agent/ja-netfilter/plugins/url.jar deleted file mode 100644 index 495804e..0000000 Binary files a/src/main/resources/static/agent/ja-netfilter/plugins/url.jar and /dev/null differ diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index d579cbe..91b3374 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -11,7 +11,7 @@

- 🇨🇳 Download ja-netfilter.zip , and configure + 🇨🇳 Download ja-netfilter.zip , and configure your JetBrains's vmoptions!
🇨🇳 Also you can Refill license information to customizer your license!
🇨🇳 Please note that this page is only developed and used by