This commit is contained in:
Cikaros 2024-09-10 17:41:50 +08:00 committed by GitHub
commit db04ba66e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 336 additions and 66 deletions

View File

@ -53,20 +53,21 @@
### 功能列表
| 功能 | DID |
|:-------------------------|:---:|
| Jetbrains全产品支持 | ✅ |
| Jetbrains全插件支持 | ✅ |
| 插件库全自动订阅官网更新 | ✅ |
| 公私钥/证书, 自动生成管理 | ✅ |
| power.conf文件自动配置 | ✅ |
| ja-netfilter.zip自动打包 | ✅ |
| 自定义License Show | ✅ |
| 支持实时搜索 | ✅ |
| 插件默认按名称排序 | ✅ |
| 支持local/jar/dockerfile运行 | ✅ |
| 单码全家桶激活支持 | ✅ |
| …… | ☑️ |
| 功能 | DID |
|:---------------------------|:---:|
| Jetbrains全产品支持 | ✅ |
| Jetbrains全插件支持 | ✅ |
| 插件库全自动订阅官网更新 | ✅ |
| 公私钥/证书, 自动生成管理 | ✅ |
| power.conf文件自动配置 | ✅ |
| ja-netfilter.zip自动打包 | ✅ |
| 自定义License Show | ✅ |
| 支持实时搜索 | ✅ |
| 插件默认按名称排序 | ✅ |
| 支持local/jar/dockerfile运行 | ✅ |
| 单码全家桶激活支持 | ✅ |
| Jetbrains License Server支持 | ✅ |
| …… | ☑️ |
## 运行教程
@ -139,7 +140,7 @@
- **点击** `编辑自定义虚拟机选型`
- **键入** 如下配置
```
-javaagent:you-path/ja-netfilter.jar
-javaagent:you-path/ja-netfilter.jar=jetbrains
--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
--add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
```
@ -157,7 +158,7 @@
- **点击** `编辑JVM选项`
- **键入** 如下配置
```
-javaagent:you-path/ja-netfilter.jar
-javaagent:you-path/ja-netfilter.jar=jetbrains
--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
--add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
```

View File

@ -1,9 +1,11 @@
package com.jetbrains.help;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.Ipv4Util;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.jetbrains.help.context.*;
import lombok.SneakyThrows;
@ -17,6 +19,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import java.net.InetAddress;
import java.security.cert.Certificate;
import java.util.Collection;
import java.util.List;
@ -37,7 +40,6 @@ public class JetbrainsHelpApplication {
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" +

View File

@ -26,7 +26,7 @@ public class AgentContextHolder {
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 final String POWER_CONF_FILE_NAME = JA_NETFILTER_FILE_PATH + "/config-jetbrains/power.conf";
private static File jaNetfilterFile;
@ -35,7 +35,7 @@ public class AgentContextHolder {
public static void init() {
log.info("Agent context init loading...");
jaNetfilterZipFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH + ".zip");
if (!FileTools.fileExists(JA_NETFILTER_FILE_PATH)) {
if (FileTools.fileNotExists(JA_NETFILTER_FILE_PATH)) {
unzipJaNetfilter();
if (!powerConfHasInit()) {
log.info("Agent config init loading...");
@ -75,14 +75,27 @@ public class AgentContextHolder {
@SneakyThrows
private static String generatePowerConfigRule() {
X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile()));
RSAPublicKey publicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile()));
RSAPublicKey rootPublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.rootKeyFile()));
StringBuilder result = new StringBuilder();
X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile()));
X509Certificate licenseCrt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getLicenseCrtFile()));
RSAPublicKey publicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile()));
RSAPublicKey rootPublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getRootKeyFile()));
RSAPublicKey rootLicensePublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getRootLicenseKeyFile()));
BigInteger x = new BigInteger(1, crt.getSignature());
BigInteger y = BigInteger.valueOf(65537L);
BigInteger y = rootPublicKey.getPublicExponent();
BigInteger z = rootPublicKey.getModulus();
BigInteger r = x.modPow(publicKey.getPublicExponent(), publicKey.getModulus());
return CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r);
result.append("; Activation Code").append("\n")
.append(CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r))
.append("\n");
x = new BigInteger(1, licenseCrt.getSignature());
z = rootLicensePublicKey.getModulus();
y = rootLicensePublicKey.getPublicExponent();
r = x.modPow(publicKey.getPublicExponent(), publicKey.getModulus());
result.append("; License Server").append("\n")
.append(CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r))
.append("\n");
return result.toString();
}
private static String generatePowerConfigStr(String ruleValue) {

View File

@ -3,10 +3,12 @@ package com.jetbrains.help.context;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.crypto.PemUtil;
import cn.hutool.crypto.SecureUtil;
import com.jetbrains.help.util.FileTools;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.x500.X500Name;
@ -27,29 +29,47 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
/**
* <a href="https://github.com/JetBrains/marketplace-makemecoffee-plugin/blob/master/src/main/java/com/company/license/CheckLicense.java">源证书来源</a>
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CertificateContextHolder {
private static final String ROOT_KEY_FILE_NAME = "external/certificate/root.key";
private static final String ROOT_LICENSE_KEY_FILE_NAME = "external/certificate/license-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 final String LICENSE_CET_FILE_NAME = "external/certificate/license-ca.crt";
public static final String SERVER_UID = "cikaros.top";
@Getter
private static File rootKeyFile;
@Getter
private static File rootLicenseKeyFile;
@Getter
private static File privateKeyFile;
@Getter
private static File publicKeyFile;
@Getter
private static File crtFile;
@Getter
private static File licenseCrtFile;
public static void init() {
log.info("certificate context init loading...");
rootKeyFile = FileTools.getFileOrCreat(ROOT_KEY_FILE_NAME);
if (!FileTools.fileExists(PRIVATE_KEY_FILE_NAME)
|| !FileTools.fileExists(PUBLIC_KEY_FILE_NAME)
|| !FileTools.fileExists(CET_FILE_NAME)) {
rootLicenseKeyFile = FileTools.getFileOrCreat(ROOT_LICENSE_KEY_FILE_NAME);
if (FileTools.fileNotExists(PRIVATE_KEY_FILE_NAME)
|| FileTools.fileNotExists(PUBLIC_KEY_FILE_NAME)
|| FileTools.fileNotExists(CET_FILE_NAME)
|| FileTools.fileNotExists(LICENSE_CET_FILE_NAME)) {
log.info("certificate context generate loading...");
generateCertificate();
log.info("certificate context generate success!");
@ -57,27 +77,11 @@ public class CertificateContextHolder {
privateKeyFile = FileTools.getFileOrCreat(PRIVATE_KEY_FILE_NAME);
publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME);
crtFile = FileTools.getFileOrCreat(CET_FILE_NAME);
licenseCrtFile = FileTools.getFileOrCreat(LICENSE_CET_FILE_NAME);
}
log.info("certificate context init success !");
}
public static File rootKeyFile() {
return CertificateContextHolder.rootKeyFile;
}
public static File privateKeyFile() {
return CertificateContextHolder.privateKeyFile;
}
public static File publicKeyFile() {
return CertificateContextHolder.publicKeyFile;
}
public static File crtFile() {
return CertificateContextHolder.crtFile;
}
public static void generateCertificate() {
KeyPair keyPair = SecureUtil.generateKeyPair("RSA", 4096);
PrivateKey privateKey = keyPair.getPrivate();
@ -86,16 +90,8 @@ public class CertificateContextHolder {
PemUtil.writePemObject("PRIVATE KEY", privateKey.getEncoded(), FileUtil.getWriter(privateKeyFile, StandardCharsets.UTF_8, false));
publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME);
PemUtil.writePemObject("PUBLIC KEY", publicKey.getEncoded(), FileUtil.getWriter(publicKeyFile, StandardCharsets.UTF_8, false));
JcaX509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
new X500Name("CN=JetProfile CA"),
BigInteger.valueOf(System.currentTimeMillis()),
DateUtil.yesterday(),
DateUtil.date().offset(DateField.YEAR, 100),
new X500Name("CN=Jetbrains-Help"),
SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
try {
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey);
Certificate certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateBuilder.build(signer));
Certificate certificate = generateCertificate(keyPair,"JetProfile CA","Jetbrains-Help");
crtFile = FileTools.getFileOrCreat(CET_FILE_NAME);
PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(crtFile, StandardCharsets.UTF_8, false));
} catch (OperatorCreationException e) {
@ -105,7 +101,33 @@ public class CertificateContextHolder {
} catch (CertificateException e) {
throw new IllegalArgumentException("The certificate read exception", e);
}
//TODO 创建License Servers CA
try {
Certificate certificate = generateCertificate(keyPair,"License Servers CA",String.format("%s.lsrv.jetbrains.com", SERVER_UID));
licenseCrtFile = FileTools.getFileOrCreat(LICENSE_CET_FILE_NAME);
PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(licenseCrtFile, StandardCharsets.UTF_8, false));
} catch (OperatorCreationException e) {
throw new IllegalArgumentException("Certificate operator creation exception", e);
} catch (CertificateEncodingException e) {
throw new IllegalArgumentException("The certificate encoding exception", e);
} catch (CertificateException e) {
throw new IllegalArgumentException("The certificate read exception", e);
}
}
private static Certificate generateCertificate(KeyPair keyPair, String issuer, String subject) throws OperatorCreationException, CertificateException {
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
JcaX509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
new X500Name(String.format("CN=%s", issuer)),
BigInteger.valueOf(System.currentTimeMillis()),
DateUtil.yesterday(),
DateUtil.date().offset(DateField.YEAR, 100),
new X500Name(String.format("CN=%s", subject)),
SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey);
return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateBuilder.build(signer));
}
}

View File

@ -1,20 +1,24 @@
package com.jetbrains.help.context;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.crypto.PemUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SignUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.json.JSONUtil;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
@ -22,7 +26,7 @@ import java.security.cert.CertificateEncodingException;
import java.util.List;
import java.util.Set;
import static cn.hutool.crypto.asymmetric.SignAlgorithm.SHA1withRSA;
import static cn.hutool.crypto.asymmetric.SignAlgorithm.*;
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@ -43,9 +47,9 @@ public class LicenseContextHolder {
.setProducts(products);
String licensePartJson = JSONUtil.toJsonStr(licensePart);
String licensePartBase64 = Base64.encode(licensePartJson);
PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.privateKeyFile()));
PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile()));
Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile()));
PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.getPrivateKeyFile()));
PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile()));
Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile()));
Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded());
String signatureBase64 = Base64.encode(sign.sign(licensePartJson));
String certBase64;
@ -74,4 +78,147 @@ public class LicenseContextHolder {
private String paidUpTo;
}
public static String obtainTicket(ObtainTicketRequest request) {
PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.getPrivateKeyFile()));
PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile()));
Certificate cert = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile()));
Certificate licenseCert = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getLicenseCrtFile()));
ObtainTicketResponse response = obtainTicket(privateKey, publicKey, cert, licenseCert, request);
Document document = XmlUtil.beanToXml(response);
String result = XmlUtil.toStr(document, "UTF-8", true, true);
//!important 必须去除XML中所有的换行和空格即紧凑模式
result = result.replaceAll("[\n ]", "");
Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded());
String sign64 = Base64.encode(sign.sign(result));
String licenseCertBase64;
try {
licenseCertBase64 = Base64.encode(licenseCert.getEncoded());
} catch (CertificateEncodingException e) {
throw new IllegalArgumentException("Certificate extraction failed", e);
}
//<!-- SHA1withRSA-{下面 xml 主体内容的签名 }-{license Server CA} -->
String signRes = String.format("<!-- SHA1withRSA-%s-%s -->", sign64, licenseCertBase64);
return signRes + "\n" + result;
}
public static ObtainTicketResponse obtainTicket(PrivateKey privateKey, PublicKey publicKey, Certificate cert, Certificate licenseCert, ObtainTicketRequest request) {
String certBase64;
String licenseCertBase64;
try {
certBase64 = Base64.encode(cert.getEncoded());
licenseCertBase64 = Base64.encode(licenseCert.getEncoded());
} catch (CertificateEncodingException e) {
throw new IllegalArgumentException("Certificate extraction failed", e);
}
long time = System.currentTimeMillis();
long leaseTime = time + (1000L * 60 * 60 * 24 * 365);
//{失效时间戳}:{serverUid}
String serverLease = String.format("%d:%s", leaseTime, CertificateContextHolder.SERVER_UID);
Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded());
String signatureBase64 = Base64.encode(sign.sign(String.format("%d:%s", time, request.getMachineId())));
//{时间戳}:{客户端提交的machineId}:SHA1withRSA:{{时间戳}:{客户端提交的machineId}的签名}:{license Server CA}
String confirmationStamp = String.format("%d:%s:SHA1withRSA:%s:%s",
time, request.getMachineId(), signatureBase64, licenseCertBase64);
sign = SignUtil.sign(SHA512withRSA, privateKey.getEncoded(), publicKey.getEncoded());
byte[] bytes = sign.sign(serverLease);
signatureBase64 = Base64.encode(bytes);
//SHA512withRSA-{serverLease的值进行签名}-{JetProfile CA}
String leaseSignature = String.format("SHA512withRSA-%s-%s", signatureBase64, certBase64);
// String leaseSignature = new BigInteger(1,bytes).toString();
return ObtainTicketResponse.builder()
.action(Action.NONE)
.responseCode(ResponseCode.OK)
.message(ResponseCode.OK.name())
.ticketId("20")
.ticketProperties(String.format("licensee=%s\tlicenseType=4\tmetadata=0120230914PSAX000005", request.getUserName()))
.validationPeriod(71829000L)
.validationDeadlinePeriod(-1)
.prolongationPeriod(63462000L)//有效期
.salt(request.getSalt())
.serverUid(CertificateContextHolder.SERVER_UID)
.serverLease(serverLease)
.confirmationStamp(confirmationStamp)
.leaseSignature(leaseSignature)
.build();
}
public enum Action {
NONE,
}
public enum ResponseCode {
OK
}
/**
* 获取认证票据
*
* @author Cikaros
* @date 2023/11/1
*/
@Data
@Builder
public static class ObtainTicketResponse {
@Builder.Default
private Action action = Action.NONE;
private String confirmationStamp;
private String leaseSignature;
@Builder.Default
private String message = "";
@Builder.Default
private Long prolongationPeriod = 1800000L;
@Builder.Default
private ResponseCode responseCode = ResponseCode.OK;
private String salt;
private String serverLease;
private String serverUid;
@Builder.Default
private String ticketId = "1";
private String ticketProperties;
@Builder.Default
private Integer validationDeadlinePeriod = -1;
@Builder.Default
private Long validationPeriod = 1800000L;
}
/**
* @author Cikaros
* @date 2023/10/20
*/
@Data
public static class ObtainTicketRequest {
private String ideProductCode;
private Boolean empty;
private String buildDate;
private String buildNumber;
private Integer clientVersion;
private String hostName;
private String machineId;
private String productCode;
private String productFamilyId;
private String salt;
private Boolean secure;
private String userName;
private String version;
private Long versionNumber;
}
}

View File

@ -0,0 +1,22 @@
package com.jetbrains.help.controller;
import com.jetbrains.help.context.LicenseContextHolder;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/rpc")
public class ObtainTicketApiController {
@GetMapping(value = "/obtainTicket.action", produces = {"application/xml;charset=utf-8"})
@ResponseBody
public ResponseEntity<String> obtainTicket(LicenseContextHolder.ObtainTicketRequest request) {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_XML)
.body(LicenseContextHolder.obtainTicket(request));
}
}

View File

@ -4,15 +4,19 @@ 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 cn.hutool.crypto.digest.DigestUtil;
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 jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.cglib.core.Local;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
@ -20,11 +24,13 @@ 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 org.thymeleaf.util.DateUtils;
import java.io.File;
import java.util.List;
import java.util.*;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
@Controller
@ -34,15 +40,37 @@ public class IndexController {
private final JetbrainsHelpProperties jetbrainsHelpProperties;
@GetMapping
public String index(Model model) {
public String index(HttpServletRequest request,Model model) {
String basePath = (request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath());
List<ProductsContextHolder.ProductCache> productCacheList = ProductsContextHolder.productCacheList();
List<PluginsContextHolder.PluginCache> pluginCacheList = PluginsContextHolder.pluginCacheList();
model.addAttribute("products", productCacheList);
model.addAttribute("plugins", pluginCacheList);
model.addAttribute("defaults", jetbrainsHelpProperties);
model.addAttribute("basePath", basePath);
return "index";
}
@GetMapping("/scoop/ja-netfilter")
@ResponseBody
public ResponseEntity<Map<String,Object>> scoopInstall(HttpServletRequest request) {
String basePath = (request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath());
Map<String, Object> json = new HashMap<>();
String version = DateUtils.format(new Date(),"yyyyMMdd", Locale.getDefault());
json.put("version", version);
json.put("description", "JetBrains' dragon slayer");
json.put("homepage", "https://cikaros.top");
json.put("license", "MIT");
json.put("url", String.format("%s/ja-netfilter#dl.zip", basePath));
json.put("extract_to", Arrays.asList("", "config-jetbrains", "plugins-jetbrains", "scripts", "vmoptions"));
json.put("hash", DigestUtil.sha256Hex(AgentContextHolder.jaNetfilterZipFile()));
json.put("post_install", "cscript $dir/scripts/install-current-user.vbs");
json.put("pre_uninstall", "cscript $dir/scripts/uninstall-current-user.vbs");
return ResponseEntity.ok()
.contentType(APPLICATION_JSON)
.body(json);
}
@GetMapping("search")
public String index(@RequestParam(required = false) String search, Model model) {
List<ProductsContextHolder.ProductCache> productCacheList = ProductsContextHolder.productCacheList();
@ -70,4 +98,5 @@ public class IndexController {
.contentType(APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(FileUtil.getInputStream(jaNetfilterZipFile)));
}
}

View File

@ -3,12 +3,10 @@ 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;
import java.io.IOException;
public interface FileTools {
@ -19,6 +17,10 @@ public interface FileTools {
return getFile(path).exists();
}
static boolean fileNotExists(String path) {
return !fileExists(path);
}
static File getFile(String path) {
File homeDir = application.getDir();
File source = application.getSource();

View File

@ -6,6 +6,6 @@ spring:
server:
port: 10768
help:
default-license-name: 光云
default-assignee-name: 藏柏
default-expiry-date: 2111-11-11
default-license-name: cikaros.top
default-assignee-name: Cikaros
default-expiry-date: 2099-12-31

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFTDCCAzSgAwIBAgIJAMCrW9HV+hjZMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV
BAMMEkxpY2Vuc2UgU2VydmVycyBDQTAgFw0xNjEwMTIxNDMwNTRaGA8yMTE2MTIy
NzE0MzA1NFowHTEbMBkGA1UEAwwSTGljZW5zZSBTZXJ2ZXJzIENBMIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoT7LvHj3JKK2pgc5f02z+xEiJDcvlBi6
fIwrg/504UaMx3xWXAE5CEPelFty+QPRJnTNnSxqKQQmg2s/5tMJpL9lzGwXaV7a
rrcsEDbzV4el5mIXUnk77Bm/QVv48s63iQqUjVmvjQt9SWG2J7+h6X3ICRvF1sQB
yeat/cO7tkpz1aXXbvbAws7/3dXLTgAZTAmBXWNEZHVUTcwSg2IziYxL8HRFOH0+
GMBhHqa0ySmF1UTnTV4atIXrvjpABsoUvGxw+qOO2qnwe6ENEFWFz1a7pryVOHXg
P+4JyPkI1hdAhAqT2kOKbTHvlXDMUaxAPlriOVw+vaIjIVlNHpBGhqTj1aqfJpLj
qfDFcuqQSI4O1W5tVPRNFrjr74nDwLDZnOF+oSy4E1/WhL85FfP3IeQAIHdswNMJ
y+RdkPZCfXzSUhBKRtiM+yjpIn5RBY+8z+9yeGocoxPf7l0or3YF4GUpud202zgy
Y3sJqEsZksB750M0hx+vMMC9GD5nkzm9BykJS25hZOSsRNhX9InPWYYIi6mFm8QA
2Dnv8wxAwt2tDNgqa0v/N8OxHglPcK/VO9kXrUBtwCIfZigO//N3hqzfRNbTv/ZO
k9lArqGtcu1hSa78U4fuu7lIHi+u5rgXbB6HMVT3g5GQ1L9xxT1xad76k2EGEi3F
9B+tSrvru70CAwEAAaOBjDCBiTAdBgNVHQ4EFgQUpsRiEz+uvh6TsQqurtwXMd4J
8VEwTQYDVR0jBEYwRIAUpsRiEz+uvh6TsQqurtwXMd4J8VGhIaQfMB0xGzAZBgNV
BAMMEkxpY2Vuc2UgU2VydmVycyBDQYIJAMCrW9HV+hjZMAwGA1UdEwQFMAMBAf8w
CwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQCJ9+GQWvBS3zsgPB+1PCVc
oG6FY87N6nb3ZgNTHrUMNYdo7FDeol2DSB4wh/6rsP9Z4FqVlpGkckB+QHCvqU+d
rYPe6QWHIb1kE8ftTnwapj/ZaBtF80NWUfYBER/9c6To5moW63O7q6cmKgaGk6zv
St2IhwNdTX0Q5cib9ytE4XROeVwPUn6RdU/+AVqSOspSMc1WQxkPVGRF7HPCoGhd
vqebbYhpahiMWfClEuv1I37gJaRtsoNpx3f/jleoC/vDvXjAznfO497YTf/GgSM2
LCnVtpPQQ2vQbOfTjaBYO2MpibQlYpbkbjkd5ZcO5U5PGrQpPFrWcylz7eUC3c05
UVeygGIthsA/0hMCioYz4UjWTgi9NQLbhVkfmVQ5lCVxTotyBzoubh3FBz+wq2Qt
iElsBrCMR7UwmIu79UYzmLGt3/gBdHxaImrT9SQ8uqzP5eit54LlGbvGekVdAL5l
DFwPcSB1IKauXZvi1DwFGPeemcSAndy+Uoqw5XGRqE6jBxS7XVI7/4BSMDDRBz1u
a+JMGZXS8yyYT+7HdsybfsZLvkVmc9zVSDI7/MjVPdk6h0sLn+vuPC1bIi5edoNy
PdiG2uPH5eDO6INcisyPpLS4yFKliaO4Jjap7yzLU9pbItoWgCAYa2NpxuxHJ0tB
7tlDFnvaRnQukqSG+VqNWg==
-----END CERTIFICATE-----

View File

@ -13,7 +13,8 @@
<p>
🇨🇳 下载 <a href="ja-netfilter" title="Download jetbra first">ja-netfilter.zip</a> , 然后配置
你的JetBrains IDE(anything)'s <a onclick="showVmoptins()">IDE.vmoptions</a> 配置文件!<br>
🇨🇳 当然你也可以 <a onclick="showLicenseForm()">重新定制激活授权</a> 用以自定义你的激活信息!</br>
🇨🇳 当然你也可以 <a onclick="showLicenseForm()">重新定制激活授权</a> 用以自定义你的激活信息!<br>
🇨🇳 也可使用Scoop进行安装<code th:text="'scoop install ' + ${basePath}+'/scoop/ja-netfilter'"></code><br>
<strong>🇨🇳 请注意,此页面仅由 <span
th:text="${defaults.getDefaultLicenseName()} + '/' + ${defaults.getDefaultAssigneeName()}"></span>
个人所有!</strong>