2023-08-26 17:30:49 +01:00
|
|
|
package stirling.software.SPDF.model;
|
|
|
|
|
2025-02-24 22:18:34 +00:00
|
|
|
import static stirling.software.SPDF.utils.validation.Validator.*;
|
|
|
|
|
2025-01-06 12:41:30 +00:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileNotFoundException;
|
2024-10-20 13:30:58 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.net.HttpURLConnection;
|
|
|
|
import java.net.URI;
|
|
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.net.URL;
|
2024-05-12 19:58:34 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Collection;
|
2023-08-27 00:39:22 +01:00
|
|
|
import java.util.List;
|
|
|
|
|
2023-08-26 17:30:49 +01:00
|
|
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
2025-01-06 12:41:30 +00:00
|
|
|
import org.springframework.context.annotation.Bean;
|
2023-08-26 17:30:49 +01:00
|
|
|
import org.springframework.context.annotation.Configuration;
|
2024-10-14 22:34:41 +01:00
|
|
|
import org.springframework.core.Ordered;
|
|
|
|
import org.springframework.core.annotation.Order;
|
2025-01-06 12:41:30 +00:00
|
|
|
import org.springframework.core.env.ConfigurableEnvironment;
|
|
|
|
import org.springframework.core.env.PropertySource;
|
2024-10-14 22:34:41 +01:00
|
|
|
import org.springframework.core.io.ClassPathResource;
|
|
|
|
import org.springframework.core.io.FileSystemResource;
|
|
|
|
import org.springframework.core.io.Resource;
|
2025-01-06 12:41:30 +00:00
|
|
|
import org.springframework.core.io.support.EncodedResource;
|
2023-08-26 17:30:49 +01:00
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
import lombok.Data;
|
2024-10-20 13:30:58 +02:00
|
|
|
import lombok.Getter;
|
|
|
|
import lombok.Setter;
|
2024-09-13 16:42:38 +01:00
|
|
|
import lombok.ToString;
|
2025-01-06 12:41:30 +00:00
|
|
|
import lombok.extern.slf4j.Slf4j;
|
2025-02-23 13:36:21 +00:00
|
|
|
|
2025-01-06 12:41:30 +00:00
|
|
|
import stirling.software.SPDF.config.InstallationPathConfig;
|
2023-08-26 17:30:49 +01:00
|
|
|
import stirling.software.SPDF.config.YamlPropertySourceFactory;
|
2025-02-24 22:18:34 +00:00
|
|
|
import stirling.software.SPDF.model.exception.UnsupportedProviderException;
|
|
|
|
import stirling.software.SPDF.model.provider.GitHubProvider;
|
2024-06-15 14:15:09 +02:00
|
|
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
|
|
|
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
2025-02-24 22:18:34 +00:00
|
|
|
import stirling.software.SPDF.model.provider.Provider;
|
2023-08-26 17:30:49 +01:00
|
|
|
|
|
|
|
@Configuration
|
|
|
|
@ConfigurationProperties(prefix = "")
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2024-10-14 22:34:41 +01:00
|
|
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
2025-01-06 12:41:30 +00:00
|
|
|
@Slf4j
|
2023-08-26 17:30:49 +01:00
|
|
|
public class ApplicationProperties {
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2025-01-06 12:41:30 +00:00
|
|
|
@Bean
|
|
|
|
public PropertySource<?> dynamicYamlPropertySource(ConfigurableEnvironment environment)
|
|
|
|
throws IOException {
|
|
|
|
String configPath = InstallationPathConfig.getSettingsPath();
|
|
|
|
log.debug("Attempting to load settings from: " + configPath);
|
|
|
|
|
|
|
|
File file = new File(configPath);
|
|
|
|
if (!file.exists()) {
|
|
|
|
log.error("Warning: Settings file does not exist at: " + configPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
Resource resource = new FileSystemResource(configPath);
|
|
|
|
if (!resource.exists()) {
|
|
|
|
throw new FileNotFoundException("Settings file not found at: " + configPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
EncodedResource encodedResource = new EncodedResource(resource);
|
|
|
|
PropertySource<?> propertySource =
|
|
|
|
new YamlPropertySourceFactory().createPropertySource(null, encodedResource);
|
|
|
|
environment.getPropertySources().addFirst(propertySource);
|
|
|
|
|
|
|
|
log.debug("Loaded properties: " + propertySource.getSource());
|
|
|
|
|
|
|
|
return propertySource;
|
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
private Legal legal = new Legal();
|
|
|
|
private Security security = new Security();
|
|
|
|
private System system = new System();
|
|
|
|
private Ui ui = new Ui();
|
|
|
|
private Endpoints endpoints = new Endpoints();
|
|
|
|
private Metrics metrics = new Metrics();
|
|
|
|
private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
|
2025-03-25 17:57:17 +00:00
|
|
|
|
|
|
|
private Premium premium = new Premium();
|
2024-09-13 16:42:38 +01:00
|
|
|
private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
|
|
|
|
private AutoPipeline autoPipeline = new AutoPipeline();
|
2024-11-06 17:43:57 -07:00
|
|
|
private ProcessExecutor processExecutor = new ProcessExecutor();
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-08-26 22:33:23 +01:00
|
|
|
public static class AutoPipeline {
|
|
|
|
private String outputFolder;
|
2024-09-13 16:42:38 +01:00
|
|
|
}
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
|
|
|
public static class Legal {
|
|
|
|
private String termsAndConditions;
|
|
|
|
private String privacyPolicy;
|
|
|
|
private String accessibilityStatement;
|
|
|
|
private String cookiePolicy;
|
|
|
|
private String impressum;
|
2023-12-30 19:11:27 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-08-26 17:30:49 +01:00
|
|
|
public static class Security {
|
|
|
|
private Boolean enableLogin;
|
|
|
|
private Boolean csrfDisabled;
|
2024-09-13 16:42:38 +01:00
|
|
|
private InitialLogin initialLogin = new InitialLogin();
|
|
|
|
private OAUTH2 oauth2 = new OAUTH2();
|
2024-10-20 13:30:58 +02:00
|
|
|
private SAML2 saml2 = new SAML2();
|
2023-12-29 20:48:21 +00:00
|
|
|
private int loginAttemptCount;
|
|
|
|
private long loginResetTimeMinutes;
|
2024-06-15 14:15:09 +02:00
|
|
|
private String loginMethod = "all";
|
2024-12-10 20:39:24 +00:00
|
|
|
private String customGlobalAPIKey;
|
2024-06-15 14:15:09 +02:00
|
|
|
|
2024-10-20 13:30:58 +02:00
|
|
|
public Boolean isAltLogin() {
|
|
|
|
return saml2.getEnabled() || oauth2.getEnabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum LoginMethods {
|
|
|
|
ALL("all"),
|
|
|
|
NORMAL("normal"),
|
|
|
|
OAUTH2("oauth2"),
|
|
|
|
SAML2("saml2");
|
|
|
|
|
|
|
|
private String method;
|
|
|
|
|
|
|
|
LoginMethods(String method) {
|
|
|
|
this.method = method;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-06 18:58:26 +00:00
|
|
|
public boolean isUserPass() {
|
|
|
|
return (loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString())
|
|
|
|
|| loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString()));
|
|
|
|
}
|
|
|
|
|
2025-02-24 22:18:34 +00:00
|
|
|
public boolean isOauth2Active() {
|
2025-01-06 18:58:26 +00:00
|
|
|
return (oauth2 != null
|
|
|
|
&& oauth2.getEnabled()
|
|
|
|
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
|
|
|
}
|
|
|
|
|
2025-02-24 22:18:34 +00:00
|
|
|
public boolean isSaml2Active() {
|
2025-01-06 18:58:26 +00:00
|
|
|
return (saml2 != null
|
|
|
|
&& saml2.getEnabled()
|
|
|
|
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-09-29 23:58:37 +01:00
|
|
|
public static class InitialLogin {
|
|
|
|
private String username;
|
2024-09-13 16:42:38 +01:00
|
|
|
@ToString.Exclude private String password;
|
2023-12-30 19:11:27 +00:00
|
|
|
}
|
2024-04-29 15:01:22 -06:00
|
|
|
|
2024-10-20 13:30:58 +02:00
|
|
|
@Getter
|
|
|
|
@Setter
|
2024-12-18 13:40:24 +01:00
|
|
|
@ToString
|
2024-10-20 13:30:58 +02:00
|
|
|
public static class SAML2 {
|
2025-02-24 22:18:34 +00:00
|
|
|
private String provider;
|
2024-10-14 22:34:41 +01:00
|
|
|
private Boolean enabled = false;
|
2024-10-20 13:30:58 +02:00
|
|
|
private Boolean autoCreateUser = false;
|
|
|
|
private Boolean blockRegistration = false;
|
|
|
|
private String registrationId = "stirling";
|
2024-12-18 13:40:24 +01:00
|
|
|
@ToString.Exclude private String idpMetadataUri;
|
2024-10-20 13:30:58 +02:00
|
|
|
private String idpSingleLogoutUrl;
|
|
|
|
private String idpSingleLoginUrl;
|
|
|
|
private String idpIssuer;
|
|
|
|
private String idpCert;
|
2024-12-18 13:40:24 +01:00
|
|
|
@ToString.Exclude private String privateKey;
|
|
|
|
@ToString.Exclude private String spCert;
|
2024-10-20 13:30:58 +02:00
|
|
|
|
|
|
|
public InputStream getIdpMetadataUri() throws IOException {
|
|
|
|
if (idpMetadataUri.startsWith("classpath:")) {
|
|
|
|
return new ClassPathResource(idpMetadataUri.substring("classpath".length()))
|
|
|
|
.getInputStream();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
URI uri = new URI(idpMetadataUri);
|
|
|
|
URL url = uri.toURL();
|
|
|
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
|
|
connection.setRequestMethod("GET");
|
|
|
|
return connection.getInputStream();
|
|
|
|
} catch (URISyntaxException e) {
|
|
|
|
throw new IOException("Invalid URI format: " + idpMetadataUri, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public Resource getSpCert() {
|
2025-01-06 12:41:30 +00:00
|
|
|
if (spCert == null) return null;
|
2024-10-20 13:30:58 +02:00
|
|
|
if (spCert.startsWith("classpath:")) {
|
|
|
|
return new ClassPathResource(spCert.substring("classpath:".length()));
|
|
|
|
} else {
|
|
|
|
return new FileSystemResource(spCert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-24 22:18:34 +00:00
|
|
|
public Resource getIdpCert() {
|
2025-01-06 12:41:30 +00:00
|
|
|
if (idpCert == null) return null;
|
2024-10-20 13:30:58 +02:00
|
|
|
if (idpCert.startsWith("classpath:")) {
|
|
|
|
return new ClassPathResource(idpCert.substring("classpath:".length()));
|
|
|
|
} else {
|
|
|
|
return new FileSystemResource(idpCert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public Resource getPrivateKey() {
|
|
|
|
if (privateKey.startsWith("classpath:")) {
|
|
|
|
return new ClassPathResource(privateKey.substring("classpath:".length()));
|
|
|
|
} else {
|
|
|
|
return new FileSystemResource(privateKey);
|
2024-10-14 22:34:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2024-04-29 15:01:22 -06:00
|
|
|
public static class OAUTH2 {
|
2024-05-25 18:19:03 +02:00
|
|
|
private Boolean enabled = false;
|
2024-04-29 15:01:22 -06:00
|
|
|
private String issuer;
|
|
|
|
private String clientId;
|
2024-09-13 16:42:38 +01:00
|
|
|
@ToString.Exclude private String clientSecret;
|
2024-05-25 18:19:03 +02:00
|
|
|
private Boolean autoCreateUser = false;
|
2024-08-16 12:57:37 +02:00
|
|
|
private Boolean blockRegistration = false;
|
2024-05-12 19:58:34 +02:00
|
|
|
private String useAsUsername;
|
2024-05-25 18:19:03 +02:00
|
|
|
private Collection<String> scopes = new ArrayList<>();
|
2024-05-12 19:58:34 +02:00
|
|
|
private String provider;
|
2024-05-25 18:19:03 +02:00
|
|
|
private Client client = new Client();
|
2024-05-12 19:58:34 +02:00
|
|
|
|
2024-05-25 18:19:03 +02:00
|
|
|
public void setScopes(String scopes) {
|
2024-05-12 19:58:34 +02:00
|
|
|
List<String> scopesList =
|
2025-02-24 22:18:34 +00:00
|
|
|
Arrays.stream(scopes.split(",")).map(String::trim).toList();
|
2024-05-12 19:58:34 +02:00
|
|
|
this.scopes.addAll(scopesList);
|
|
|
|
}
|
|
|
|
|
2024-05-25 18:19:03 +02:00
|
|
|
protected boolean isValid(String value, String name) {
|
2024-09-13 16:42:38 +01:00
|
|
|
return value != null && !value.trim().isEmpty();
|
2024-05-25 18:19:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean isValid(Collection<String> value, String name) {
|
2024-09-13 16:42:38 +01:00
|
|
|
return value != null && !value.isEmpty();
|
2024-05-25 18:19:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isSettingsValid() {
|
2025-02-24 22:18:34 +00:00
|
|
|
return !isStringEmpty(this.getIssuer())
|
|
|
|
&& !isStringEmpty(this.getClientId())
|
|
|
|
&& !isStringEmpty(this.getClientSecret())
|
|
|
|
&& !isCollectionEmpty(this.getScopes())
|
|
|
|
&& !isStringEmpty(this.getUseAsUsername());
|
2024-05-25 18:19:03 +02:00
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2024-05-25 18:19:03 +02:00
|
|
|
public static class Client {
|
|
|
|
private GoogleProvider google = new GoogleProvider();
|
2025-02-24 22:18:34 +00:00
|
|
|
private GitHubProvider github = new GitHubProvider();
|
2024-05-25 18:19:03 +02:00
|
|
|
private KeycloakProvider keycloak = new KeycloakProvider();
|
|
|
|
|
2024-06-15 14:15:09 +02:00
|
|
|
public Provider get(String registrationId) throws UnsupportedProviderException {
|
2025-02-24 22:18:34 +00:00
|
|
|
return switch (registrationId.toLowerCase()) {
|
|
|
|
case "google" -> getGoogle();
|
|
|
|
case "github" -> getGithub();
|
|
|
|
case "keycloak" -> getKeycloak();
|
|
|
|
default ->
|
|
|
|
throw new UnsupportedProviderException(
|
|
|
|
"Logout from the provider "
|
|
|
|
+ registrationId
|
|
|
|
+ " is not supported. "
|
|
|
|
+ "Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
|
|
|
|
};
|
2024-05-25 18:19:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-08-26 17:30:49 +01:00
|
|
|
public static class System {
|
|
|
|
private String defaultLocale;
|
|
|
|
private Boolean googlevisibility;
|
2024-04-21 13:15:18 +02:00
|
|
|
private boolean showUpdate;
|
|
|
|
private Boolean showUpdateOnlyAdmin;
|
2024-04-27 11:03:57 +01:00
|
|
|
private boolean customHTMLFiles;
|
2024-08-15 11:43:56 +02:00
|
|
|
private String tessdataDir;
|
2023-12-26 20:10:37 +00:00
|
|
|
private Boolean enableAlphaFunctionality;
|
Improved Configuration and YAML Management (#2966)
# Description of Changes
**What was changed:**
- **Configuration Updates:**
Replaced all calls to `GeneralUtils.saveKeyToConfig` with the new
`GeneralUtils.saveKeyToSettings` method across multiple classes (e.g.,
`LicenseKeyChecker`, `InitialSetup`, `SettingsController`, etc.). This
update ensures consistent management of configuration settings.
- **File Path and Exception Handling:**
Updated file path handling in `SPDFApplication` by creating `Path`
objects from string paths and logging these paths for clarity. Also
refined exception handling by catching more specific exceptions (e.g.,
using `IOException` instead of a generic `Exception`).
- **Analytics Flag and Rate Limiting:**
Changed the analytics flag in the application properties from a `String`
to a `Boolean`, and updated related logic in `AppConfig` and
`PostHogService`. The rate-limiting property retrieval in `AppConfig`
was also refined for clarity.
- **YAML Configuration Management:**
Replaced the previous manual, line-based YAML merging logic in
`ConfigInitializer` with a new `YamlHelper` class. This helper leverages
the SnakeYAML engine to load, update, and save YAML configurations more
robustly while preserving comments and formatting.
**Why the change was made:**
- **Improved Maintainability:**
Consolidating configuration update logic into a single utility method
(`saveKeyToSettings`) reduces code duplication and simplifies future
maintenance.
- **Enhanced Robustness:**
The new `YamlHelper` class ensures that configuration files are merged
accurately and safely, minimizing risks of data loss or format
corruption.
- **Better Type Safety and Exception Handling:**
Switching the analytics flag to a Boolean and refining exception
handling improves code robustness and debugging efficiency.
- **Clarity and Consistency:**
Standardizing file path handling and logging practices enhances code
readability across the project.
**Challenges encountered:**
- **YAML Merging Complexity:**
Integrating the new `YamlHelper` required careful handling to preserve
existing settings, comments, and formatting during merges.
- **Type Conversion and Backward Compatibility:**
Updating the analytics flag from a string to a Boolean required
extensive testing to ensure backward compatibility and proper
functionality.
- **Exception Granularity:**
Refactoring exception handling from a generic to a more specific
approach involved a detailed review to cover all edge cases.
Closes #<issue_number>
---
## Checklist
- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings
### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### UI Changes (if applicable)
- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)
### Testing (if applicable)
- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
---------
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2025-02-25 22:52:59 +01:00
|
|
|
private Boolean enableAnalytics;
|
2025-01-06 18:58:26 +00:00
|
|
|
private Datasource datasource;
|
2025-02-01 00:36:50 +01:00
|
|
|
private Boolean disableSanitize;
|
2025-03-25 17:57:17 +00:00
|
|
|
private Boolean enableUrlToPDF;
|
2025-02-23 13:36:21 +00:00
|
|
|
private CustomPaths customPaths = new CustomPaths();
|
Improved Configuration and YAML Management (#2966)
# Description of Changes
**What was changed:**
- **Configuration Updates:**
Replaced all calls to `GeneralUtils.saveKeyToConfig` with the new
`GeneralUtils.saveKeyToSettings` method across multiple classes (e.g.,
`LicenseKeyChecker`, `InitialSetup`, `SettingsController`, etc.). This
update ensures consistent management of configuration settings.
- **File Path and Exception Handling:**
Updated file path handling in `SPDFApplication` by creating `Path`
objects from string paths and logging these paths for clarity. Also
refined exception handling by catching more specific exceptions (e.g.,
using `IOException` instead of a generic `Exception`).
- **Analytics Flag and Rate Limiting:**
Changed the analytics flag in the application properties from a `String`
to a `Boolean`, and updated related logic in `AppConfig` and
`PostHogService`. The rate-limiting property retrieval in `AppConfig`
was also refined for clarity.
- **YAML Configuration Management:**
Replaced the previous manual, line-based YAML merging logic in
`ConfigInitializer` with a new `YamlHelper` class. This helper leverages
the SnakeYAML engine to load, update, and save YAML configurations more
robustly while preserving comments and formatting.
**Why the change was made:**
- **Improved Maintainability:**
Consolidating configuration update logic into a single utility method
(`saveKeyToSettings`) reduces code duplication and simplifies future
maintenance.
- **Enhanced Robustness:**
The new `YamlHelper` class ensures that configuration files are merged
accurately and safely, minimizing risks of data loss or format
corruption.
- **Better Type Safety and Exception Handling:**
Switching the analytics flag to a Boolean and refining exception
handling improves code robustness and debugging efficiency.
- **Clarity and Consistency:**
Standardizing file path handling and logging practices enhances code
readability across the project.
**Challenges encountered:**
- **YAML Merging Complexity:**
Integrating the new `YamlHelper` required careful handling to preserve
existing settings, comments, and formatting during merges.
- **Type Conversion and Backward Compatibility:**
Updating the analytics flag from a string to a Boolean required
extensive testing to ensure backward compatibility and proper
functionality.
- **Exception Granularity:**
Refactoring exception handling from a generic to a more specific
approach involved a detailed review to cover all edge cases.
Closes #<issue_number>
---
## Checklist
- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings
### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### UI Changes (if applicable)
- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)
### Testing (if applicable)
- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
---------
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2025-02-25 22:52:59 +01:00
|
|
|
|
|
|
|
public boolean isAnalyticsEnabled() {
|
|
|
|
return this.getEnableAnalytics() != null && this.getEnableAnalytics();
|
|
|
|
}
|
2025-02-23 13:36:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class CustomPaths {
|
|
|
|
private Pipeline pipeline = new Pipeline();
|
|
|
|
private Operations operations = new Operations();
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class Pipeline {
|
|
|
|
private String watchedFoldersDir;
|
|
|
|
private String finishedFoldersDir;
|
|
|
|
private String webUIConfigsDir;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class Operations {
|
|
|
|
private String weasyprint;
|
|
|
|
private String unoconvert;
|
|
|
|
}
|
2025-01-06 18:58:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class Datasource {
|
|
|
|
private boolean enableCustomDatabase;
|
|
|
|
private String customDatabaseUrl;
|
|
|
|
private String type;
|
|
|
|
private String hostName;
|
|
|
|
private Integer port;
|
|
|
|
private String name;
|
|
|
|
private String username;
|
|
|
|
@ToString.Exclude private String password;
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum Driver {
|
|
|
|
H2("h2"),
|
|
|
|
POSTGRESQL("postgresql"),
|
|
|
|
ORACLE("oracle"),
|
|
|
|
MYSQL("mysql");
|
|
|
|
|
|
|
|
private final String driverName;
|
|
|
|
|
|
|
|
Driver(String driverName) {
|
|
|
|
this.driverName = driverName;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return """
|
2025-02-24 22:18:34 +00:00
|
|
|
Driver {
|
|
|
|
driverName='%s'
|
|
|
|
}
|
|
|
|
"""
|
2025-01-06 18:58:26 +00:00
|
|
|
.formatted(driverName);
|
|
|
|
}
|
2023-12-30 19:11:27 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-08-26 17:30:49 +01:00
|
|
|
public static class Ui {
|
2023-08-27 00:38:17 +01:00
|
|
|
private String appName;
|
|
|
|
private String homeDescription;
|
|
|
|
private String appNameNavbar;
|
2025-02-03 11:52:34 +01:00
|
|
|
private List<String> languages;
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2023-08-27 00:38:17 +01:00
|
|
|
public String getAppName() {
|
2024-09-13 16:42:38 +01:00
|
|
|
return appName != null && appName.trim().length() > 0 ? appName : null;
|
2023-08-27 00:38:17 +01:00
|
|
|
}
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2023-08-27 00:38:17 +01:00
|
|
|
public String getHomeDescription() {
|
2024-09-13 16:42:38 +01:00
|
|
|
return homeDescription != null && homeDescription.trim().length() > 0
|
|
|
|
? homeDescription
|
|
|
|
: null;
|
2023-08-27 00:38:17 +01:00
|
|
|
}
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2023-08-27 00:38:17 +01:00
|
|
|
public String getAppNameNavbar() {
|
2024-09-13 16:42:38 +01:00
|
|
|
return appNameNavbar != null && appNameNavbar.trim().length() > 0
|
|
|
|
? appNameNavbar
|
|
|
|
: null;
|
2023-08-26 17:30:49 +01:00
|
|
|
}
|
2023-12-30 19:11:27 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-08-26 17:30:49 +01:00
|
|
|
public static class Endpoints {
|
|
|
|
private List<String> toRemove;
|
|
|
|
private List<String> groupsToRemove;
|
2023-12-30 19:11:27 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-08-26 17:30:49 +01:00
|
|
|
public static class Metrics {
|
|
|
|
private Boolean enabled;
|
|
|
|
}
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
2023-08-26 17:30:49 +01:00
|
|
|
public static class AutomaticallyGenerated {
|
2024-09-13 16:42:38 +01:00
|
|
|
@ToString.Exclude private String key;
|
2024-10-14 22:34:41 +01:00
|
|
|
private String UUID;
|
2024-12-10 11:17:50 +00:00
|
|
|
private String appVersion;
|
2024-09-13 16:42:38 +01:00
|
|
|
}
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2025-03-25 17:57:17 +00:00
|
|
|
// TODO: Remove post migration
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
|
|
|
public static class EnterpriseEdition {
|
2024-10-14 22:34:41 +01:00
|
|
|
private boolean enabled;
|
2024-09-13 16:42:38 +01:00
|
|
|
@ToString.Exclude private String key;
|
2024-10-14 22:34:41 +01:00
|
|
|
private int maxUsers;
|
2025-01-09 14:40:51 +00:00
|
|
|
private boolean ssoAutoLogin;
|
2024-09-13 16:42:38 +01:00
|
|
|
private CustomMetadata customMetadata = new CustomMetadata();
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
@Data
|
|
|
|
public static class CustomMetadata {
|
|
|
|
private boolean autoUpdateMetadata;
|
|
|
|
private String author;
|
|
|
|
private String creator;
|
|
|
|
private String producer;
|
2023-12-30 19:11:27 +00:00
|
|
|
|
2024-09-13 16:42:38 +01:00
|
|
|
public String getCreator() {
|
|
|
|
return creator == null || creator.trim().isEmpty() ? "Stirling-PDF" : creator;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getProducer() {
|
|
|
|
return producer == null || producer.trim().isEmpty() ? "Stirling-PDF" : producer;
|
|
|
|
}
|
2023-08-26 17:30:49 +01:00
|
|
|
}
|
|
|
|
}
|
2024-11-06 17:43:57 -07:00
|
|
|
|
2025-03-25 17:57:17 +00:00
|
|
|
@Data
|
|
|
|
public static class Premium {
|
|
|
|
private boolean enabled;
|
|
|
|
@ToString.Exclude private String key;
|
|
|
|
private int maxUsers;
|
|
|
|
private ProFeatures proFeatures = new ProFeatures();
|
|
|
|
private EnterpriseFeatures enterpriseFeatures = new EnterpriseFeatures();
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class ProFeatures {
|
|
|
|
private boolean ssoAutoLogin;
|
|
|
|
private CustomMetadata customMetadata = new CustomMetadata();
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class CustomMetadata {
|
|
|
|
private boolean autoUpdateMetadata;
|
|
|
|
private String author;
|
|
|
|
private String creator;
|
|
|
|
private String producer;
|
|
|
|
|
|
|
|
public String getCreator() {
|
|
|
|
return creator == null || creator.trim().isEmpty() ? "Stirling-PDF" : creator;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getProducer() {
|
|
|
|
return producer == null || producer.trim().isEmpty()
|
|
|
|
? "Stirling-PDF"
|
|
|
|
: producer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class EnterpriseFeatures {
|
|
|
|
private PersistentMetrics persistentMetrics = new PersistentMetrics();
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class PersistentMetrics {
|
|
|
|
private boolean enabled;
|
|
|
|
private int retentionDays;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-06 17:43:57 -07:00
|
|
|
@Data
|
|
|
|
public static class ProcessExecutor {
|
|
|
|
private SessionLimit sessionLimit = new SessionLimit();
|
|
|
|
private TimeoutMinutes timeoutMinutes = new TimeoutMinutes();
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class SessionLimit {
|
|
|
|
private int libreOfficeSessionLimit;
|
|
|
|
private int pdfToHtmlSessionLimit;
|
|
|
|
private int pythonOpenCvSessionLimit;
|
|
|
|
private int weasyPrintSessionLimit;
|
|
|
|
private int installAppSessionLimit;
|
|
|
|
private int calibreSessionLimit;
|
2024-11-26 20:50:35 +00:00
|
|
|
private int qpdfSessionLimit;
|
|
|
|
private int tesseractSessionLimit;
|
|
|
|
|
|
|
|
public int getQpdfSessionLimit() {
|
|
|
|
return qpdfSessionLimit > 0 ? qpdfSessionLimit : 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getTesseractSessionLimit() {
|
|
|
|
return tesseractSessionLimit > 0 ? tesseractSessionLimit : 1;
|
|
|
|
}
|
2024-11-06 17:43:57 -07:00
|
|
|
|
|
|
|
public int getLibreOfficeSessionLimit() {
|
|
|
|
return libreOfficeSessionLimit > 0 ? libreOfficeSessionLimit : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getPdfToHtmlSessionLimit() {
|
|
|
|
return pdfToHtmlSessionLimit > 0 ? pdfToHtmlSessionLimit : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getPythonOpenCvSessionLimit() {
|
|
|
|
return pythonOpenCvSessionLimit > 0 ? pythonOpenCvSessionLimit : 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getWeasyPrintSessionLimit() {
|
|
|
|
return weasyPrintSessionLimit > 0 ? weasyPrintSessionLimit : 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getInstallAppSessionLimit() {
|
|
|
|
return installAppSessionLimit > 0 ? installAppSessionLimit : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getCalibreSessionLimit() {
|
|
|
|
return calibreSessionLimit > 0 ? calibreSessionLimit : 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Data
|
|
|
|
public static class TimeoutMinutes {
|
|
|
|
private long libreOfficeTimeoutMinutes;
|
|
|
|
private long pdfToHtmlTimeoutMinutes;
|
|
|
|
private long pythonOpenCvTimeoutMinutes;
|
|
|
|
private long weasyPrintTimeoutMinutes;
|
|
|
|
private long installAppTimeoutMinutes;
|
|
|
|
private long calibreTimeoutMinutes;
|
2024-11-26 20:50:35 +00:00
|
|
|
private long tesseractTimeoutMinutes;
|
|
|
|
private long qpdfTimeoutMinutes;
|
|
|
|
|
|
|
|
public long getTesseractTimeoutMinutes() {
|
|
|
|
return tesseractTimeoutMinutes > 0 ? tesseractTimeoutMinutes : 30;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getQpdfTimeoutMinutes() {
|
|
|
|
return qpdfTimeoutMinutes > 0 ? qpdfTimeoutMinutes : 30;
|
|
|
|
}
|
2024-11-06 17:43:57 -07:00
|
|
|
|
|
|
|
public long getLibreOfficeTimeoutMinutes() {
|
|
|
|
return libreOfficeTimeoutMinutes > 0 ? libreOfficeTimeoutMinutes : 30;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getPdfToHtmlTimeoutMinutes() {
|
|
|
|
return pdfToHtmlTimeoutMinutes > 0 ? pdfToHtmlTimeoutMinutes : 20;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getPythonOpenCvTimeoutMinutes() {
|
|
|
|
return pythonOpenCvTimeoutMinutes > 0 ? pythonOpenCvTimeoutMinutes : 30;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getWeasyPrintTimeoutMinutes() {
|
|
|
|
return weasyPrintTimeoutMinutes > 0 ? weasyPrintTimeoutMinutes : 30;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getInstallAppTimeoutMinutes() {
|
|
|
|
return installAppTimeoutMinutes > 0 ? installAppTimeoutMinutes : 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getCalibreTimeoutMinutes() {
|
|
|
|
return calibreTimeoutMinutes > 0 ? calibreTimeoutMinutes : 30;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-26 17:30:49 +01:00
|
|
|
}
|