Merge branch 'main' of git@github.com:Stirling-Tools/Stirling-PDF.git
into main
This commit is contained in:
@@ -17,9 +17,11 @@ public class EEAppConfig {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
@Bean(name = "RunningEE")
|
||||
@Autowired
|
||||
private LicenseKeyChecker licenseKeyChecker;
|
||||
|
||||
@Bean(name = "runningEE")
|
||||
public boolean runningEnterpriseEdition() {
|
||||
return applicationProperties.getEnterpriseEdition().getEnabled();
|
||||
// TODO: check EE license key
|
||||
return licenseKeyChecker.getEnterpriseEnabledResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,58 +14,58 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.posthog.java.shaded.org.json.JSONObject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class KeygenLicenseVerifier {
|
||||
private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
|
||||
private static final String PRODUCT_ID = "f9bb2423-62c9-4d39-8def-4fdc5aca751e";
|
||||
private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
|
||||
private static final String PUBLIC_KEY =
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzJaf7jPx/bamT/ctmvrf\n"
|
||||
+ "5HfzV9CrTx39Hv48NvRIjw9jBAlmcSndLbgcrTUWFrd7pJPPEhzmfJ9tLRg0a3Si\n"
|
||||
+ "34Ed9gQ24mODj0Wpos5uwwxu1M5wzsKPjkLZDigB3d9L/79nyKvSUo+mx+dZmZnD\n"
|
||||
+ "D19TMM93ZDxG+Bru5/rvvxaZzMHZAnqrTdoO55vFjpss5XJNt6kz4jxr+D6a3lFU\n"
|
||||
+ "GGCx7bjeanHCNGRw84dLYbU8s5DGsx5JNX1xPGR1kODocvsHfHJvsxfdNtpH4vke\n"
|
||||
+ "yOrtEUCp01Mh2kr3zM8R4Yjh4ae2qHiZne0FiVhiUaHmbf2dmcA9O1Kynz33634s\n"
|
||||
+ "fwIDAQAB\n"
|
||||
+ "-----END PUBLIC KEY-----";
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static boolean verifyLicense(String licenseKey) {
|
||||
public boolean verifyLicense(String licenseKey) {
|
||||
try {
|
||||
String machineFingerprint = generateMachineFingerprint();
|
||||
|
||||
// First, try to validate the license
|
||||
boolean isValid = validateLicense(licenseKey, machineFingerprint);
|
||||
|
||||
// If validation fails, try to activate the machine
|
||||
if (!isValid) {
|
||||
System.out.println(
|
||||
"License validation failed. Attempting to activate the machine...");
|
||||
isValid = activateMachine(licenseKey, machineFingerprint);
|
||||
|
||||
if (isValid) {
|
||||
// If activation is successful, try to validate again
|
||||
isValid = validateLicense(licenseKey, machineFingerprint);
|
||||
JsonNode validationResponse = validateLicense(licenseKey, machineFingerprint);
|
||||
log.debug(validationResponse.asText());
|
||||
if (validationResponse != null) {
|
||||
boolean isValid = validationResponse.path("meta").path("valid").asBoolean();
|
||||
String licenseId = validationResponse.path("data").path("id").asText();
|
||||
if (!isValid) {
|
||||
String code = validationResponse.path("meta").path("code").asText();
|
||||
log.debug(code);
|
||||
if ("NO_MACHINE".equals(code) || "NO_MACHINES".equals(code) || "FINGERPRINT_SCOPE_MISMATCH".equals(code)) {
|
||||
log.info("License not activated for this machine. Attempting to activate...");
|
||||
boolean activated = activateMachine(licenseKey, licenseId, machineFingerprint);
|
||||
if (activated) {
|
||||
// Revalidate after activation
|
||||
validationResponse = validateLicense(licenseKey, machineFingerprint);
|
||||
isValid = validationResponse != null && validationResponse.path("meta").path("valid").asBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error verifying license: " + e.getMessage());
|
||||
log.error("Error verifying license: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean validateLicense(String licenseKey, String machineFingerprint)
|
||||
private static JsonNode validateLicense(String licenseKey, String machineFingerprint)
|
||||
throws Exception {
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
String requestBody =
|
||||
String.format(
|
||||
"{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\",\"product\":\"%s\"}}}",
|
||||
licenseKey, machineFingerprint, PRODUCT_ID);
|
||||
|
||||
"{\"meta\":{\"key\":\"%s\",\"scope\":{\"fingerprint\":\"%s\"}}}",
|
||||
licenseKey, machineFingerprint );
|
||||
HttpRequest request =
|
||||
HttpRequest.newBuilder()
|
||||
.uri(
|
||||
@@ -76,107 +76,81 @@ public class KeygenLicenseVerifier {
|
||||
+ "/licenses/actions/validate-key"))
|
||||
.header("Content-Type", "application/vnd.api+json")
|
||||
.header("Accept", "application/vnd.api+json")
|
||||
.header("Authorization", "license " + licenseKey)
|
||||
//.header("Authorization", "License " + licenseKey)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
log.debug(" validateLicenseResponse body: " + response.body());
|
||||
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
||||
if (response.statusCode() == 200) {
|
||||
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
||||
|
||||
JsonNode metaNode = jsonResponse.path("meta");
|
||||
boolean isValid = metaNode.path("valid").asBoolean();
|
||||
|
||||
String detail = metaNode.path("detail").asText();
|
||||
String code = metaNode.path("code").asText();
|
||||
|
||||
System.out.println("License validity: " + isValid);
|
||||
System.out.println("Validation detail: " + detail);
|
||||
System.out.println("Validation code: " + code);
|
||||
log.debug("License validity: " + isValid);
|
||||
log.debug("Validation detail: " + detail);
|
||||
log.debug("Validation code: " + code);
|
||||
|
||||
if (isValid) {
|
||||
return verifySignature(metaNode);
|
||||
}
|
||||
|
||||
} else {
|
||||
System.out.println("Error validating license. Status code: " + response.statusCode());
|
||||
System.out.println("Response body: " + response.body());
|
||||
log.error("Error validating license. Status code: " + response.statusCode());
|
||||
}
|
||||
return false;
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
private static boolean activateMachine(String licenseKey, String machineFingerprint)
|
||||
private static boolean activateMachine(String licenseKey, String licenseId, String machineFingerprint)
|
||||
throws Exception {
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
String requestBody =
|
||||
String.format(
|
||||
"{\"data\":{\"type\":\"machines\",\"attributes\":{\"fingerprint\":\"%s\"},\"relationships\":{\"license\":{\"data\":{\"type\":\"licenses\",\"id\":\"%s\"}}}}}",
|
||||
machineFingerprint, licenseKey);
|
||||
|
||||
String licenseId = "8e072b67-3cea-454b-98bb-bb73bbc04bd4";
|
||||
HttpRequest request =
|
||||
HttpRequest.newBuilder()
|
||||
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines"))
|
||||
.header("Content-Type", "application/vnd.api+json")
|
||||
.header("Accept", "application/vnd.api+json")
|
||||
.header("Authorization", "license " + licenseId)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
||||
.build();
|
||||
String hostname;
|
||||
try {
|
||||
hostname = java.net.InetAddress.getLocalHost().getHostName();
|
||||
} catch (Exception e) {
|
||||
hostname = "Unknown";
|
||||
}
|
||||
|
||||
JSONObject body = new JSONObject()
|
||||
.put("data", new JSONObject()
|
||||
.put("type", "machines")
|
||||
.put("attributes", new JSONObject()
|
||||
.put("fingerprint", machineFingerprint)
|
||||
.put("platform", System.getProperty("os.name")) // Added platform parameter
|
||||
.put("name", hostname)) // Added name parameter
|
||||
.put("relationships", new JSONObject()
|
||||
.put("license", new JSONObject()
|
||||
.put("data", new JSONObject()
|
||||
.put("type", "licenses")
|
||||
.put("id", licenseId)))));
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines"))
|
||||
.header("Content-Type", "application/vnd.api+json")
|
||||
.header("Accept", "application/vnd.api+json")
|
||||
.header("Authorization", "License " + licenseKey) // Keep the license key authentication
|
||||
.POST(HttpRequest.BodyPublishers.ofString(body.toString())) // Send the JSON body
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
log.debug("activateMachine Response body: " + response.body());
|
||||
if (response.statusCode() == 201) {
|
||||
System.out.println("Machine activated successfully");
|
||||
log.info("Machine activated successfully");
|
||||
return true;
|
||||
} else {
|
||||
System.out.println("Error activating machine. Status code: " + response.statusCode());
|
||||
System.out.println("Response body: " + response.body());
|
||||
log.error("Error activating machine. Status code: " + response.statusCode());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean verifySignature(JsonNode metaNode) throws Exception {
|
||||
String signature = metaNode.path("signature").asText();
|
||||
String data = metaNode.path("data").asText();
|
||||
|
||||
PublicKey publicKey =
|
||||
KeyFactory.getInstance("RSA")
|
||||
.generatePublic(
|
||||
new X509EncodedKeySpec(
|
||||
Base64.getDecoder()
|
||||
.decode(
|
||||
PUBLIC_KEY
|
||||
.replace(
|
||||
"-----BEGIN PUBLIC KEY-----",
|
||||
"")
|
||||
.replace(
|
||||
"-----END PUBLIC KEY-----",
|
||||
"")
|
||||
.replaceAll("\\s", ""))));
|
||||
|
||||
Signature sig = Signature.getInstance("SHA256withRSA");
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(data.getBytes());
|
||||
|
||||
boolean isSignatureValid = sig.verify(Base64.getDecoder().decode(signature));
|
||||
System.out.println("Signature validity: " + isSignatureValid);
|
||||
return isSignatureValid;
|
||||
}
|
||||
|
||||
private static String generateMachineFingerprint() {
|
||||
// This is a simplified example. In a real-world scenario, you'd want to generate
|
||||
// a more robust and unique fingerprint based on hardware characteristics.
|
||||
return "example-fingerprint-" + System.currentTimeMillis();
|
||||
return "example-fingerprint";
|
||||
}
|
||||
|
||||
public static void test() {
|
||||
String[] testKeys = {
|
||||
"FYKJ-YK7F-MEVX-RYKK-JYWE-77WW-3TKN-PJRU", "EFDB57-92B4C2-EDFA20-51146E-E1AF4A-V3"
|
||||
};
|
||||
|
||||
for (String licenseKey : testKeys) {
|
||||
System.out.println("Testing license key: " + licenseKey);
|
||||
boolean isValid = verifyLicense(licenseKey);
|
||||
System.out.println("License is valid: " + isValid);
|
||||
System.out.println("--------------------");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package stirling.software.SPDF.EE;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Component
|
||||
public class LicenseKeyChecker implements CommandLineRunner {
|
||||
public class LicenseKeyChecker {
|
||||
|
||||
private final KeygenLicenseVerifier licenseService;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private static boolean enterpriseEnbaledResult = false;
|
||||
// Inject your license service or configuration
|
||||
public LicenseKeyChecker(
|
||||
KeygenLicenseVerifier licenseService, ApplicationProperties applicationProperties) {
|
||||
@@ -20,39 +24,28 @@ public class LicenseKeyChecker implements CommandLineRunner {
|
||||
this.applicationProperties = new ApplicationProperties();
|
||||
}
|
||||
|
||||
// Validate on startup
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
// Periodic license check - runs every 7 days
|
||||
@Scheduled(fixedRate = 604800000) // 7 days in milliseconds
|
||||
public void checkLicensePeriodically() {
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
// License validation logic
|
||||
private void checkLicense() {
|
||||
boolean isValid =
|
||||
licenseService.verifyLicense(applicationProperties.getEnterpriseEdition().getKey());
|
||||
if (!isValid) {
|
||||
// Handle invalid license (shut down the app, log, etc.)
|
||||
System.out.println("License key is invalid!");
|
||||
// Optionally stop the application
|
||||
// System.exit(1); // Uncomment if you want to stop the app
|
||||
} else {
|
||||
System.out.println("License key is valid.");
|
||||
}
|
||||
if(!applicationProperties.getEnterpriseEdition().isEnabled()) {
|
||||
enterpriseEnbaledResult = false;
|
||||
} else {
|
||||
enterpriseEnbaledResult = licenseService.verifyLicense(applicationProperties.getEnterpriseEdition().getKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Method to update the license key dynamically
|
||||
public void updateLicenseKey(String newKey) {
|
||||
// Update the key in ApplicationProperties
|
||||
public void updateLicenseKey(String newKey) throws IOException {
|
||||
applicationProperties.getEnterpriseEdition().setKey(newKey);
|
||||
|
||||
// Immediately validate the new key
|
||||
System.out.println("License key has been updated. Checking new key...");
|
||||
GeneralUtils.saveKeyToConfig("EnterpriseEdition.key", newKey, false);
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
public boolean getEnterpriseEnabledResult() {
|
||||
return enterpriseEnbaledResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.github.pixee.security.SystemCommand;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class LibreOfficeListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LibreOfficeListener.class);
|
||||
@@ -31,7 +33,7 @@ public class LibreOfficeListener {
|
||||
private LibreOfficeListener() {}
|
||||
|
||||
private boolean isListenerRunning() {
|
||||
System.out.println("waiting for listener to start");
|
||||
log.info("waiting for listener to start");
|
||||
try (Socket socket = new Socket()) {
|
||||
socket.connect(
|
||||
new InetSocketAddress("localhost", 2002), 1000); // Timeout after 1 second
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.thymeleaf.spring6.SpringTemplateEngine;
|
||||
|
||||
import stirling.software.SPDF.EE.LicenseKeyChecker;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
@@ -30,6 +31,7 @@ public class AppConfig {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
name = "system.customHTMLFiles",
|
||||
@@ -169,7 +171,7 @@ public class AppConfig {
|
||||
|
||||
@Bean(name = "analyticsEnabled")
|
||||
public boolean analyticsEnabled() {
|
||||
if (applicationProperties.getEnterpriseEdition().getEnabled()) return true;
|
||||
if (applicationProperties.getEnterpriseEdition().isEnabled()) return true;
|
||||
return applicationProperties.getSystem().getEnableAnalytics() != null
|
||||
&& Boolean.parseBoolean(applicationProperties.getSystem().getEnableAnalytics());
|
||||
}
|
||||
@@ -178,4 +180,11 @@ public class AppConfig {
|
||||
public String stirlingPDFLabel() {
|
||||
return "Stirling-PDF" + " v" + appVersion();
|
||||
}
|
||||
|
||||
@Bean(name = "UUID")
|
||||
public String uuid() {
|
||||
return applicationProperties.getAutomaticallyGenerated().getUUID();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.config.interfaces.ShowAdminInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
|
||||
public class InitialSetup {
|
||||
|
||||
@Autowired private ApplicationProperties applicationProperties;
|
||||
|
||||
@PostConstruct
|
||||
public void initUUIDKey() throws IOException {
|
||||
String uuid = applicationProperties.getAutomaticallyGenerated().getUUID();
|
||||
if (!GeneralUtils.isValidUUID(uuid)) {
|
||||
uuid = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
|
||||
GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.UUID", uuid);
|
||||
applicationProperties.getAutomaticallyGenerated().setUUID(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initSecretKey() throws IOException {
|
||||
String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
|
||||
if (!GeneralUtils.isValidUUID(secretKey)) {
|
||||
secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
|
||||
GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.key", secretKey);
|
||||
applicationProperties.getAutomaticallyGenerated().setKey(secretKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
public class Beans implements WebMvcConfigurer {
|
||||
public class LocaleConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.posthog.java.PostHog;
|
||||
|
||||
import jakarta.annotation.PreDestroy;
|
||||
|
||||
@Configuration
|
||||
public class PostHogConfig {
|
||||
|
||||
@Value("${posthog.api.key}")
|
||||
private String posthogApiKey;
|
||||
|
||||
@Value("${posthog.host}")
|
||||
private String posthogHost;
|
||||
|
||||
private PostHog postHogClient;
|
||||
|
||||
@Bean
|
||||
public PostHog postHogClient() {
|
||||
postHogClient = new PostHog.Builder(posthogApiKey).host(posthogHost).build();
|
||||
return postHogClient;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdownPostHog() {
|
||||
if (postHogClient != null) {
|
||||
postHogClient.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package stirling.software.SPDF.config.fingerprint;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class FingerprintBasedSessionFilter extends OncePerRequestFilter {
|
||||
private final FingerprintGenerator fingerprintGenerator;
|
||||
private final FingerprintBasedSessionManager sessionManager;
|
||||
|
||||
@Autowired
|
||||
public FingerprintBasedSessionFilter(
|
||||
FingerprintGenerator fingerprintGenerator,
|
||||
FingerprintBasedSessionManager sessionManager) {
|
||||
this.fingerprintGenerator = fingerprintGenerator;
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (RequestUriUtils.isStaticResource(request.getContextPath(), request.getRequestURI())) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
String fingerprint = fingerprintGenerator.generateFingerprint(request);
|
||||
log.debug("Generated fingerprint for request: {}", fingerprint);
|
||||
|
||||
HttpSession session = request.getSession();
|
||||
boolean isNewSession = session.isNew();
|
||||
String sessionId = session.getId();
|
||||
|
||||
if (isNewSession) {
|
||||
log.info("New session created: {}", sessionId);
|
||||
}
|
||||
|
||||
if (!sessionManager.isFingerPrintAllowed(fingerprint)) {
|
||||
log.info("Blocked fingerprint detected, redirecting: {}", fingerprint);
|
||||
response.sendRedirect(request.getContextPath() + "/too-many-requests");
|
||||
return;
|
||||
}
|
||||
|
||||
session.setAttribute("userFingerprint", fingerprint);
|
||||
session.setAttribute(FingerprintBasedSessionManager.STARTUP_TIMESTAMP, FingerprintBasedSessionManager.APP_STARTUP_TIME);
|
||||
|
||||
sessionManager.registerFingerprint(fingerprint, sessionId);
|
||||
|
||||
log.debug("Proceeding with request: {}", request.getRequestURI());
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package stirling.software.SPDF.config.fingerprint;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.servlet.http.HttpSessionAttributeListener;
|
||||
import jakarta.servlet.http.HttpSessionEvent;
|
||||
import jakarta.servlet.http.HttpSessionListener;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FingerprintBasedSessionManager implements HttpSessionListener, HttpSessionAttributeListener {
|
||||
private static final ConcurrentHashMap<String, FingerprintInfo> activeFingerprints = new ConcurrentHashMap<>();
|
||||
|
||||
// To be reduced in later version to 8~
|
||||
private static final int MAX_ACTIVE_FINGERPRINTS = 30;
|
||||
|
||||
static final String STARTUP_TIMESTAMP = "appStartupTimestamp";
|
||||
static final long APP_STARTUP_TIME = System.currentTimeMillis();
|
||||
private static final long FINGERPRINT_EXPIRATION = TimeUnit.MINUTES.toMillis(30);
|
||||
|
||||
@Override
|
||||
public void sessionCreated(HttpSessionEvent se) {
|
||||
HttpSession session = se.getSession();
|
||||
String sessionId = session.getId();
|
||||
String fingerprint = (String) session.getAttribute("userFingerprint");
|
||||
|
||||
if (fingerprint == null) {
|
||||
log.warn("Session created without fingerprint: {}", sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (activeFingerprints) {
|
||||
if (activeFingerprints.size() >= MAX_ACTIVE_FINGERPRINTS && !activeFingerprints.containsKey(fingerprint)) {
|
||||
log.info("Max fingerprints reached. Marking session as blocked: {}", sessionId);
|
||||
session.setAttribute("blocked", true);
|
||||
} else {
|
||||
activeFingerprints.put(fingerprint, new FingerprintInfo(sessionId, System.currentTimeMillis()));
|
||||
log.info(
|
||||
"New fingerprint registered: {}. Total active fingerprints: {}",
|
||||
fingerprint,
|
||||
activeFingerprints.size()
|
||||
);
|
||||
}
|
||||
session.setAttribute(STARTUP_TIMESTAMP, APP_STARTUP_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(HttpSessionEvent se) {
|
||||
HttpSession session = se.getSession();
|
||||
String fingerprint = (String) session.getAttribute("userFingerprint");
|
||||
|
||||
if (fingerprint != null) {
|
||||
synchronized (activeFingerprints) {
|
||||
activeFingerprints.remove(fingerprint);
|
||||
log.info(
|
||||
"Fingerprint removed: {}. Total active fingerprints: {}",
|
||||
fingerprint,
|
||||
activeFingerprints.size()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFingerPrintAllowed(String fingerprint) {
|
||||
synchronized (activeFingerprints) {
|
||||
return activeFingerprints.size() < MAX_ACTIVE_FINGERPRINTS || activeFingerprints.containsKey(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerFingerprint(String fingerprint, String sessionId) {
|
||||
synchronized (activeFingerprints) {
|
||||
activeFingerprints.put(fingerprint, new FingerprintInfo(sessionId, System.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterFingerprint(String fingerprint) {
|
||||
synchronized (activeFingerprints) {
|
||||
activeFingerprints.remove(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 1800000) // Run every 30 mins
|
||||
public void cleanupStaleFingerprints() {
|
||||
log.info("Starting cleanup of stale fingerprints");
|
||||
long now = System.currentTimeMillis();
|
||||
int removedCount = 0;
|
||||
|
||||
synchronized (activeFingerprints) {
|
||||
Iterator<Map.Entry<String, FingerprintInfo>> iterator = activeFingerprints.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, FingerprintInfo> entry = iterator.next();
|
||||
FingerprintInfo info = entry.getValue();
|
||||
|
||||
if (now - info.getLastAccessTime() > FINGERPRINT_EXPIRATION) {
|
||||
iterator.remove();
|
||||
removedCount++;
|
||||
log.info("Removed stale fingerprint: {}", entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Cleanup complete. Removed {} stale fingerprints", removedCount);
|
||||
}
|
||||
|
||||
public void updateLastAccessTime(String fingerprint) {
|
||||
FingerprintInfo info = activeFingerprints.get(fingerprint);
|
||||
if (info != null) {
|
||||
info.setLastAccessTime(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Data @AllArgsConstructor
|
||||
private static class FingerprintInfo {
|
||||
private String sessionId;
|
||||
private long lastAccessTime;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package stirling.software.SPDF.config.fingerprint;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@Component
|
||||
public class FingerprintGenerator {
|
||||
|
||||
public String generateFingerprint(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder fingerprintBuilder = new StringBuilder();
|
||||
|
||||
// Add IP address
|
||||
fingerprintBuilder.append(request.getRemoteAddr());
|
||||
|
||||
// Add X-Forwarded-For header if present (for clients behind proxies)
|
||||
String forwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (forwardedFor != null) {
|
||||
fingerprintBuilder.append(forwardedFor);
|
||||
}
|
||||
|
||||
// Add User-Agent
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
if (userAgent != null) {
|
||||
fingerprintBuilder.append(userAgent);
|
||||
}
|
||||
|
||||
// Add Accept-Language header
|
||||
String acceptLanguage = request.getHeader("Accept-Language");
|
||||
if (acceptLanguage != null) {
|
||||
fingerprintBuilder.append(acceptLanguage);
|
||||
}
|
||||
|
||||
// Add Accept header
|
||||
String accept = request.getHeader("Accept");
|
||||
if (accept != null) {
|
||||
fingerprintBuilder.append(accept);
|
||||
}
|
||||
|
||||
// Add Connection header
|
||||
String connection = request.getHeader("Connection");
|
||||
if (connection != null) {
|
||||
fingerprintBuilder.append(connection);
|
||||
}
|
||||
|
||||
// Add server port
|
||||
fingerprintBuilder.append(request.getServerPort());
|
||||
|
||||
// Add secure flag
|
||||
fingerprintBuilder.append(request.isSecure());
|
||||
|
||||
// Generate a hash of the fingerprint
|
||||
return generateHash(fingerprintBuilder.toString());
|
||||
}
|
||||
|
||||
private String generateHash(String input) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(input.getBytes());
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) hexString.append('0');
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Failed to generate fingerprint hash", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package stirling.software.SPDF.config;
|
||||
package stirling.software.SPDF.config.interfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -1,4 +1,4 @@
|
||||
package stirling.software.SPDF.config;
|
||||
package stirling.software.SPDF.config.interfaces;
|
||||
|
||||
public interface ShowAdminInterface {
|
||||
default boolean getShowUpdateOnlyAdmins() {
|
||||
@@ -7,7 +7,7 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.config.ShowAdminInterface;
|
||||
import stirling.software.SPDF.config.interfaces.ShowAdminInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.simpleyaml.configuration.file.YamlFile;
|
||||
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
||||
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
|
||||
@@ -39,15 +34,6 @@ public class InitialSecuritySetup {
|
||||
initializeInternalApiUser();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initSecretKey() throws IOException {
|
||||
String secretKey = applicationProperties.getAutomaticallyGenerated().getKey();
|
||||
if (!isValidUUID(secretKey)) {
|
||||
secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key
|
||||
saveKeyToConfig(secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeAdminUser() throws IOException {
|
||||
String initialUsername =
|
||||
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
||||
@@ -89,33 +75,4 @@ public class InitialSecuritySetup {
|
||||
log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveKeyToConfig(String key) throws IOException {
|
||||
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
||||
|
||||
final YamlFile settingsYml = new YamlFile(path.toFile());
|
||||
DumperOptions yamlOptionssettingsYml =
|
||||
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
|
||||
yamlOptionssettingsYml.setSplitLines(false);
|
||||
|
||||
settingsYml.loadWithComments();
|
||||
|
||||
settingsYml
|
||||
.path("AutomaticallyGenerated.key")
|
||||
.set(key)
|
||||
.comment("# Automatically Generated Settings (Do Not Edit Directly)");
|
||||
settingsYml.save();
|
||||
}
|
||||
|
||||
private boolean isValidUUID(String uuid) {
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
UUID.fromString(uuid);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +75,9 @@ public class SecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
if (loginEnabledValue) {
|
||||
|
||||
http.addFilterBefore(
|
||||
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
http.csrf(csrf -> csrf.disable());
|
||||
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
@@ -86,7 +85,7 @@ public class SecurityConfiguration {
|
||||
sessionManagement ->
|
||||
sessionManagement
|
||||
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
||||
.maximumSessions(10)
|
||||
.maximumSessions(4)
|
||||
.maxSessionsPreventsLogin(false)
|
||||
.sessionRegistry(sessionRegistry)
|
||||
.expiredUrl("/login?logout=true"));
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.config.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||
import stirling.software.SPDF.model.AuthenticationType;
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.utils.FileInfo;
|
||||
|
||||
@Slf4j
|
||||
|
||||
@@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.api.general.CropPdfForm;
|
||||
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
||||
import stirling.software.SPDF.service.PostHogService;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -36,9 +37,13 @@ public class CropController {
|
||||
|
||||
private final CustomPDDocumentFactory pdfDocumentFactory;
|
||||
|
||||
private final PostHogService postHogService;
|
||||
|
||||
@Autowired
|
||||
public CropController(CustomPDDocumentFactory pdfDocumentFactory) {
|
||||
public CropController(
|
||||
CustomPDDocumentFactory pdfDocumentFactory, PostHogService postHogService) {
|
||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
||||
this.postHogService = postHogService;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/crop", consumes = "multipart/form-data")
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Controller
|
||||
@Tag(name = "Settings", description = "Settings APIs")
|
||||
@RequestMapping("/api/v1/settings")
|
||||
@Hidden
|
||||
public class SettingsController {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
@PostMapping("/update-enable-analytics")
|
||||
@Hidden
|
||||
public ResponseEntity<String> updateApiKey(@RequestBody Boolean enabled) throws IOException {
|
||||
if (!"undefined".equals(applicationProperties.getSystem().getEnableAnalytics())) {
|
||||
return ResponseEntity.status(HttpStatus.ALREADY_REPORTED)
|
||||
.body(
|
||||
"Setting has already been set, To adjust please edit /config/settings.yml");
|
||||
}
|
||||
GeneralUtils.saveKeyToConfig("system.enableAnalytics", String.valueOf(enabled), false);
|
||||
applicationProperties.getSystem().setEnableAnalytics(String.valueOf(enabled));
|
||||
return ResponseEntity.ok("Updated");
|
||||
}
|
||||
}
|
||||
@@ -60,8 +60,6 @@ public class SplitPDFController {
|
||||
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
|
||||
int totalPages = document.getNumberOfPages();
|
||||
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||
System.out.println(
|
||||
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
if (!pageNumbers.contains(totalPages - 1)) {
|
||||
// Create a mutable ArrayList so we can add to it
|
||||
pageNumbers = new ArrayList<>(pageNumbers);
|
||||
|
||||
@@ -32,9 +32,9 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import stirling.software.SPDF.config.PdfMetadataService;
|
||||
import stirling.software.SPDF.model.PdfMetadata;
|
||||
import stirling.software.SPDF.model.api.SplitPdfByChaptersRequest;
|
||||
import stirling.software.SPDF.service.PdfMetadataService;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
|
||||
@@ -30,6 +30,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
import stirling.software.SPDF.model.AuthenticationType;
|
||||
@@ -40,6 +41,7 @@ import stirling.software.SPDF.model.api.user.UsernameAndPass;
|
||||
@Controller
|
||||
@Tag(name = "User", description = "User APIs")
|
||||
@RequestMapping("/api/v1/user")
|
||||
@Slf4j
|
||||
public class UserController {
|
||||
|
||||
@Autowired private UserService userService;
|
||||
@@ -191,13 +193,12 @@ public class UserController {
|
||||
Map<String, String[]> paramMap = request.getParameterMap();
|
||||
Map<String, String> updates = new HashMap<>();
|
||||
|
||||
System.out.println("Received parameter map: " + paramMap);
|
||||
|
||||
|
||||
for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
|
||||
updates.put(entry.getKey(), entry.getValue()[0]);
|
||||
}
|
||||
|
||||
System.out.println("Processed updates: " + updates);
|
||||
log.debug("Processed updates: " + updates);
|
||||
|
||||
// Assuming you have a method in userService to update the settings for a user
|
||||
userService.updateUserSettings(principal.getName(), updates);
|
||||
|
||||
@@ -60,8 +60,6 @@ public class ExtractImagesController {
|
||||
MultipartFile file = request.getFileInput();
|
||||
String format = request.getFormat();
|
||||
boolean allowDuplicates = request.isAllowDuplicates();
|
||||
System.out.println(
|
||||
System.currentTimeMillis() + " file=" + file.getName() + ", format=" + format);
|
||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||
|
||||
// Determine if multithreading should be used based on PDF size or number of pages
|
||||
|
||||
@@ -25,12 +25,13 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.misc.PrintFileRequest;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/misc")
|
||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||
@Slf4j
|
||||
public class PrintFileController {
|
||||
|
||||
// TODO
|
||||
@@ -59,7 +60,7 @@ public class PrintFileController {
|
||||
new IllegalArgumentException(
|
||||
"No matching printer found"));
|
||||
|
||||
System.out.println("Selected Printer: " + selectedService.getName());
|
||||
log.info("Selected Printer: " + selectedService.getName());
|
||||
|
||||
if ("application/pdf".equals(contentType)) {
|
||||
PDDocument document = Loader.loadPDF(file.getBytes());
|
||||
|
||||
@@ -58,7 +58,6 @@ public class RedactController {
|
||||
float customPadding = request.getCustomPadding();
|
||||
boolean convertPDFToImage = request.isConvertPDFToImage();
|
||||
|
||||
System.out.println(listOfTextString);
|
||||
String[] listOfText = listOfTextString.split("\n");
|
||||
PDDocument document = pdfDocumentFactory.load(file);
|
||||
|
||||
@@ -75,7 +74,6 @@ public class RedactController {
|
||||
|
||||
for (String text : listOfText) {
|
||||
text = text.trim();
|
||||
System.out.println(text);
|
||||
TextFinder textFinder = new TextFinder(text, useRegex, wholeWordSearchBool);
|
||||
List<PDFText> foundTexts = textFinder.getTextLocations(document);
|
||||
redactFoundText(document, foundTexts, customPadding, redactColor);
|
||||
|
||||
@@ -11,6 +11,8 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
@@ -24,6 +26,7 @@ import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
@ConfigurationProperties(prefix = "")
|
||||
@PropertySource(value = "file:./configs/settings.yml", factory = YamlPropertySourceFactory.class)
|
||||
@Data
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class ApplicationProperties {
|
||||
|
||||
private Legal legal = new Legal();
|
||||
@@ -176,11 +179,12 @@ public class ApplicationProperties {
|
||||
@Data
|
||||
public static class AutomaticallyGenerated {
|
||||
@ToString.Exclude private String key;
|
||||
private String UUID;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class EnterpriseEdition {
|
||||
private Boolean enabled;
|
||||
private boolean enabled;
|
||||
@ToString.Exclude private String key;
|
||||
private CustomMetadata customMetadata = new CustomMetadata();
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.apache.pdfbox.text.TextPosition;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.PDFText;
|
||||
|
||||
@Slf4j
|
||||
public class TextFinder extends PDFTextStripper {
|
||||
|
||||
private final String searchText;
|
||||
@@ -92,7 +94,7 @@ public class TextFinder extends PDFTextStripper {
|
||||
|
||||
public List<PDFText> getTextLocations(PDDocument document) throws Exception {
|
||||
this.getText(document);
|
||||
System.out.println(
|
||||
log.debug(
|
||||
"Found "
|
||||
+ textOccurrences.size()
|
||||
+ " occurrences of '"
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.SPDF.config.PdfMetadataService;
|
||||
import stirling.software.SPDF.model.PdfMetadata;
|
||||
import stirling.software.SPDF.model.api.PDFFile;
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.search.Search;
|
||||
|
||||
@Service
|
||||
public class MetricsAggregatorService {
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final PostHogService postHogService;
|
||||
private final Map<String, Double> lastSentMetrics = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
public MetricsAggregatorService(MeterRegistry meterRegistry, PostHogService postHogService) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
this.postHogService = postHogService;
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 900000) // Run every 15 minutes
|
||||
public void aggregateAndSendMetrics() {
|
||||
Map<String, Object> metrics = new HashMap<>();
|
||||
Search.in(meterRegistry)
|
||||
.name("http.requests")
|
||||
.counters()
|
||||
.forEach(counter -> {
|
||||
String key = String.format(
|
||||
"http_requests_%s_%s",
|
||||
counter.getId().getTag("method"),
|
||||
counter.getId().getTag("uri").replace("/", "_"));
|
||||
|
||||
double currentCount = counter.count();
|
||||
double lastCount = lastSentMetrics.getOrDefault(key, 0.0);
|
||||
double difference = currentCount - lastCount;
|
||||
|
||||
if (difference > 0) {
|
||||
metrics.put(key, difference);
|
||||
lastSentMetrics.put(key, currentCount);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Send aggregated metrics to PostHog
|
||||
if (!metrics.isEmpty()) {
|
||||
postHogService.captureEvent("aggregated_metrics", metrics);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package stirling.software.SPDF.config;
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
374
src/main/java/stirling/software/SPDF/service/PostHogService.java
Normal file
374
src/main/java/stirling/software/SPDF/service/PostHogService.java
Normal file
@@ -0,0 +1,374 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.posthog.java.PostHog;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
public class PostHogService {
|
||||
private final PostHog postHog;
|
||||
private final String uniqueId;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired
|
||||
public PostHogService(
|
||||
PostHog postHog,
|
||||
@Qualifier("UUID") String uuid,
|
||||
ApplicationProperties applicationProperties) {
|
||||
this.postHog = postHog;
|
||||
this.uniqueId = uuid;
|
||||
this.applicationProperties = applicationProperties;
|
||||
captureSystemInfo();
|
||||
}
|
||||
|
||||
private void captureSystemInfo() {
|
||||
try {
|
||||
postHog.capture(uniqueId, "system_info_captured", captureServerMetrics());
|
||||
|
||||
} catch (Exception e) {
|
||||
// Handle exceptions
|
||||
}
|
||||
}
|
||||
|
||||
public void captureEvent(String eventName, Map<String, Object> properties) {
|
||||
postHog.capture(uniqueId, eventName, properties);
|
||||
}
|
||||
|
||||
public Map<String, Object> captureServerMetrics() {
|
||||
Map<String, Object> metrics = new HashMap<>();
|
||||
|
||||
try {
|
||||
// System info
|
||||
metrics.put("os_name", System.getProperty("os.name"));
|
||||
metrics.put("os_version", System.getProperty("os.version"));
|
||||
metrics.put("java_version", System.getProperty("java.version"));
|
||||
metrics.put("user_name", System.getProperty("user.name"));
|
||||
metrics.put("user_home", System.getProperty("user.home"));
|
||||
metrics.put("user_dir", System.getProperty("user.dir"));
|
||||
|
||||
// CPU and Memory
|
||||
metrics.put("cpu_cores", Runtime.getRuntime().availableProcessors());
|
||||
metrics.put("total_memory", Runtime.getRuntime().totalMemory());
|
||||
metrics.put("free_memory", Runtime.getRuntime().freeMemory());
|
||||
|
||||
// Network and Server Identity
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
metrics.put("ip_address", localHost.getHostAddress());
|
||||
metrics.put("hostname", localHost.getHostName());
|
||||
metrics.put("mac_address", getMacAddress());
|
||||
|
||||
// JVM info
|
||||
metrics.put("jvm_vendor", System.getProperty("java.vendor"));
|
||||
metrics.put("jvm_version", System.getProperty("java.vm.version"));
|
||||
|
||||
// Locale and Timezone
|
||||
metrics.put("system_language", System.getProperty("user.language"));
|
||||
metrics.put("system_country", System.getProperty("user.country"));
|
||||
metrics.put("timezone", TimeZone.getDefault().getID());
|
||||
metrics.put("locale", Locale.getDefault().toString());
|
||||
|
||||
// Disk info
|
||||
File root = new File(".");
|
||||
metrics.put("total_disk_space", root.getTotalSpace());
|
||||
metrics.put("free_disk_space", root.getFreeSpace());
|
||||
|
||||
// Process info
|
||||
metrics.put("process_id", ProcessHandle.current().pid());
|
||||
|
||||
// JVM metrics
|
||||
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
|
||||
metrics.put("jvm_uptime_ms", runtimeMXBean.getUptime());
|
||||
metrics.put("jvm_start_time", runtimeMXBean.getStartTime());
|
||||
|
||||
// Memory metrics
|
||||
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
|
||||
metrics.put("heap_memory_usage", memoryMXBean.getHeapMemoryUsage().getUsed());
|
||||
metrics.put("non_heap_memory_usage", memoryMXBean.getNonHeapMemoryUsage().getUsed());
|
||||
|
||||
// CPU metrics
|
||||
OperatingSystemMXBean osMXBean = ManagementFactory.getOperatingSystemMXBean();
|
||||
metrics.put("system_load_average", osMXBean.getSystemLoadAverage());
|
||||
|
||||
// Thread metrics
|
||||
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
metrics.put("thread_count", threadMXBean.getThreadCount());
|
||||
metrics.put("daemon_thread_count", threadMXBean.getDaemonThreadCount());
|
||||
metrics.put("peak_thread_count", threadMXBean.getPeakThreadCount());
|
||||
|
||||
// Garbage collection metrics
|
||||
for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
|
||||
metrics.put("gc_" + gcBean.getName() + "_count", gcBean.getCollectionCount());
|
||||
metrics.put("gc_" + gcBean.getName() + "_time", gcBean.getCollectionTime());
|
||||
}
|
||||
|
||||
// Network interfaces
|
||||
metrics.put("network_interfaces", getNetworkInterfacesInfo());
|
||||
|
||||
// Docker detection and stats
|
||||
boolean isDocker = isRunningInDocker();
|
||||
metrics.put("is_docker", isDocker);
|
||||
if (isDocker) {
|
||||
metrics.put("docker_metrics", getDockerMetrics());
|
||||
}
|
||||
metrics.put("application_properties", captureApplicationProperties());
|
||||
|
||||
} catch (Exception e) {
|
||||
metrics.put("error", e.getMessage());
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private boolean isRunningInDocker() {
|
||||
return Files.exists(Paths.get("/.dockerenv"));
|
||||
}
|
||||
|
||||
private Map<String, Object> getDockerMetrics() {
|
||||
Map<String, Object> dockerMetrics = new HashMap<>();
|
||||
|
||||
// Network-related Docker info
|
||||
dockerMetrics.put("docker_network_mode", System.getenv("DOCKER_NETWORK_MODE"));
|
||||
|
||||
// Container name (if set)
|
||||
String containerName = System.getenv("CONTAINER_NAME");
|
||||
if (containerName != null && !containerName.isEmpty()) {
|
||||
dockerMetrics.put("container_name", containerName);
|
||||
}
|
||||
|
||||
// Docker compose information
|
||||
String composeProject = System.getenv("COMPOSE_PROJECT_NAME");
|
||||
String composeService = System.getenv("COMPOSE_SERVICE_NAME");
|
||||
if (composeProject != null && composeService != null) {
|
||||
dockerMetrics.put("compose_project", composeProject);
|
||||
dockerMetrics.put("compose_service", composeService);
|
||||
}
|
||||
|
||||
// Kubernetes-specific info (if running in K8s)
|
||||
String k8sPodName = System.getenv("KUBERNETES_POD_NAME");
|
||||
if (k8sPodName != null) {
|
||||
dockerMetrics.put("k8s_pod_name", k8sPodName);
|
||||
dockerMetrics.put("k8s_namespace", System.getenv("KUBERNETES_NAMESPACE"));
|
||||
dockerMetrics.put("k8s_node_name", System.getenv("KUBERNETES_NODE_NAME"));
|
||||
}
|
||||
|
||||
// New environment variables
|
||||
dockerMetrics.put("version_tag", System.getenv("VERSION_TAG"));
|
||||
dockerMetrics.put("docker_enable_security", System.getenv("DOCKER_ENABLE_SECURITY"));
|
||||
dockerMetrics.put("fat_docker", System.getenv("FAT_DOCKER"));
|
||||
|
||||
return dockerMetrics;
|
||||
}
|
||||
|
||||
private void addIfNotEmpty(Map<String, Object> map, String key, Object value) {
|
||||
if (value != null) {
|
||||
if (value instanceof String) {
|
||||
String strValue = (String) value;
|
||||
if (!StringUtils.isBlank(strValue)) {
|
||||
map.put(key, strValue.trim());
|
||||
}
|
||||
} else {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> captureApplicationProperties() {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
// Capture Legal properties
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"legal_termsAndConditions",
|
||||
applicationProperties.getLegal().getTermsAndConditions());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"legal_privacyPolicy",
|
||||
applicationProperties.getLegal().getPrivacyPolicy());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"legal_accessibilityStatement",
|
||||
applicationProperties.getLegal().getAccessibilityStatement());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"legal_cookiePolicy",
|
||||
applicationProperties.getLegal().getCookiePolicy());
|
||||
addIfNotEmpty(
|
||||
properties, "legal_impressum", applicationProperties.getLegal().getImpressum());
|
||||
|
||||
// Capture Security properties
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_enableLogin",
|
||||
applicationProperties.getSecurity().getEnableLogin());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_csrfDisabled",
|
||||
applicationProperties.getSecurity().getCsrfDisabled());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_loginAttemptCount",
|
||||
applicationProperties.getSecurity().getLoginAttemptCount());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_loginResetTimeMinutes",
|
||||
applicationProperties.getSecurity().getLoginResetTimeMinutes());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_loginMethod",
|
||||
applicationProperties.getSecurity().getLoginMethod());
|
||||
|
||||
// Capture OAuth2 properties (excluding sensitive information)
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_oauth2_enabled",
|
||||
applicationProperties.getSecurity().getOauth2().getEnabled());
|
||||
if (applicationProperties.getSecurity().getOauth2().getEnabled()) {
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_oauth2_autoCreateUser",
|
||||
applicationProperties.getSecurity().getOauth2().getAutoCreateUser());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_oauth2_blockRegistration",
|
||||
applicationProperties.getSecurity().getOauth2().getBlockRegistration());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_oauth2_useAsUsername",
|
||||
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"security_oauth2_provider",
|
||||
applicationProperties.getSecurity().getOauth2().getProvider());
|
||||
}
|
||||
// Capture System properties
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"system_defaultLocale",
|
||||
applicationProperties.getSystem().getDefaultLocale());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"system_googlevisibility",
|
||||
applicationProperties.getSystem().getGooglevisibility());
|
||||
addIfNotEmpty(
|
||||
properties, "system_showUpdate", applicationProperties.getSystem().isShowUpdate());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"system_showUpdateOnlyAdmin",
|
||||
applicationProperties.getSystem().getShowUpdateOnlyAdmin());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"system_customHTMLFiles",
|
||||
applicationProperties.getSystem().isCustomHTMLFiles());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"system_tessdataDir",
|
||||
applicationProperties.getSystem().getTessdataDir());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"system_enableAlphaFunctionality",
|
||||
applicationProperties.getSystem().getEnableAlphaFunctionality());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"system_enableAnalytics",
|
||||
applicationProperties.getSystem().getEnableAnalytics());
|
||||
|
||||
// Capture UI properties
|
||||
addIfNotEmpty(properties, "ui_appName", applicationProperties.getUi().getAppName());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"ui_homeDescription",
|
||||
applicationProperties.getUi().getHomeDescription());
|
||||
addIfNotEmpty(
|
||||
properties, "ui_appNameNavbar", applicationProperties.getUi().getAppNameNavbar());
|
||||
|
||||
// Capture Metrics properties
|
||||
addIfNotEmpty(
|
||||
properties, "metrics_enabled", applicationProperties.getMetrics().getEnabled());
|
||||
|
||||
// Capture EnterpriseEdition properties
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"enterpriseEdition_enabled",
|
||||
applicationProperties.getEnterpriseEdition().isEnabled());
|
||||
if (applicationProperties.getEnterpriseEdition().isEnabled()) {
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"enterpriseEdition_customMetadata_autoUpdateMetadata",
|
||||
applicationProperties
|
||||
.getEnterpriseEdition()
|
||||
.getCustomMetadata()
|
||||
.isAutoUpdateMetadata());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"enterpriseEdition_customMetadata_author",
|
||||
applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"enterpriseEdition_customMetadata_creator",
|
||||
applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator());
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"enterpriseEdition_customMetadata_producer",
|
||||
applicationProperties.getEnterpriseEdition().getCustomMetadata().getProducer());
|
||||
}
|
||||
// Capture AutoPipeline properties
|
||||
addIfNotEmpty(
|
||||
properties,
|
||||
"autoPipeline_outputFolder",
|
||||
applicationProperties.getAutoPipeline().getOutputFolder());
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private String getMacAddress() {
|
||||
try {
|
||||
Enumeration<NetworkInterface> networkInterfaces =
|
||||
NetworkInterface.getNetworkInterfaces();
|
||||
while (networkInterfaces.hasMoreElements()) {
|
||||
NetworkInterface ni = networkInterfaces.nextElement();
|
||||
byte[] hardwareAddress = ni.getHardwareAddress();
|
||||
if (hardwareAddress != null) {
|
||||
String[] hexadecimal = new String[hardwareAddress.length];
|
||||
for (int i = 0; i < hardwareAddress.length; i++) {
|
||||
hexadecimal[i] = String.format("%02X", hardwareAddress[i]);
|
||||
}
|
||||
return String.join("-", hexadecimal);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Handle exception
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private Map<String, String> getNetworkInterfacesInfo() {
|
||||
Map<String, String> interfacesInfo = new HashMap<>();
|
||||
try {
|
||||
Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
|
||||
while (nets.hasMoreElements()) {
|
||||
NetworkInterface netint = nets.nextElement();
|
||||
interfacesInfo.put(netint.getName(), netint.getDisplayName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
interfacesInfo.put("error", e.getMessage());
|
||||
}
|
||||
return interfacesInfo;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,12 @@ import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.simpleyaml.configuration.file.YamlFile;
|
||||
import org.simpleyaml.configuration.file.YamlFileWrapper;
|
||||
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
||||
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -262,4 +267,38 @@ public class GeneralUtils {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isValidUUID(String uuid) {
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
UUID.fromString(uuid);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveKeyToConfig(String id, String key) throws IOException {
|
||||
saveKeyToConfig(id, key, true);
|
||||
}
|
||||
|
||||
public static void saveKeyToConfig(String id, String key, boolean autoGenerated)
|
||||
throws IOException {
|
||||
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
||||
|
||||
final YamlFile settingsYml = new YamlFile(path.toFile());
|
||||
DumperOptions yamlOptionssettingsYml =
|
||||
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
|
||||
yamlOptionssettingsYml.setSplitLines(false);
|
||||
|
||||
settingsYml.loadWithComments();
|
||||
|
||||
YamlFileWrapper writer = settingsYml.path(id).set(key);
|
||||
if (autoGenerated) {
|
||||
writer.comment("# Automatically Generated Settings (Do Not Edit Directly)");
|
||||
}
|
||||
settingsYml.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,6 @@ public class PDFToFile {
|
||||
Files.deleteIfExists(tempInputFile);
|
||||
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
|
||||
}
|
||||
System.out.println("fileBytes=" + fileBytes.length);
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user