Security fixes, enterprise stuff and more (#3241)

# Description of Changes

Please provide a summary of the changes, including:

- Enable user to add custom JAVA ops with env JAVA_CUSTOM_OPTS
- Added support for prometheus (enabled via JAVA_CUSTOM_OPTS +
enterprise license)
- Changed settings from enterprise naming to 'Premium'
- KeygenLicense Check to support offline licenses
- Disable URL-to-PDF due to huge security bug
- Remove loud Split PDF logs
- addUsers renamed to adminSettings
- Added Usage analytics page
- Add user button to only be enabled based on total users free
- Improve Merge memory usage


Closes #(issue_number)

---

## Checklist

### General

- [ ] 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)
- [ ] I have performed a self-review of my own code
- [ ] 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)

- [ ] 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: a <a>
Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
Co-authored-by: Connor Yoh <[email protected]>
This commit is contained in:
Anthony Stirling
2025-03-25 17:57:17 +00:00
committed by GitHub
co-authored by a pixeebot[bot] Connor Yoh
parent 86becc61de
commit e151286337
35 changed files with 1603 additions and 267 deletions
+1 -1
View File
@@ -349,7 +349,7 @@
</script>
<div class="mb-3 mt-4 text-center">
<a th:href="@{'/logout'}" role="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</a>
<a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" th:href="@{'/addUsers'}" role="button" th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
<a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" th:href="@{'/adminSettings'}" role="button" th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
</div>
</div>
</div>
@@ -32,21 +32,46 @@
</div>
<!-- User Settings Title -->
<div style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
<a href="#" data-bs-toggle="modal" data-bs-target="#addUserModal" class="btn btn-outline-success" th:title="#{adminUserSettings.addUser}">
<span class="material-symbols-rounded">person_add</span>
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
</a>
<a href="#" data-bs-toggle="modal" data-bs-target="#changeUserRoleModal" class="btn btn-outline-success" th:title="#{adminUserSettings.changeUserRole}">
<span class="material-symbols-rounded">edit</span>
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
</a>
<div class="my-4">
<strong th:text="#{adminUserSettings.totalUsers}">Total Users:</strong> <span th:text="${totalUsers}"></span>
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong> <span th:text="${activeUsers}"></span>
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.disabledUsers}">Disabled Users:</strong> <span th:text="${disabledUsers}"></span>
</div>
</div>
<div style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
<a href="#"
th:data-bs-toggle="${@runningEE && totalUsers >= maxEnterpriseUsers} ? null : 'modal'"
th:data-bs-target="${@runningEE && totalUsers >= maxEnterpriseUsers} ? null : '#addUserModal'"
th:class="${@runningEE && totalUsers >= maxEnterpriseUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
th:title="${@runningEE && totalUsers >= maxEnterpriseUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
<span class="material-symbols-rounded">person_add</span>
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
</a>
<a href="#"
data-bs-toggle="modal"
data-bs-target="#changeUserRoleModal"
class="btn btn-outline-success"
th:title="#{adminUserSettings.changeUserRole}">
<span class="material-symbols-rounded">edit</span>
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
</a>
<a href="/usage"
class="btn btn-outline-success"
th:title="#{adminUserSettings.usage}">
<span class="material-symbols-rounded">analytics</span>
<span th:text="#{adminUserSettings.usage}">Usage Statistics</span>
</a>
<div class="my-4">
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong>
<span th:text="${totalUsers}"></span>
<span th:if="${@runningEE}" th:text="'/'+${maxEnterpriseUsers}"></span>
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
<span th:text="${activeUsers}"></span>
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.disabledUsers}">Disabled Users:</strong>
<span th:text="${disabledUsers}"></span>
</div>
</div>
<div th:if="${addMessage}" class="p-3" style="background: var(--md-sys-color-outline-variant);border-radius: 2rem; text-align: center;">
<div class="alert alert-danger mb-auto">
<span th:text="#{${addMessage}}">Default message if not found</span>
@@ -101,7 +126,7 @@
</tbody>
</table>
</div>
<p th:text="#{enterpriseEdition.ssoAdvert}"></p>
<p th:if="${!@runningEE}" th:text="#{enterpriseEdition.ssoAdvert}"></p>
<script th:inline="javascript">
const delete_confirm_text = /*[[#{adminUserSettings.confirmDeleteUser}]]*/ 'Should the user be deleted?';
+110
View File
@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{endpointStatistics.title}, header=#{endpointStatistics.header})}"></th:block>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<link rel="stylesheet" th:href="@{'/css/usage.css'}">
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-9 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon">analytics</span>
<span class="tool-header-text" th:text="#{endpointStatistics.header}">Endpoint Statistics</span>
</div>
<!-- Statistics Summary Box -->
<div class="stats-box">
<div class="chart-controls">
<button id="top10Btn" class="btn btn-outline-primary active">
<span class="material-symbols-rounded">bar_chart</span>
<span th:text="#{endpointStatistics.top10}">Top 10</span>
</button>
<button id="top20Btn" class="btn btn-outline-primary">
<span class="material-symbols-rounded">data_usage</span>
<span th:text="#{endpointStatistics.top20}">Top 20</span>
</button>
<button id="allBtn" class="btn btn-outline-primary">
<span class="material-symbols-rounded">insights</span>
<span th:text="#{endpointStatistics.all}">All</span>
</button>
<button id="refreshBtn" class="btn btn-outline-secondary">
<span class="material-symbols-rounded">refresh</span>
<span th:text="#{endpointStatistics.refresh}">Refresh</span>
</button>
</div>
<div class="filter-controls">
<label class="filter-checkbox">
<input type="checkbox" id="hideHomeCheckbox" checked>
<span th:text="#{endpointStatistics.includeHomepage}">Include Homepage ('/')</span>
</label>
<label class="filter-checkbox">
<input type="checkbox" id="hideLoginCheckbox" checked>
<span th:text="#{endpointStatistics.includeLoginPage}">Include Login Page ('/login')</span>
</label>
</div>
<div class="my-4" style="color: var(--md-sys-color-on-surface); font-weight: 500;">
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.totalEndpoints} + ':'">Total Endpoints:</strong> <span id="totalEndpoints">0</span></span>
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.totalVisits} + ':'">Total Visits:</strong> <span id="totalVisits">0</span></span>
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.showing} + ':'">Showing:</strong> <span id="currentlyShowing">Top 10</span></span>
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.selectedVisits} + ':'">Selected Visits:</strong> <span id="displayedVisits">0</span> (<span id="displayedPercentage">0</span>%)</span>
</div>
</div>
<!-- Chart Container -->
<div class="bg-card mt-3 mb-3">
<div class="chart-container">
<canvas id="endpointChart"></canvas>
</div>
</div>
<!-- Table for detailed data -->
<div class="bg-card mt-3 mb-3">
<table class="endpoint-table">
<thead>
<tr>
<th scope="col" style="width: 5%;">#</th>
<th scope="col" style="width: 55%;" th:text="#{endpointStatistics.endpoint}">Endpoint</th>
<th scope="col" style="width: 20%;" th:text="#{endpointStatistics.visits}">Visits</th>
<th scope="col" style="width: 20%;" th:text="#{endpointStatistics.percentage}">Percentage</th>
</tr>
</thead>
<tbody id="endpointTableBody">
<!-- Table rows will be dynamically generated -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script type="module" th:src="@{'/js/usage.js'}"></script>
<script th:inline="javascript">
const endpointStatsTranslations = {
all: /*[[#{endpointStatistics.all}]]*/ 'All',
top20: /*[[#{endpointStatistics.top20}]]*/ 'Top 20',
loading: /*[[#{endpointStatistics.loading}]]*/ 'Loading...',
failedToLoad: /*[[#{endpointStatistics.failedToLoad}]]*/ 'Failed to load endpoint data. Please try refreshing.',
home: /*[[#{endpointStatistics.home}]]*/ 'Home',
login: /*[[#{endpointStatistics.login}]]*/ 'Login',
top: /*[[#{endpointStatistics.top}]]*/ 'Top ',
numberOfVisits: /*[[#{endpointStatistics.numberOfVisits}]]*/ 'Number of Visits',
visitsTooltip: /*[[#{endpointStatistics.visitsTooltip}]]*/ 'Visits: {0} ({1}% of total)',
retry: /*[[#{endpointStatistics.retry}]]*/ 'Retry'
};
</script>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>