mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
format_java
679
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7a748d4ad2 |
Add stored supporting files for pipeline steps (#7146)
# Description of Changes Backend only change for pipelines to support files (ie pipeline to sign all files with the same cert file etc) --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
34819ae502 |
Draw signatures on a phone via QR code in the Sign tool (#7335)
# Description of Changes Scan a QR code in the Sign tool, draw your signature on your phone, and it appears on your desktop ready to place. Rides the mobile scanner's existing transfer sessions — no new backend endpoints. **Desktop:** a **Mobile upload** button above the signature source selector shows a QR code. When the signature arrives, the modal closes, it lands in the matching source, and **placement activates automatically** — click the PDF to place. **Phone:** a new public `/mobile-sign` page with three tabs (same order as the desktop sources): - **Draw** → canvas signature. Touch-first pad (pointer events, DPR-aware, smoothed strokes, undo/clear, black/blue ink, 3 pen sizes), exported as a transparent PNG cropped to the ink. Compact layout in phone landscape. - **Photo** → image signature. "Take a photo" opens the camera directly; "From gallery" opens the picker. A preview of the current image signature now shows in the desktop's Image source (previously arrival was invisible until placement — also fixes this for saved image signatures). - **Type** → text signature. Travels as data (text + font + colour), so it stays *editable* on the desktop. Fonts are the sign tool's own text-mode list. **Security:** the transfer endpoints are unauthenticated by design (10-min sessions, files deleted after download — same model as the scanner). The desktop treats every arrival as untrusted: images only, and the text payload is clamped field by field. **Config:** new `system.enableMobileSignature` flag (default on), independent of `enableMobileScanner`; the shared endpoints accept either. The Tauri desktop app serves a self-contained `mobile-sign.html` (draw-only), mirroring `mobile-upload.html`. **Refactor:** the session lifecycle (create/poll/download/expiry) moved out of `MobileUploadModal` into a shared `useMobileTransferSession` hook; the scanner modal now uses it, behaviour unchanged. Also fixes two bugs hit along the way: the signature pad collapsing to its 150px intrinsic height (indefinite parent height), and a setState loop in `SignSettings` when text parameters are set programmatically (draft-sync effects ping-ponging). ## Screenshots | Desktop: QR entry | Phone: draw | Desktop: received | |---|---|---| |  |  |  | ## How to test 1. Open the app on an address your phone can reach (not `localhost`), Sign tool → **Mobile upload**, scan the QR. 2. Draw → **Send to computer** → it becomes the active canvas signature and placement is live: click the PDF to place. 3. Photo tab → arrives in the Image source with a preview. Type tab → arrives editable in the Text source. 4. Flags: `enableMobileSignature: false` hides the button; signature still works with the scanner disabled. Verified end-to-end (all three kinds, portrait/landscape/tablet) plus `task frontend:check` and the touched backend tests. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] 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/devGuide/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) - [x] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [x] 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 run `task check` to verify linters, typechecks, and tests pass - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
8b1bfb87f7 |
Add S3 Object Lock retention to policy outputs (#7094)
# Description of Changes Add S3 Object Lock retention to policy outputs (create file and cant be deleted untill after a set deadline passed) --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
c146f7e877 |
chore(saas): drop the last dead Flyway migration (#7433)
Removes `app/saas/src/main/resources/db/migration/saas/V33__api_keys.sql`, the only file left in that tree. ## Flyway does not run There is no Flyway dependency in any `build.gradle` and no Flyway configuration in any properties file. Nothing has executed these for some time, so `V33` was never applied by anything. SaaS schema has exactly two writers today: 1. Supabase CLI migrations in the `Stirling-PDF-SaaS` repo, applied by that repo's PR CI 2. Hibernate `ddl-auto=update` in the app, which only ever adds ## Why delete rather than leave it A directory of plausible-looking migrations is a trap. The next person to make a schema change adds a `V34`, assumes it will run, and it silently does not. I nearly did exactly that while working on [#7414](https://github.com/Stirling-Tools/Stirling-PDF/pull/7414) before checking whether Flyway was actually wired. ## Nothing is lost Both tables it declared, `api_keys` and `api_key_daily_usage`, have JPA entities (`ApiKey`, `ApiKeyDailyUsage`), so `ddl-auto` creates them. That is already how they exist on every deployment that has them, since Flyway was not the thing creating them. ## Checks No references anywhere: nothing in code, config, gradle or docs mentions `db/migration`, `flyway` or `V33`. Two code comments mention Flyway historically, explaining why a column looks the way it does; those are accurate history and are left alone. `:saas:compileJava` and `:saas:processResources` green after the removal. |
||
|
|
df170fd4a6 |
feat(storage): encryption-at-rest ops — audit, admin kill switch, migration, key rotation (PR2) (#7173)
# Description of Changes
**PR2 of the encrypt-at-rest initiative — PR1 was #7155** Makes the P1
crypto operable and compliance-credible: admins can see the feature's
state, flip the kill switch over an API instead of raw SQL, encrypt the
pre-existing plaintext backlog, rotate the master key, and every
security-relevant event lands in the audit trail. No frontend — that's
PR3.
**What was changed**
- **Audit events** — new `STORAGE_ENCRYPTION` audit type, emitted
through a small listener interface so the crypto classes stay plain
objects: `encrypt`, `decrypt` (per-read events honour
`storage.encryption.auditReads`, default **on** — HIPAA reviewers expect
read audit; busy installs can disable), `decrypt.denied` (always),
`key.created/disabled/enabled`, `master.rotated`, `migration.completed`,
plus a `plaintextExport` marker whenever a plaintext copy of
encrypted-at-rest content is served (with `inline` flag to distinguish
in-app view from saved download).
- **Admin API** `/api/v1/admin/storage-encryption` (`hasRole('ADMIN')`):
- `GET /status` — write/decrypt state, **master-key fingerprint**
(SHA-256 prefix for backup verification, never key material), encrypted
vs plaintext file counts, full key list with status history.
- `POST /keys/{id}/disable` / `enable` — the kill switch, now with
active cache invalidation so revocation is immediate on the handling
node (cross-node converges within the 60s cache TTL). Enable is
restricted to DISABLED keys so two ACTIVE keys can't exist per scope.
- `POST /migrate` + `GET /migrate/status` — encrypt-existing job.
- `POST /master/rotate` — key material is never accepted over HTTP; keys
come from config/env.
- **Deliberately no delete endpoint** — key material can be disabled but
never destroyed through the API.
- **Encrypt-existing migration job** — new writes are encrypted from the
moment the flag is on; this converts the backlog. Crash-safe per file:
store the encrypted copy under a NEW storage key → compare-and-swap the
DB row → only then delete the old blob. A CAS miss (user replaced the
file mid-run) discards the job's copy — the user's file always wins.
Worst crash outcome is an orphaned blob, never a lost file; re-runs are
idempotent (`encryption_key_id IS NULL` selection, cursor-paged so
failures can't wedge the loop). Handles all three blobs per row
(main/history/audit-log), runs on a throttled virtual thread,
single-flight guarded.
- **Master-key rotation** — cheap by design thanks to the P1 hierarchy:
rotate re-wraps the handful of KEK rows, zero file I/O. New config
`stirling.security.fileEncryptionKeyPrevious` (+env) gives `unwrap` a
fallback during rotation, and
`stirling.security.fileEncryptionKeyVersion` marks which master wrapped
each row. Runbook: set new key primary + old as previous + bump version
→ restart (startup self-check passes via fallback, warns about pending
rows) → `POST /master/rotate` → remove the previous key.
- **Shared state bean** — `StorageEncryptionState` is built once and
shared by the storage decorator and the admin API, so kill-switch cache
invalidation hits the same caches the decorator reads.
**Reviewer notes**
- The revoked→403 mapping promised for PR2 already landed in #7155 after
manual testing; this PR adds the matching `decrypt.denied` audit event.
- 19 new tests: audit emission (encrypt/decrypt/denied, legacy plaintext
emits nothing), kill-switch immediacy (no TTL wait), rotation
(previous-key fallback, re-wrap + cleanup, idempotent second call),
migration (backlog encrypted byte-identical, CAS-miss discards own copy,
per-file failure counting, concurrent-start rejection, write-disabled
rejection), admin controller status/conflict/not-found paths.
- Full proprietary suite: 2246/2247 green (the one failure is the
pre-existing Windows-symlink FolderIdentitiesTest, unrelated).
---
[ENCRYPTION_AT_REST_TEST_REPORT.html](https://github.com/user-attachments/files/30664158/ENCRYPTION_AT_REST_TEST_REPORT.html)
---------
Co-authored-by: Reece Browne <74901996+reecebrowne@users.noreply.github.com>
|
||
|
|
05eb74022a |
chore(ci): migrate Python tooling to uv and standardize workflow execution (#7386)
# Description of Changes This PR modernizes the project's Python tooling across GitHub Actions by migrating CI workflows from pip-based dependency management to `uv` and aligning Python execution with the engine project's managed environment. ### What was changed - Replaced `actions/setup-python` and ad-hoc `pip install` steps with `astral-sh/setup-uv` across CI workflows. - Configured shared `uv` dependency caching using `engine/pyproject.toml` and `engine/uv.lock`. - Updated Python script execution to use `uv run --project engine --locked` for a consistent runtime environment. - Replaced package installation steps with `uv sync` for the required dependency groups (e.g. `tools` and `cucumber`). - Added Docker image build validation for both production and development AI engine images. - Updated workflow cache configuration and Docker build context where required. - Removed obsolete Python requirements files that are no longer needed after the migration. - Applied minor Python code modernizations, including import cleanup, modern built-in generic type annotations (`list[...]`, `tuple[...]`, `float | None`), and small style improvements. - Removed unnecessary Python formatter/linter extensions from the development container configuration. ### Why the change was made - Standardize Python dependency management across the repository. - Reduce duplicated dependency installation logic in CI. - Improve workflow performance through shared dependency caching. - Ensure all Python utilities execute against the same locked dependency set managed by the engine project. - Simplify long-term maintenance by eliminating legacy requirements files and pip-specific workflow steps. --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. --------- Signed-off-by: Carsten Drewes <c.drewes@stud.uni-hannover.de> Co-authored-by: albanobattistella <34811668+albanobattistella@users.noreply.github.com> Co-authored-by: kastenherri <116314318+kastenherri@users.noreply.github.com> Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Brunton <jbrunton96@gmail.com> |
||
|
|
c929386442 |
Record policy-run failures as durable, actionable events (Review Flow PR 1) (#7269)
# Description of Changes PR 1 of the failure-notification work: a durable, team-scoped record of **why a policy run failed**, surfaced in the portal with the triage actions each failure allows. Today a failed policy run is not quite invisible, but it is unusable: the ledger marks the file `ERROR`, and the audit aspect keeps the exception message and status code. Nothing classifies either one, nothing surfaces them, and neither offers a next step. If the file came from a folder, bucket or webhook there is also no user watching, so nobody learns it never made it through. This adds the record and the read surface; the remediation that acts on documents comes later (see below). ## What this does **A failure kind registry as data.** `FailureKind` describes what can go wrong: a stable wire id, i18n keys, an English fallback, and four facets the review surface needs (`Stage`, `Severity`, `Remedy`, `Scope`). It is shaped like the existing `ExceptionUtils.ErrorCode` and *links* to that vocabulary rather than replacing it. **Classification off structured codes, not message matching.** Policy steps dispatch over loopback HTTP, so a tool's 4xx arrives as a `RestClientResponseException` whose body is the Problem Details document carrying `errorCode`. `FailureClassifier` reads that. Anything unrecognised becomes `UNKNOWN`, which is the point: every failed run gets an addressable record from day one, and which kinds to promote next is answered by production frequency rather than guesswork. **Actions declared by a kind, implemented as beans.** A kind lists the `FailureActionId`s it offers; behaviour lives in `FailureAction` beans resolved by id — the idiom this codebase already uses for `InputSource`, `PolicyOutputSink` and `PolicyTrigger`. A kind cannot be sent an action it never declared (400), so an incoherent pairing is unreachable rather than merely unrendered. A new kind ships as a registry entry plus copy: no new endpoint, no UI change. **Repeat folding.** Recording folds a genuine repeat into the existing incident instead of inserting again, keyed on `(team_id, dedup_key)`. That matters for a snapshot-mode source that re-lists every file on each sweep: the same broken file is one incident, not one per sweep. Distinct files keep distinct rows. The unique constraint is enforced by the database, and a writer that loses the insert race folds into the winner's row. One granularity caveat worth naming: nothing populates `file_id` in this PR, so every row has it NULL. A FILE-scoped kind therefore dedups on `policy + run` rather than `policy + file`. That still yields one row per document for the sources shipped here, because the folder, S3 and webhook sources each start one run per file; it stops holding as soon as a single run carries several documents, which is why editor-origin reporting (item 3 below) populates `file_id`. **No document identity is stored.** No file name, no content. `fileId` is an opaque reference only the owner's own client can resolve locally. `detail` keeps the raw message (the only diagnostic an `UNKNOWN` failure has) with anything path- or filename-shaped stripped on the way in, capped at 2,000 characters. `PolicyExecutor`'s type-mismatch message now reports the *extension* rather than the filename, since that message becomes the stored `detail`. **Access.** Reads and triage are leader-only, gated exactly the way `PolicyController` gates policy editing, with the single-user carve-out when login is disabled. Every read and write is scoped to the caller's own team from the authenticated principal — there is no team parameter on the API. Self-hosted needs no migration: the table is created from the entity by `ddl-auto=update`, as with every other table. ## What this does not do yet - **Actions are incident dispositions, not document dispositions.** Acknowledge and Dismiss change how a failure is displayed and touch nothing else — not the document, not the processed-file ledger, not the run, not any output destination. That is what makes them safe to offer against `UNKNOWN`, and why there is no Approve/Release yet. - **Two kinds only.** `INPUT_PASSWORD_PROTECTED` and `UNKNOWN`. Everything else classifies as `UNKNOWN` and shows its raw message. - **Editor-origin failures are not reported.** Every row is `PROCESSOR`. `FailureOrigin.EDITOR` and `API` exist in the enum but nothing writes them. - **The list is dev-only for now.** The section renders behind `import.meta.env.DEV`, so it ships in no production bundle. The endpoints are live and gated. - **No retention or per-team cap** on `file_run_events`. Tracked separately. - **No suspend-and-prompt.** `PolicyInputRequiredException` and the engine's `suspend()` exist but nothing throws it, so a run cannot pause to ask for a password today. - **SaaS needs a migration** in `Stirling-PDF-SaaS` (`CREATE TABLE IF NOT EXISTS stirling_pdf.file_run_events`), per the convention documented at `app/saas/src/main/resources/application-saas.properties:21`. ## What follows in later PRs 1. **Map the remaining error codes to specific kinds** — corrupted file, OCR unavailable, output destination unreachable, entitlement refusals, and so on — each with its own copy and its own action set, replacing today's `UNKNOWN` catch-all with a named notification in the review UI. 2. **Real remediation actions** attached to those kinds: fix (supply a password and resume), skip (drop this file, continue the batch), and decline (reject an incoming file outright), acting on the held document rather than only on the incident row. This is where the suspend-and-prompt path gets wired. 3. **Editor-origin reporting**, so a failure a user hits in the editor lands in the same queue as one from a bucket. 4. **The user-facing review surface**: notifications with a sticky review section, per-file badges, and an export gate, with the dev-only list here replaced by the real thing. ## How to test Needs a SaaS or proprietary build with login enabled, and an account that leads a team. 1. Create a policy in the Processor with any step (Auto-redact is fine) and a source you can drop files into. 2. Upload two files that will fail it: **a password-protected PDF**, and **a corrupted PDF** (truncate a valid one, or rename a `.csv` to `.pdf`). 3. Let the policy run and fail on both. 4. Go to the portal's **Documents** view and scroll to **Failures** (dev builds only). Expect two rows: - **Password-protected document** — classified from `E004`, with the kind's own labels **"I'll unlock this"** and **"Skip this file"** rather than generic wording. - **Unrecognised failure** — the corrupted file, classified `UNKNOWN` (`E001` is not claimed by a kind yet), showing its raw message with generic **Acknowledge** / **Dismiss**. Neither row contains a file name anywhere, including in the raw message. Press **Show raw JSON** to read exactly what the server returned. Acting on a row transitions it and comes back with both buttons disabled and a reason. Re-running the same batch increments the occurrence count on the existing rows rather than adding new ones; two *different* password-protected files produce two separate rows. --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass |
||
|
|
59ed4f5fd1 |
Fix automate unrunnable tools (#7311)
# Description of Changes Fix automate unrunnable tools ## Problem - Remove Image failed in Automate with `Tool operation not supported: removeImage` - Its registry entry had `operationConfig: undefined` even though the config existed and was already tested - The Automate picker only filtered on `supportsAutomate`, never on `operationConfig` — so broken tools were selectable and failed only at run time ## Fixes - Wire up `removeImage` and `pageLayout` operation configs (both already existed, just never registered) - Exclude `validateSignature` (report tool, not on the operationConfig seam) and `scannerEffect` (no frontend implementation) via `supportsAutomate: false` - Picker now also filters on `operationConfig`, so this class of bug can't reach users again - `overlay-pdfs` returns 400 instead of 500 when overlay files or mode are missing - Fix `new URL().pathname` Windows path bug that stopped 2 test suites from loading --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
af5f54274d |
Add defaults to calculations for ToolIO (#7289)
# Description of Changes Currently when calculating the output file type for some tools, the system will get it wrong because it doesn't know about what the default parameters in tools are, so if it doesn't have a value for some key, it'll just bail out and say "it might not be compatible". This PR adds logic to `ToolIO` to read the default values set for the parameters if the tool has `ToolIOCase`s and takes them into account when figuring out the output type. I've built it with horrible Java reflection magic to avoid having to specify the default for params twice, which will make it impossible for the defaults to disagree with each other. This just runs once at startup so there's negligible performance impact. The change is easily tested with Change Parameters, which is just `add-password` behind the scenes but with the password params omitted (so Change Password is always PDF->PDF, never encrypted like Add Password). Also (somewhat hackily) fixes a bug I noticed where saving a Change Permissions step then leaving and returning to the pipeline will cause the step to be reloaded as Add Password. I've added a system to disambiguate tools which share the same endpoint (which is only these two currently). ## Currently <img width="455" height="135" alt="image" src="https://github.com/user-attachments/assets/c8867d6a-599b-4f21-a2db-4a1b6ac22d73" /> ## Now <img width="415" height="122" alt="image" src="https://github.com/user-attachments/assets/bd8d1bab-f00a-421b-8c91-5af0d2ad5335" /> |
||
|
|
4e901f7524 |
refactor(package): rename example classes and update package structure (#7400)
# Description of Changes Refactors a small set of previously vendored/derived PDFBox "example" classes into Stirling’s own package namespaces. <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [X] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [X] 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [X] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
0c0a96aa83 |
build(deps): bump org.simplejavamail from 8.12.0 to 9.2.0 (#7398)
Bump org.simplejavamail:simple-java-mail and outlook-module from 8.12.6 to 9.2.0 in app/common/build.gradle. Add a Dependabot group for the org.simplejavamail artifacts in .github/dependabot.yml so updates for those packages are grouped into a single PR. --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
6d08cb2f52 |
build(deps): bump com.drewnoakes:metadata-extractor from 2.20.0 to 2.21.0 (#7338)
Bumps [com.drewnoakes:metadata-extractor](https://github.com/drewnoakes/metadata-extractor) from 2.20.0 to 2.21.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/drewnoakes/metadata-extractor/releases">com.drewnoakes:metadata-extractor's releases</a>.</em></p> <blockquote> <h2>2.21.0</h2> <h2>What's Changed</h2> <ul> <li>Replace custom number formatting in IccDescriptor with String.format by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/721">drewnoakes/metadata-extractor#721</a></li> <li>Fix OOM DoS vector in BmpHeaderDescriptor.formatHex via unbounded digits parameter by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/722">drewnoakes/metadata-extractor#722</a></li> <li>Normalize sign to numerator in Rational.toString() by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/724">drewnoakes/metadata-extractor#724</a></li> <li>Extract IPTC metadata from PNG text chunks by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/723">drewnoakes/metadata-extractor#723</a></li> <li>Tolerate duplicate PNG chunks that are not allowed to appear multiple times by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/725">drewnoakes/metadata-extractor#725</a></li> <li>Null-check and length-guard getPowerUpTimeDescription by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/727">drewnoakes/metadata-extractor#727</a></li> <li>Move DisposalMethod description logic to GifControlDescriptor by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/729">drewnoakes/metadata-extractor#729</a></li> <li>Port .NET RiffReader.processChunks error checking to Java by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/728">drewnoakes/metadata-extractor#728</a></li> <li>Fix NPE in ItemLocationBox when offset_size/length_size is 0 (valid AVIF) by <a href="https://github.com/hanskr"><code>@hanskr</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/732">drewnoakes/metadata-extractor#732</a></li> <li>Harden ICO parsing against oversized image counts by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/734">drewnoakes/metadata-extractor#734</a></li> <li>Support LONG8 and SLONG8 TIFF data formats by <a href="https://github.com/dschmidt"><code>@dschmidt</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/739">drewnoakes/metadata-extractor#739</a></li> <li>Detect QuickTime files that lack a leading ftyp box by <a href="https://github.com/dschmidt"><code>@dschmidt</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/736">drewnoakes/metadata-extractor#736</a></li> <li>Add basic MP4 udta metadata (Title, Comment, Subtitle, Rating, Category, Mood) by <a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/740">drewnoakes/metadata-extractor#740</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/drewnoakes"><code>@drewnoakes</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> made their first contribution in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/721">drewnoakes/metadata-extractor#721</a></li> <li><a href="https://github.com/hanskr"><code>@hanskr</code></a> made their first contribution in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/732">drewnoakes/metadata-extractor#732</a></li> <li><a href="https://github.com/dschmidt"><code>@dschmidt</code></a> made their first contribution in <a href="https://redirect.github.com/drewnoakes/metadata-extractor/pull/739">drewnoakes/metadata-extractor#739</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/drewnoakes/metadata-extractor/compare/2.20.0...2.20.1">https://github.com/drewnoakes/metadata-extractor/compare/2.20.0...2.20.1</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/6f037943c0811799a59b0ea19896cb2b07da17be"><code>6f03794</code></a> Merge pull request <a href="https://redirect.github.com/drewnoakes/metadata-extractor/issues/740">#740</a> from drewnoakes/dev/drnoakes/mp4-metadata-pr-review</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/d01cc419eea457b8d482f864d1f1bbacee834828"><code>d01cc41</code></a> Detect QuickTime files that lack a leading ftyp box (<a href="https://redirect.github.com/drewnoakes/metadata-extractor/issues/736">#736</a>)</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/1b45d70723192a02bde7626d6e011081d52c3592"><code>1b45d70</code></a> Support LONG8 and SLONG8 TIFF data formats</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/353e27c37c414702e92304a4067de96bb383def1"><code>353e27c</code></a> Address Copilot review: align entry parsing and fix tag name casing</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/31632d2d70497333e5aa71401323f17e0f6d515a"><code>31632d2</code></a> Clean up salvaged MP4 udta metadata code</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/845cdbab9a8f1348c52b1b4e10a6f774929ba303"><code>845cdba</code></a> Add more UDTA metadata to MP4 parser (Title, Comment, Subtitle, Rating, Categ...</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/5647ffd0541279bc488f6387ea91aa3c785de36b"><code>5647ffd</code></a> Harden ICO parsing against oversized image counts (<a href="https://redirect.github.com/drewnoakes/metadata-extractor/issues/734">#734</a>)</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/99383f6d345dfa9a72e0780c37876a8ebd31ce31"><code>99383f6</code></a> Merge pull request <a href="https://redirect.github.com/drewnoakes/metadata-extractor/issues/732">#732</a> from hanskr/fix-iloc-zero-offset-size-npe</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/35af34ae39dec240e1e0a1bded0161f6f86df38e"><code>35af34a</code></a> Fix NPE in ItemLocationBox when offset_size/length_size is 0</li> <li><a href="https://github.com/drewnoakes/metadata-extractor/commit/520e07fed8167863e2245f67d2622f9bb473d017"><code>520e07f</code></a> Merge pull request <a href="https://redirect.github.com/drewnoakes/metadata-extractor/issues/728">#728</a> from drewnoakes/copilot/enhance-error-checking-riffre...</li> <li>Additional commits viewable in <a href="https://github.com/drewnoakes/metadata-extractor/compare/2.20.0...2.21.0">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
2cf6db99ce |
Fix timing-fragile Valkey rate-limit boundary test (#7302)
# Description of Changes Fix timing-fragile Valkey rate-limit boundary test --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
ad8830b645 |
build(deps): bump com.sun.xml.bind:jaxb-core from 4.0.7 to 4.0.9 (#7270)
Bumps com.sun.xml.bind:jaxb-core from 4.0.7 to 4.0.9. [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
8094765bab |
build(licenses): Module-specific license. Add dependency overrides. (#7049)
# Description of Changes This change adds a version-scoped override mechanism for dependencies whose published metadata does not expose a detectable license. - Added `app/license-overrides.json` with verified Apache License 2.0 metadata for: - `com.hubspot.immutables:immutables-exceptions:1.9` - `com.hubspot:algebra:1.5` - Added `ModuleLicenseOverrideFilter` as custom `buildSrc` logic for the Gradle dependency license report plugin. - Applied overrides only when the exact `group:artifact:version` matches and no usable license metadata was detected. - Added automatic maintenance of the override file: - Removes overrides when the dependency is no longer resolved. - Removes overrides when the dependency starts publishing valid license metadata. - Migrates stale overrides to newer unresolved versions and clears their metadata for re-verification. - Adds null-valued placeholders for newly detected dependencies without license metadata. - Preserves populated overrides for newer versions when already present. - Added Gradle version-aware dependency ordering for override migration. - Registered `app/license-overrides.json` as an input for license-report and license-check preparation tasks. - Centralized the dependency license report plugin version in `buildSrc`. - Added unit tests covering override application, cleanup, migration, exact-version matching, concurrent versions, placeholder generation, and numeric version ordering. - Added documentation describing the override lifecycle, verification requirements, maintenance workflow, and validation commands. - Replaced broad null-license allowances for the two HubSpot modules with explicit Apache License 2.0 metadata. - Added accepted GNU Lesser General Public License name variants encountered in dependency metadata. The change was made because some dependencies have known upstream licenses but do not publish license metadata in a form detected by the Gradle license report plugin. Previously, these dependencies were permitted through module-specific null-license exceptions, leaving incomplete information in the generated report. The new mechanism supplies verified metadata without overriding valid metadata published by dependencies. --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
b10fc1b2de |
fix(java): prevent executor, task, regex, and stream resource leaks (#7284)
# Description of Changes - Added graceful shutdown handling for service-owned executors in `JobExecutorService`, `PolicyEngine`, and `AsyncConfig`. - Added expiration and cleanup for abandoned pending jobs in `TaskManager`. - Replaced the unbounded regex pattern cache with a bounded cache limited to 512 entries. - Ensured `Files.walk()` is closed correctly in `MobileScannerService`. - These changes prevent unbounded heap growth, lingering virtual-thread executors, and file-descriptor leaks. - Added configurable pending-job expiration through `stirling.job.pendingExpiryMinutes`, defaulting to 24 hours. --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
866e56728d |
fix(storage): delete share access records before expired share links (#7161)
# Description of Changes - Updated expired share-link cleanup to delete related `FileShareAccess` records before deleting their parent `FileShare` records. - Wrapped the cleanup operation in a transaction to ensure the deletion order is enforced atomically. - Prevents foreign-key constraint violations and scheduled-task failures during cleanup. - The full backend check was limited by a Gradle distribution download/network error. ```cmd [backend:dev:proprietary] 16:25:43.362 [scheduled-vt-2] WARN org.hibernate.orm.jdbc.error - HHH000247: ErrorCode: 23503, SQLState: 23503 [backend:dev:proprietary] 16:25:43.362 [scheduled-vt-2] WARN org.hibernate.orm.jdbc.error - Referentielle Integrität verletzt: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))" [backend:dev:proprietary] Referential integrity constraint violation: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))"; SQL statement: [backend:dev:proprietary] delete from file_shares where file_share_id=? [23503-240] [backend:dev:proprietary] 16:25:43.380 [scheduled-vt-2] ERROR o.s.s.s.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task [backend:dev:proprietary] org.springframework.dao.DataIntegrityViolationException: could not execute statement [Referentielle Integrität verletzt: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))" [backend:dev:proprietary] Referential integrity constraint violation: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))"; SQL statement: [backend:dev:proprietary] delete from file_shares where file_share_id=? [23503-240]] [delete from file_shares where file_share_id=?]; SQL [delete from file_shares where file_share_id=?]; constraint [FKQ6V4QH5LFCAWII0ABRVSJO5SG] [backend:dev:proprietary] at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.convertHibernateAccessException(HibernateExceptionTranslator.java:169) [backend:dev:proprietary] at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.convertHibernateAccessException(HibernateExceptionTranslator.java:131) [backend:dev:proprietary] at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.translateExceptionIfPossible(HibernateExceptionTranslator.java:105) [backend:dev:proprietary] at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223) [backend:dev:proprietary] at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:557) [backend:dev:proprietary] at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:794) [backend:dev:proprietary] at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:757) [backend:dev:proprietary] at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:687) [backend:dev:proprietary] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:408) [backend:dev:proprietary] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:130) [backend:dev:proprietary] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [backend:dev:proprietary] at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:135) [backend:dev:proprietary] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [backend:dev:proprietary] at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:166) [backend:dev:proprietary] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [backend:dev:proprietary] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222) [backend:dev:proprietary] at jdk.proxy4/jdk.proxy4.$Proxy246.deleteAll(Unknown Source) [backend:dev:proprietary] at stirling.software.proprietary.storage.service.StorageCleanupService.cleanupExpiredShareLinks(StorageCleanupService.java:71) [backend:dev:proprietary] at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) [backend:dev:proprietary] at java.base/java.lang.reflect.Method.invoke(Method.java:565) [backend:dev:proprietary] at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:128) [backend:dev:proprietary] at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$1(ScheduledMethodRunnable.java:122) [backend:dev:proprietary] at io.micrometer.observation.Observation.observe(Observation.java:569) [backend:dev:proprietary] at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:122) [backend:dev:proprietary] at org.springframework.scheduling.config.Task$OutcomeTrackingRunnable.run(Task.java:88) [backend:dev:proprietary] at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [backend:dev:proprietary] at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) [backend:dev:proprietary] at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:369) [backend:dev:proprietary] at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:310) [backend:dev:proprietary] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) [backend:dev:proprietary] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) [backend:dev:proprietary] at java.base/java.lang.VirtualThread.run(VirtualThread.java:460) [backend:dev:proprietary] Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement [Referentielle Integrität verletzt: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))" [backend:dev:proprietary] Referential integrity constraint violation: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))"; SQL statement: [backend:dev:proprietary] delete from file_shares where file_share_id=? [23503-240]] [delete from file_shares where file_share_id=?] [backend:dev:proprietary] at org.hibernate.dialect.H2Dialect.lambda$buildSQLExceptionConversionDelegate$0(H2Dialect.java:840) [backend:dev:proprietary] at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:34) [backend:dev:proprietary] at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:115) [backend:dev:proprietary] at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:184) [backend:dev:proprietary] at org.hibernate.engine.jdbc.mutation.internal.AbstractMutationExecutor.performNonBatchedMutation(AbstractMutationExecutor.java:145) [backend:dev:proprietary] at org.hibernate.engine.jdbc.mutation.internal.MutationExecutorSingleNonBatched.performNonBatchedOperations(MutationExecutorSingleNonBatched.java:53) [backend:dev:proprietary] at org.hibernate.engine.jdbc.mutation.internal.AbstractMutationExecutor.execute(AbstractMutationExecutor.java:66) [backend:dev:proprietary] at org.hibernate.persister.entity.mutation.AbstractDeleteCoordinator.doStaticDelete(AbstractDeleteCoordinator.java:268) [backend:dev:proprietary] at org.hibernate.persister.entity.mutation.AbstractDeleteCoordinator.delete(AbstractDeleteCoordinator.java:79) [backend:dev:proprietary] at org.hibernate.action.internal.EntityDeleteAction.execute(EntityDeleteAction.java:119) [backend:dev:proprietary] at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:634) [backend:dev:proprietary] at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:505) [backend:dev:proprietary] at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:381) [backend:dev:proprietary] at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:40) [backend:dev:proprietary] at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:138) [backend:dev:proprietary] at org.hibernate.internal.SessionImpl.fireFlush(SessionImpl.java:1484) [backend:dev:proprietary] at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:481) [backend:dev:proprietary] at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2111) [backend:dev:proprietary] at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2033) [backend:dev:proprietary] at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:410) [backend:dev:proprietary] at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:166) [backend:dev:proprietary] at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commitNoRollbackOnly(JdbcResourceLocalTransactionCoordinatorImpl.java:248) [backend:dev:proprietary] at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:242) [backend:dev:proprietary] at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:89) [backend:dev:proprietary] at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:553) [backend:dev:proprietary] ... 27 common frames omitted [backend:dev:proprietary] Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Referentielle Integrität verletzt: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))" [backend:dev:proprietary] Referential integrity constraint violation: "FKQ6V4QH5LFCAWII0ABRVSJO5SG: PUBLIC.FILE_SHARE_ACCESSES FOREIGN KEY(FILE_SHARE_ID) REFERENCES PUBLIC.FILE_SHARES(FILE_SHARE_ID) (CAST(97 AS BIGINT))"; SQL statement: [backend:dev:proprietary] delete from file_shares where file_share_id=? [23503-240] [backend:dev:proprietary] at org.h2.message.DbException.getJdbcSQLException(DbException.java:520) [backend:dev:proprietary] at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) [backend:dev:proprietary] at org.h2.message.DbException.get(DbException.java:223) [backend:dev:proprietary] at org.h2.message.DbException.get(DbException.java:199) [backend:dev:proprietary] at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:363) [backend:dev:proprietary] at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:380) [backend:dev:proprietary] at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:254) [backend:dev:proprietary] at org.h2.table.Table.fireConstraints(Table.java:1208) [backend:dev:proprietary] at org.h2.table.Table.fireAfterRow(Table.java:1226) [backend:dev:proprietary] at org.h2.command.dml.Delete.update(Delete.java:81) [backend:dev:proprietary] at org.h2.command.dml.DataChangeStatement.update(DataChangeStatement.java:77) [backend:dev:proprietary] at org.h2.command.CommandContainer.update(CommandContainer.java:139) [backend:dev:proprietary] at org.h2.command.Command.executeUpdate(Command.java:306) [backend:dev:proprietary] at org.h2.command.Command.executeUpdate(Command.java:250) [backend:dev:proprietary] at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:213) [backend:dev:proprietary] at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:172) [backend:dev:proprietary] at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61) [backend:dev:proprietary] at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java) [backend:dev:proprietary] at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:181) [backend:dev:proprietary] ... 48 common frames omitted ``` --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
e560ee4cc4 |
chore(deps): update junrar dependency to version 8.0.0 (#7210)
# Description of Changes Since this was a major version update i tested manually, afterwards figured i'll submit as PR. This version of junrar adds long-awaited (by me) RAR 5 support to the library. RAR 5 is newest version of the RAR file format and was not available in previous Junrar version, but is somewhat common for CBR files to be RAR 5. For junrar release notes see: https://github.com/junrar/junrar/releases/tag/v8.0.0 Changes: - Bumped junrar dep to version 8.0.0 <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [X] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [X] 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [X] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
7cccee4c34 |
refactor(get-info): remove redundant PDF validation logic (#7213)
# Description of Changes Could not get past validation, since very few endpoint have such validation, i think redundant. Changes: * Removed the `validatePdfFile` method, which previously checked for file presence, size limits, and content type, from `GetInfoOnPDF.java`. * Deleted the invocation of `validatePdfFile` and its associated error handling from the `getPdfInfo` method, so uploaded files are no longer validated at this layer. <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [X] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [X] 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [X] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
a2dd0298dc |
refactor(api): replace length checks with isEmpty (#7214)
# Description of Changes Stylistic problem reported by static analyzer. Changes: * Replaced `sb.length() > 0` and `sb.length() == 0` with `!sb.isEmpty()` and `sb.isEmpty()` for `StringBuilder`, `String`, and collections throughout the codebase, improving readability and aligning with modern Java best practices. <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
d0d197f09f |
Procurement: draft Enterprise Agreement + signature, legal pages & consent, quote/agreement split (#7021)
Consolidates the enterprise procurement and legal work into one PR off `main`. Supersedes #7020 (closed; every commit from it is contained here). Sits on top of PAYG prepaid bundles (#7032) and the `--color-*` → `--c-*` portal token rename. ## Why Enterprise procurement was a mock. The stage screens read from a fake state machine, the "agreement" was prose hardcoded in a component, and nothing a buyer did was recorded anywhere. To actually sell to an enterprise we need three things it didn't have: a real document they can read and sign, a record that proves they signed that exact version, and a licence that flips when they pay. ## What **The agreement is a real versioned document** - Registry at `resources/legal/manifest.json` + `legal/<id>/<version>/*.md`. Publishing a new version is a markdown file and a manifest bump, no code change. `@`-prefixed parts are generated sections. - `AgreementAssembler` builds MSA (Part A) + generated Order Form (Part B) + DPA (Part C) as one document. Only the Order Form varies per deal. - `AgreementPdfRenderer` goes through our own pipeline (commonmark → `FileToPdf`/WeasyPrint), so we dogfood it. - Immutable signature record pinning document id and version, a SHA-256 of the exact rendered markdown, the variable snapshot, typed signatory details, timestamp and IP. **Legal document pages and consent logging** - `GET /api/v1/legal/{docId}` serves any registry document; a viewer modal renders it with a draft badge. The SLA exhibit is viewable for the first time. - `legal_consent` + `POST /api/v1/legal/consent`. EULA clickwrap is recorded once: at trial start, or at the quote step only if there was no trial. **Quote and Agreement are separate steps** The quote step is a plain itemised review (figures, renewal, PO) with download and "Accept quote". Accepting advances to the agreement and does not charge Stripe. Signing the agreement is still the commitment point. **One quote number** We no longer mint our own reference. The Stripe quote number is the identifier everywhere, so the UI and the memo can't disagree. `quote_number` is nullable until Stripe assigns it at finalisation (`20260808000000`). **Payment takes the deal live** `invoice.paid` on the stripe-webhook moves the deal to live and the UI reflects it. Nothing watched for payment before, so a paid customer sat in "payment" forever. Needs `invoice.paid` enabled on the webhook endpoint in the Stripe dashboard. **Security** Any signup could self-issue a $0 enterprise licence, from three things compounding: leader-on-signup, no entitlement gate, and no ACV floor. So: `startTrial` now has a stage guard (it was replacing committed licences), the offline `.lic` is gated on entitlement, the ACV floor is enforced before the quote persists, and the air-gap check reads the quote's deployment rather than the deal's. Invitee emails are redacted in logs. Dev and Storybook were hitting real Stripe; both now route through `resolveDemoResponse`. **Removed the dead procurement island** The original stage-by-stage page survived the rebuild with no route and no consumer, so it was invisible to review but still cost a reader's time. 16 unreferenced files, 182 lines of superseded API, 53 orphaned en-US keys, and `Procurement.css` from 1665 to 968 lines. Nothing deleted had a live consumer. ## Screenshots Home, deal underway (hero card footer): <!-- home-in-procurement.png --> Quote builder, step 1: <!-- quote-builder.png --> Agreement, ready to sign: <!-- agreement-signing.png --> Payment and live: <!-- stage-payment.png / stage-live.png --> ## How to test **Storybook** covers every state without a backend: ```bash cd frontend && npm run storybook ``` Then `Portal/Procurement/*`: | Story | What to look at | | --- | --- | | `DealStatusHero` — Trial / Quote / Agreement / Payment / Live | One hero per stage: progress band, one-line status, stage CTA | | `QuoteBuilder` — Default | 4 steps. Users + volume drive the price; Governance and PDF size are multipliers; step 4 is the itemised review | | `ProcurementAgreement` — Default / Signing | Header actions, always-visible scrollbar on the paper, one-line signature row | | `ProcurementStages` — Payment / Live / License | "View & pay invoice" opens Stripe directly; licence key and `.lic` download | | `Views/Home` — Subscribed In Procurement | The hero in real page context | Note: `ProcurementAgreement` renders "Could not load the agreement" in Storybook because it fetches the document from the backend. The chrome is accurate, the paper body needs the app. **Full flow** needs SaaS running and a linked team: 1. Home → **Explore enterprise** → trial setup (deployment + seats). EULA is recorded here. 2. **Build your quote** → 4 steps → Generate. Buyer details are required first. 3. Review the itemised quote → **Accept quote**. Confirm Stripe was *not* charged. 4. Agreement → tick, fill signatory, **Sign agreement**. Check `procurement_signature` for the version and content hash. 5. **View & pay invoice** → pay in Stripe test mode → deal should move to live on the `invoice.paid` webhook. Worth reviewing specifically: the licence cannot be issued without entitlement (step 3 before payment), and `startTrial` on an already-committed deal is rejected rather than overwriting. ## Verification - `:saas compileJava` + `spotlessJavaCheck` - `task frontend:check:all` green end to end: 9 typecheck variants, eslint at zero warnings, `theme-lint`, `lint:css`, prettier, build, **1656 tests across 188 files** - 7 deno tests on the `invoice.paid` handler, covering all four shapes Stripe uses for the subscription reference ## Open, not addressed here - **The commercial model contradicts itself in three places.** The Order Form says annual-in-advance, the MSA §2.3/§3.2 implies otherwise, the quote engine computes `tcv = annualNet × termYears` flat, and Stripe only invoices one year. Needs a decision before this is customer-facing. - The 25 MB data-processing increments vs the ×1.4/×2.4 size multiplier, deferred pending Matt. - All legal text is **draft**. It renders with a draft badge and is not presented as executed; counsel's read is still a publish gate. - `{{subprocessor_url}}` / `{{eula_url}}` awaiting marketing's final links. - `frontend-a11y` is red on pre-existing portal contrast debt, deferred by decision. ## Schema notes Two migrations land on the SaaS side (`v3`), both applied by that repo's PR CI: - `20260808000000` drops the NOT NULL on `procurement_quote.quote_number`, which is required rather than cosmetic — the number now comes from Stripe at finalisation, so a draft holds NULL, and `ddl-auto` cannot drop an existing NOT NULL itself. - `20260809000000` adds `procurement_deal.last_paid_invoice_id`, nullable. Nothing here needs a migration in this repo: Flyway is not on the classpath, so the Java side only ever adds via `ddl-auto`, and Postgres migrations run ahead of the app deploy. |
||
|
|
d7c130fca9 |
Serve SPA shell for deep frontend routes (#7145)
# Description of Changes stops the /new urls crashing page on f5 --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. --------- Co-authored-by: Reece Browne <74901996+reecebrowne@users.noreply.github.com> |
||
|
|
921bdac4b7 |
Desktop installer fixes (#7174)
# Description of Changes
* Adds a `windows-11-arm` CI/release leg (NSIS, Microsoft JDK 25,
updater keys); JPDFium natives deliberately excluded
(`jpdfiumPlatforms=none`) until published, so don't ship ARM64
installers to users yet
* Defaults `WEBKIT_DISABLE_DMABUF_RENDERER=1` on Linux (crash switching
tools on NVIDIA)
* Strips the bundled libwayland from AppImages (blank window on Fedora
Wayland)
* Blocks off-app webview navigation + window drop guard + close failsafe
(drag-drop bricks the app)
* 120s startup grace before the backend is declared unhealthy, restart
success only announced after a real health check ("Backend stopped
unexpectedly" spam and likely the OAuth port churn)
* Verified: green `windows-arm64` build (234 MB NSIS artifact) and green
Linux run with libwayland confirmed stripped
* JPDFium fixes for multi threading issues
---
## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### Translations (if applicable)
- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)
### 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 run `task check` to verify linters, typechecks, and tests
pass
- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing)
for more details.
|
||
|
|
50bc4a7866 |
fix(storage): don't query the encryption key registry when storage is off (unblocks backend:dev:saas) (#7265)
# Description of Changes Fixes a startup failure introduced by #7155 and reported against `task backend:dev:saas`. **What goes wrong** `StorageProviderConfig.storageEncryptionState(...)` is created on every startup, in every profile. When `storage.encryption.enabled` is false — the default, and what SaaS ships — the `||` short-circuit evaluates `fileEncryptionKeyRepository.count()`, a live query against `file_encryption_keys`: ```java if (writeEnabled || fileEncryptionKeyRepository.count() > 0) { // <- always runs when the flag is off ``` That table only exists if `ddl-auto=update` managed to create it. When it cannot — permissions on a shared Supabase branch DB, concurrent DDL from several developers, schema ordering — **ddl-auto logs and continues**, so the situation used to be a warning nobody noticed. Now it is a query that throws during bean creation and takes the whole context down. Two things make this sting in SaaS specifically: `storage.enabled` is false there, so before this feature nothing ever touched the table; and `hibernate.default_schema=stirling_pdf` means the table has to exist in a schema the app may not be able to create in. There is a second exposure on the request path: `suppressDirectDownloads()` also counts (60s cached), so even a surviving boot could 500 on downloads. **Fix** - The boot probe runs only when `storage.enabled` is true, so a deployment that does not use storage never touches the table. - Registry reads are wrapped. The boot probe degrades to "no keys" rather than propagating; `suppressDirectDownloads()` **fails safe by suppressing** rather than issuing a presigned URL it cannot vouch for. Losing the direct-download fast path is recoverable; serving ciphertext is not. **Safety is unchanged, and that is the important part.** The decorator is still installed unconditionally, so any blob carrying the `SPDFEAR1` magic is still decrypted via lazy materialisation or fails loudly — the eager probe only ever bought *earlier* master-key verification. A node that can actually serve stored files has `storage.enabled` on by definition, which is exactly the node the drifted-node protection is for; that test now configures it that way, and a new test pins that the decorator remains installed even with storage off. **Tests** — storage-disabled never calls `count()`; an unreadable registry still boots *and* still suppresses direct downloads; the decorator stays installed with storage off; storage-enabled still probes. Full proprietary suite green apart from the pre-existing Windows-symlink `FolderIdentitiesTest` failure, which is environmental and unrelated. **Note on scope:** deliberately minimal so it can land quickly. The Aikido `findAll()` code-quality finding lives in #7173 only (`rotateMasterKey` does not exist on main), so it is fixed there rather than here. #7173 will be rebased once this merges. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
21dff695fe |
Add SFTP, FTP and SMB network sources to the processor (#7153)
# Description of Changes Add SFTP, FTP and SMB network sources to the processor plus UI change to enable it --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
cd199c8659 |
Define tool inputs & outputs in a structured way (#7204)
# Description of Changes Change tool APIs to use structured definitions for input/output/type info because we need that info to be able to validate whether policies can actually successfully work based on whether one tool accepts the output of another. There were various bugs in the previous string definitions because of either misspellings or just incorrect definitions, so I've gone through and fixed all that I can find. <img width="729" height="271" alt="image" src="https://github.com/user-attachments/assets/08357e96-6fbb-4b9c-ba4d-8995420c7b86" /> <img width="749" height="264" alt="image" src="https://github.com/user-attachments/assets/76f46284-1866-4b64-b1ed-2480e01866e9" /> <img width="402" height="636" alt="image" src="https://github.com/user-attachments/assets/8f7a36ca-2845-4f14-a2df-ec9c772e66f6" /> <img width="393" height="317" alt="image" src="https://github.com/user-attachments/assets/46d8b891-9820-4ce3-8109-a8b782277037" /> --------- Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> |
||
|
|
88cdfb3a43 |
build(deps): bump org.postgresql:postgresql from 42.7.11 to 42.7.13 (#7243)
Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.11 to 42.7.13. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/pgjdbc/pgjdbc/releases">org.postgresql:postgresql's releases</a>.</em></p> <blockquote> <h2>v42.7.13</h2> <h2>Changes</h2> <ul> <li>docs: add 42.7.13 release changelog <a href="https://github.com/davecramer"><code>@davecramer</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4270">#4270</a>)</li> <li>Adjust EditorConfig für Makefile <a href="https://github.com/BaumiCoder"><code>@BaumiCoder</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4279">#4279</a>)</li> <li>fix(scram): fail closed on channel-binding downgrade (no scram bump) <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4272">#4272</a>)</li> <li>Bump pgjdbc version from 42.7.12 to 42.7.13 <a href="https://github.com/davecramer"><code>@davecramer</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4269">#4269</a>)</li> <li>chore: remove test-anorm-sbt module and its disabled CI wiring <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4261">#4261</a>)</li> <li>refactor(test-gss): convert to Java/JUnit 5 submodule of the main build <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4166">#4166</a>)</li> <li>ci: derive PG test versions from a Renovate-managed maxPgVersion <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4218">#4218</a>)</li> <li>feat(insert): cap reWriteBatchedInserts by the protocol limit, not 128 <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4207">#4207</a>)</li> <li>refactor(metadata): derive getPrimaryKeys from pg_constraint.conkey <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4202">#4202</a>)</li> <li>fix(protocol): defer flushes until response processing <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4196">#4196</a>)</li> <li>fix(build): resolve the Temurin 8 test toolchain by vendor <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4257">#4257</a>)</li> <li>build: include multi-release source sets in the JaCoCo coverage report <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4256">#4256</a>)</li> <li>fix(ci): read java_vendor before overwriting java_distribution <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4255">#4255</a>)</li> <li>ci: generate the whole matrix in one batch, coverage job included <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4253">#4253</a>)</li> <li>ci: pass CODECOV_TOKEN so protected-branch coverage uploads succeed <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4254">#4254</a>)</li> <li>ci: collect coverage on one pinned job <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4245">#4245</a>)</li> <li>ci: apply -DqueryTimeout from the matrix query_timeout axis <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4246">#4246</a>)</li> <li>ci: make Codecov project and patch statuses informational <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4244">#4244</a>)</li> <li>fix(build): restore JaCoCo XML report so Codecov receives coverage <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4240">#4240</a>)</li> <li>test(replication): shrink big-transaction inserts to avoid CI timeouts <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4243">#4243</a>)</li> <li>update maintainers <a href="https://github.com/davecramer"><code>@davecramer</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4222">#4222</a>)</li> <li>test: add hermetic test for localSocketAddress <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4224">#4224</a>)</li> <li>docs(translation): clean up leftover German header in ja.po <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4206">#4206</a>)</li> <li>Update ja.po <a href="https://github.com/davecramer"><code>@davecramer</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/2004">#2004</a>)</li> <li>test: add PostgreSQL 18 to the CI test matrix <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4198">#4198</a>)</li> <li>test: silence expected SSPI warning stack trace in SSPIClientWaffleTest <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4197">#4197</a>)</li> <li>fix(ssl): build PKIX trust anchors without a KeyStore so FIPS-mode JVMs can load sslrootcert <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4193">#4193</a>)</li> <li>test: fix flaky sentLocationEqualToLastReceiveLSN replication test <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4175">#4175</a>)</li> <li>build: promote MethodCanBeStatic to error level <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4172">#4172</a>)</li> <li>Fix PGInterval.setSeconds to reject out of range and NaN values <a href="https://github.com/sehrope"><code>@sehrope</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4194">#4194</a>)</li> <li>Replace connectThreadFactory with connectExecutor <a href="https://github.com/sehrope"><code>@sehrope</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4165">#4165</a>)</li> <li>Fix deleting temp file when spooling large stream to disk in StreamWrapper <a href="https://github.com/sehrope"><code>@sehrope</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4190">#4190</a>)</li> <li>chore: Add top level /scratch to gitignore <a href="https://github.com/sehrope"><code>@sehrope</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4164">#4164</a>)</li> <li>refactor: favour composition over inheritance for Driver.ConnectTask <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4160">#4160</a>)</li> <li>Fix NumberParser.getFastLong(...) handling of overlong values <a href="https://github.com/sehrope"><code>@sehrope</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4163">#4163</a>)</li> <li>build: produce a multi-release jar from reduced-pom.xml on Java 11+ <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4157">#4157</a>)</li> <li>Add connectThreadFactory and refactor Driver to use FutureTask for loginTimeout connection attempts <a href="https://github.com/sehrope"><code>@sehrope</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4120">#4120</a>)</li> <li>test: verify custom properties reach socket factory <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4125">#4125</a>)</li> <li>test: fix LazyCleanerTest timeouts for the lingering Java 8 cleanup thread <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4122">#4122</a>)</li> <li>test: stabilise StatementTest.fastCloses on Windows <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4121">#4121</a>)</li> <li>fix: append default non-proxy hosts when socksNonProxyHosts is set <a href="https://github.com/davecramer"><code>@davecramer</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4045">#4045</a>)</li> <li>test: budget terminating Sync in BatchDeadlockTest small-RETURNING branch <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4116">#4116</a>)</li> <li>test: make message assertions locale-independent <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4113">#4113</a>)</li> <li>build: drop xgettext default keywords; regenerate translations <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4100">#4100</a>)</li> <li>ci: opt-in scheduled workflows via ENABLE_SCHEDULED_JOBS repo variable <a href="https://github.com/vlsi"><code>@vlsi</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4085">#4085</a>)</li> <li>Avoid direct java.lang.management dependency in maxResultBuffer parser <a href="https://github.com/mblakley-casana"><code>@mblakley-casana</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4069">#4069</a>)</li> <li>fix: restore pre-describe for generated-key batches <a href="https://github.com/bilalshehata"><code>@bilalshehata</code></a> (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4014">#4014</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md">org.postgresql:postgresql's changelog</a>.</em></p> <blockquote> <h2>[42.7.13] (2026-07-06)</h2> <h3>Added</h3> <ul> <li>feat: invalidate the prepared-statement cache when the server reports a <code>search_path</code> change via GUC_REPORT (PostgreSQL 18+), so cached plans are no longer used against the wrong schema [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4259">#4259</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4259">pgjdbc/pgjdbc#4259</a>)</li> <li>feat: <code>reWriteBatchedInserts</code> now merges up to 32768 rows into one multi-values <code>INSERT</code> (bounded by the 65535 bind-parameter limit on the extended protocol) instead of capping at 128, which speeds up batches of few-column rows. The new <code>reWriteBatchedInsertsSize</code> connection property lowers that cap when set; the default of <code>0</code> uses that maximum. [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4207">#4207</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4207">pgjdbc/pgjdbc#4207</a>)</li> <li>feat: invalidate the prepared-statement cache after CREATE/DROP/ALTER so callers no longer trip on "cached plan must not change result type" without opting into <code>autosave=ALWAYS</code>. Controlled by the new <code>flushCacheOnDdl</code> connection property (default <code>true</code>); set to <code>false</code> for the prior behaviour. [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4067">#4067</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4067">pgjdbc/pgjdbc#4067</a>)</li> <li>feat: add <code>connectExecutor</code> connection property to customize the <code>Executor</code> used to run the worker task that performs the connection attempt when <code>loginTimeout</code> is in effect. The value is the fully qualified name of a class implementing <code>java.util.concurrent.Executor</code>. With a null value, the default, the driver retains the prior behavior of running the connection attempt on a daemon thread named <code>"PostgreSQL JDBC driver connection thread"</code>. The executor must run the task on a thread other than the caller's. Running the attempt on a named thread lets applications that monitor driver-created threads identify it. [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4165">#4165</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4165">pgjdbc/pgjdbc#4165</a>)</li> <li>feat: add <code>classLoaderStrategy</code> connection property to control which classloaders the driver searches when loading a class named by a connection property, for example <code>socketFactory</code>. The default <code>driver-first</code> now falls back to the thread context classloader when the driver's classloader cannot resolve the class, which fixes class loading in non-flat class paths such as Quarkus and OSGi. Set <code>driver</code> to keep the previous driver-classloader-only behaviour, or <code>context-first</code> to prefer the thread context classloader [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/2112">#2112</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/2112">pgjdbc/pgjdbc#2112</a>) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4167">#4167</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4167">pgjdbc/pgjdbc#4167</a>)</li> <li>feat: add OID constants for geometric arrays, <code>RECORD</code>, and <code>refcursor</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4220">#4220</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4220">pgjdbc/pgjdbc#4220</a>)</li> <li>feat: <code>LargeObject</code> <code>BlobInputStream</code> now skips by seeking instead of reading, and the driver exposes the server version so it can select the 64-bit large-object API where available [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4204">#4204</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4204">pgjdbc/pgjdbc#4204</a>)</li> </ul> <h3>Changed</h3> <ul> <li>refactor: the worker that runs the connection attempt under <code>loginTimeout</code> is now a <code>FutureTask</code> (<code>ConnectTask</code>) instead of the hand-rolled <code>ConnectThread</code>. When the caller hits the timeout, the task is now cancelled with <code>cancel(true)</code>, which interrupts the worker thread rather than letting it run to completion. This makes the connection attempt interruptible, so <code>loginTimeout</code> can stop a slow connection attempt instead of leaking a thread. As before, a connection that the worker still manages to establish after the caller gives up is closed by the worker so that it does not leak. There are no public API changes and this should only lead to faster background resource cleanup for connections that time out. [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4120">#4120</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4120">pgjdbc/pgjdbc#4120</a>)</li> <li>chore: <code>PGXAConnection.ConnectionHandler</code> now rejects <code>setAutoCommit(false)</code> and <code>setSavepoint(...)</code> during an active XA branch, in addition to the long-rejected <code>setAutoCommit(true)</code> / <code>commit()</code> / <code>rollback()</code>. The <code>setSavepoint</code> rejection was already meant to be in place but the guard misspelled the method name as <code>setSavePoint</code>, so savepoints silently went through. Both changes bring the proxy in line with JTA 1.2 §3.4. [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4114">#4114</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4114">pgjdbc/pgjdbc#4114</a>)</li> <li>chore: <code>commitPrepared</code> / <code>rollback</code>-of-prepared now return <code>XAER_RMFAIL</code> instead of <code>XAER_RMERR</code> when the underlying connection is left in a non-idle <code>TransactionState</code>. Transaction managers (Geronimo, Narayana, Atomikos) treat <code>XAER_RMFAIL</code> as retryable on a fresh <code>XAResource</code>; the prepared transaction is no longer abandoned. [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4114">#4114</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4114">pgjdbc/pgjdbc#4114</a>)</li> <li>refactor: derive <code>getPrimaryKeys</code> from <code>pg_constraint.conkey</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4202">#4202</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4202">pgjdbc/pgjdbc#4202</a>)</li> </ul> <h3>Fixed</h3> <ul> <li>fix: the published GitHub release now ships the released <code>postgresql-<version>.jar</code> and its detached PGP signature, taken from the same signed build that is uploaded to Maven Central, instead of a leftover SNAPSHOT jar [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3812">#3812</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3812">pgjdbc/pgjdbc#3812</a>) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3814">#3814</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/3814">pgjdbc/pgjdbc#3814</a>)</li> <li>fix: simplify the <code>Statement#cancel</code> state machine by dropping the redundant <code>CANCELLED</code> state. <code>killTimerTask</code> now waits for the state to return to <code>IDLE</code> directly, which removes a spin-forever case when more than one thread observes the cancel completing [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/1827">#1827</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/1827">pgjdbc/pgjdbc#1827</a>).</li> <li>perf: defer simple-query flushes until the driver reads the response, allowing <code>BEGIN</code> and the following query to share a network flush [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3894">#3894</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3894">pgjdbc/pgjdbc#3894</a>) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4196">#4196</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4196">pgjdbc/pgjdbc#4196</a>)</li> <li>fix: <code>reWriteBatchedInserts</code> no longer throws <code>IllegalArgumentException</code> when batching a parameterless <code>INSERT</code> (for example <code>INSERT INTO t VALUES (1, 2)</code>) of 256 rows or more [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4207">#4207</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4207">pgjdbc/pgjdbc#4207</a>)</li> <li>fix: a comment before <code>CALL</code> in a <code>CallableStatement</code> no longer hides the native call, so OUT parameter registration works for <code>/* comment */ call proc(?, ?)</code> and similar. <code>Parser.modifyJdbcCall</code> now skips leading whitespace and SQL comments (both <code>--</code> and <code>/* */</code>) before the call, tolerates a trailing comment after a <code>{ ... }</code> escape, and no longer adds a spurious comma when moving an OUT parameter into a call whose arguments are only a comment [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/2538">#2538</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/2538">pgjdbc/pgjdbc#2538</a>) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4209">#4209</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4209">pgjdbc/pgjdbc#4209</a>)</li> <li>fix: <code>PreparedStatement.toString()</code> no longer throws for a <code>bytea</code> value supplied as text via <code>PGobject</code>. Hex-format values (<code>\x...</code>) are validated and rendered as a <code>bytea</code> literal, and escape-format values are quoted and cast like any other literal [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3757">#3757</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3757">pgjdbc/pgjdbc#3757</a>) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4201">#4201</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4201">pgjdbc/pgjdbc#4201</a>)</li> <li>fix: the driver no longer nulls the <code>contextClassLoader</code> of shared <code>ForkJoinPool.commonPool()</code> worker threads, which previously left unrelated tasks on those threads running with a <code>null</code> classloader [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4155">#4155</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4155">pgjdbc/pgjdbc#4155</a>) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4156">#4156</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4156">pgjdbc/pgjdbc#4156</a>)</li> <li>fix: <code>PgResultSet#getCharacterStream</code> wraps <code>String</code> in a <code>StringReader</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4063">#4063</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4063">pgjdbc/pgjdbc#4063</a>)</li> <li>fix: <code>PGXAConnection</code> no longer saves and restores the underlying connection's JDBC <code>autoCommit</code> flag. All XA-protocol SQL (<code>BEGIN</code>, <code>PREPARE TRANSACTION</code>, <code>COMMIT</code>, <code>ROLLBACK</code>, <code>COMMIT PREPARED</code>, <code>ROLLBACK PREPARED</code>, the <code>recover()</code> SELECT) is sent through <code>QUERY_SUPPRESS_BEGIN</code>, so the caller's <code>autoCommit</code> value is invariant across every <code>XAResource</code> call. Fixes the "2nd phase commit must be issued using an idle connection" failure during recovery on managed datasources that pool connections with <code>autoCommit=false</code> (TomEE, WildFly, WebSphere Liberty) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4114">#4114</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4114">pgjdbc/pgjdbc#4114</a>)</li> <li>fix: <code>PGXAConnection.prepare()</code> now mutates XA state only after <code>PREPARE TRANSACTION</code> succeeds. A failed <code>PREPARE</code> previously left the driver thinking the branch was already prepared, so the follow-up <code>rollback(xid)</code> tried <code>ROLLBACK PREPARED</code> against a non-existent gid and returned <code>XAER_RMERR</code>. Transaction managers (Narayana) escalated this to <code>HeuristicMixedException</code>. With the fix, <code>rollback(xid)</code> takes the active-branch path and issues a plain <code>ROLLBACK</code>, which the server accepts cleanly. Fixes [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3153">#3153</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3153">pgjdbc/pgjdbc#3153</a>), [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3123">#3123</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3123">pgjdbc/pgjdbc#3123</a>). [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4114">#4114</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4114">pgjdbc/pgjdbc#4114</a>)</li> <li>fix: an updatable result set over an unqualified table name is now classified using only the table visible through <code>search_path</code>. When two schemas held a table with the same name and the same primary or unique index name but a different set of key columns, the driver took the union of both schemas' columns, so the result set could be wrongly rejected as not updatable [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4214">#4214</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4214">pgjdbc/pgjdbc#4214</a>). Supersedes [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3400">#3400</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/3400">pgjdbc/pgjdbc#3400</a>).</li> <li>fix: <code>LargeObject.close()</code> now flushes a buffered output stream before marking the object closed, so closing a large object without an explicit <code>flush()</code> no longer drops buffered writes. The flush runs while the object is still open (it calls back into <code>LargeObject.write()</code>), and <code>lo_close</code> always runs afterward; a failure from <code>lo_close</code> no longer masks an earlier flush error, and the transaction is not committed when the flush failed [Issue <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4247">#4247</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4247">pgjdbc/pgjdbc#4247</a>) [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4248">#4248</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4248">pgjdbc/pgjdbc#4248</a>).</li> <li>fix: reject empty <code>timestamp</code>, <code>timestamptz</code>, and <code>date</code> text with a clear <code>SQLException</code> (SQLState <code>22007</code>) instead of an <code>ArrayIndexOutOfBoundsException</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4278">#4278</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4278">pgjdbc/pgjdbc#4278</a>)</li> <li>fix: return null <code>CHAR_OCTET_LENGTH</code> for non-character columns [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4231">#4231</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4231">pgjdbc/pgjdbc#4231</a>)</li> <li>fix: honor scale in <code>ResultSet.getBigDecimal(int, int)</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4211">#4211</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4211">pgjdbc/pgjdbc#4211</a>)</li> <li>fix: support <code>java.time</code> values in an updatable <code>ResultSet</code> <code>updateRow()</code> / <code>insertRow()</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3848">#3848</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/3848">pgjdbc/pgjdbc#3848</a>)</li> <li>fix: improve batching when the <code>RETURNING</code> clause contains <code>varchar</code> or <code>numeric</code> types [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4014">#4014</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4014">pgjdbc/pgjdbc#4014</a>)</li> <li>fix: correct <code>estimatedReceiveBufferBytes</code> accounting after a forced <code>Sync</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4014">#4014</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4014">pgjdbc/pgjdbc#4014</a>)</li> <li>fix: avoid creating a transient <code>ResultSet</code> for describe-statement purposes, and restore the pre-describe path for generated-key batches [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4014">#4014</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4014">pgjdbc/pgjdbc#4014</a>)</li> <li>fix: add an explicit failure message when a multi-statement command executes in a batch [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4014">#4014</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4014">pgjdbc/pgjdbc#4014</a>)</li> <li>fix: detect <code>search_path</code> changes case-insensitively [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4216">#4216</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4216">pgjdbc/pgjdbc#4216</a>)</li> <li>fix: auto-detect the SSL key format instead of relying on the <code>.key</code> extension [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/3946">#3946</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/3946">pgjdbc/pgjdbc#3946</a>)</li> <li>fix: build PKIX trust anchors without a <code>KeyStore</code> so FIPS JVMs work [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4193">#4193</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4193">pgjdbc/pgjdbc#4193</a>)</li> <li>fix: use <code>gssResponseTimeout</code> rather than <code>sslResponseTimeout</code> for GSS connections [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4076">#4076</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4076">pgjdbc/pgjdbc#4076</a>)</li> <li>fix: skip the autosave savepoint for <code>SET LOCAL</code> / <code>SET SESSION TRANSACTION</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4203">#4203</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4203">pgjdbc/pgjdbc#4203</a>)</li> <li>fix: do not throw <code>AssertionError</code> from <code>BatchResultHandler</code> on a closed connection [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4187">#4187</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4187">pgjdbc/pgjdbc#4187</a>)</li> <li>fix: reject <code>SQL_TSI_FRAC_SECOND</code> with an explicit, explained error [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4229">#4229</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4229">pgjdbc/pgjdbc#4229</a>)</li> <li>fix: reject a null URL in <code>Driver.acceptsURL</code> with a clear <code>NullPointerException</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4205">#4205</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4205">pgjdbc/pgjdbc#4205</a>)</li> <li>fix: reject overlong inputs in <code>NumberParser.getFastLong</code> instead of silently wrapping [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4163">#4163</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4163">pgjdbc/pgjdbc#4163</a>)</li> <li>fix: reject out-of-range and NaN values in <code>PGInterval.setSeconds</code> [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4194">#4194</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4194">pgjdbc/pgjdbc#4194</a>)</li> <li>fix: close the socket when <code>PgConnection</code> setup fails after connect [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4161">#4161</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4161">pgjdbc/pgjdbc#4161</a>)</li> <li>fix: keep the <code>LazyCleanerImpl</code> cleanup task alive across a transient empty queue [PR <a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4038">#4038</a>](<a href="https://redirect.github.com/pgjdbc/pgjdbc/pull/4038">pgjdbc/pgjdbc#4038</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/3297557c6a8059d0d6e3522c79f0bd9a6f82ee07"><code>3297557</code></a> docs: add 42.7.13 release changelog (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4270">#4270</a>)</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/d93d370984fbbccd099fc6466e4075ba46d8ec59"><code>d93d370</code></a> style: apply Autostyle to docs/ and .github/</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/2e05ff9e3ea9e8249f25dcb7017984285f1f76b1"><code>2e05ff9</code></a> build: check docs/ and .github/ formatting with Autostyle</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/b4a6087d2f07b578923a5272fa0155602ade9d40"><code>b4a6087</code></a> Adjust EditorConfig für Makefiles</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/725cebbb4e13be777483bd916b19dfcd428b5f26"><code>725cebb</code></a> fix(jdbc): reject empty timestamp/timestamptz text with a clear error</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/23a1b0dac5a8fc633cc163e883eb21f0219ea521"><code>23a1b0d</code></a> fix(scram): fail closed on channel-binding downgrade (no scram bump)</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/0b4077a529b2448cc55a6ca87b2be8667243c9ab"><code>0b4077a</code></a> Bump pgjdbc version from 42.7.12 to 42.7.13 (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4269">#4269</a>)</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/394800a38aebf54f9f293f6198e1fc5c8b19f10f"><code>394800a</code></a> fix: flush LargeObject output stream before marking closed (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4248">#4248</a>)</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/83780f130e0c83a77bb67350603f3cca8d4b9bb2"><code>83780f1</code></a> Maintain consistency with the use of the word maintainer vs comitter (<a href="https://redirect.github.com/pgjdbc/pgjdbc/issues/4234">#4234</a>)</li> <li><a href="https://github.com/pgjdbc/pgjdbc/commit/d42cad5cd12a13197e68aa53f09a7721336dce7a"><code>d42cad5</code></a> fix(jdbc): classify updatable result set by search_path visibility</li> <li>Additional commits viewable in <a href="https://github.com/pgjdbc/pgjdbc/compare/REL42.7.11...REL42.7.13">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
98bdacc706 |
build(deps): bump org.apache.pdfbox:jbig2-imageio from 3.0.4 to 3.0.5 in /app/core (#7253)
Bumps org.apache.pdfbox:jbig2-imageio from 3.0.4 to 3.0.5. [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e4c9b0410c |
build(deps): bump org.verapdf:validation-model from 1.28.2 to 1.30.2 in /app/core (#6836)
Bumps [org.verapdf:validation-model](https://github.com/veraPDF/veraPDF-validation) from 1.28.2 to 1.30.2. <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/94caa46c1a594512247fbd46c808edae39469542"><code>94caa46</code></a> Improve security of the DocumentBuilder</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/ae243fc06775ef79495accd2165e5262e31dcf67"><code>ae243fc</code></a> Fix typo</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/f89130de3b97f19ef4030e5fc84345c4463cd867"><code>f89130d</code></a> Fix getAlt in GFPDAnnot</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/11ad0c577853a658d11370277196c12e34b5fd81"><code>11ad0c5</code></a> Proposed SECURITY.md file for veraPDF projects</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/59f9cd4c97799dd87d738ce847c066530bcf2ed1"><code>59f9cd4</code></a> Use non static isCircularMappingExist</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/7494455aa44fd322fa7bd900456a3cb8f0602f18"><code>7494455</code></a> REL - v1.30</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/ecd191d3d796dfdfa13970a114920530627a25bb"><code>ecd191d</code></a> Fix glyph name detection for symbolic TrueType font</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/32e21ce19a478a4e8e47c4a090674cbb2cb58026"><code>32e21ce</code></a> Update fixRevProperty method (<a href="https://redirect.github.com/veraPDF/veraPDF-validation/issues/726">#726</a>)</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/ddfee10116d45cc37750a988ff4385dad0c8510a"><code>ddfee10</code></a> PDF/UA. Fix Table validation</li> <li><a href="https://github.com/veraPDF/veraPDF-validation/commit/22ea95799bb98b50a2063c542de50de4aeb4ce9e"><code>22ea957</code></a> RC - v1.30</li> <li>Additional commits viewable in <a href="https://github.com/veraPDF/veraPDF-validation/compare/v1.28.2...v1.30.2">compare view</a></li> </ul> </details> <br /> > **Note** > Automatic rebases have been disabled on this pull request as it has been open for over 30 days. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> |
||
|
|
18fb663b4d |
build(deps): bump bouncycastleVersion from 1.84 to 1.85 (#7132)
Bumps `bouncycastleVersion` from 1.84 to 1.85. Updates `org.bouncycastle:bcprov-jdk18on` from 1.84 to 1.85 <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html">org.bouncycastle:bcprov-jdk18on's changelog</a>.</em></p> <blockquote> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <p><!-- raw HTML omitted --><!-- raw HTML omitted -->2.2.1 Version<!-- raw HTML omitted --><!-- raw HTML omitted --> Release: 1.85, 1.85.1<!-- raw HTML omitted --> Date: 2026, July 12th</p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/bcgit/bc-java/commits">compare view</a></li> </ul> </details> <br /> Updates `org.bouncycastle:bcpkix-jdk18on` from 1.84 to 1.85 <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html">org.bouncycastle:bcpkix-jdk18on's changelog</a>.</em></p> <blockquote> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <p><!-- raw HTML omitted --><!-- raw HTML omitted -->2.2.1 Version<!-- raw HTML omitted --><!-- raw HTML omitted --> Release: 1.85, 1.85.1<!-- raw HTML omitted --> Date: 2026, July 12th</p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/bcgit/bc-java/commits">compare view</a></li> </ul> </details> <br /> Updates `org.bouncycastle:bcutil-jdk18on` from 1.84 to 1.85 <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html">org.bouncycastle:bcutil-jdk18on's changelog</a>.</em></p> <blockquote> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <p><!-- raw HTML omitted --><!-- raw HTML omitted -->2.2.1 Version<!-- raw HTML omitted --><!-- raw HTML omitted --> Release: 1.85, 1.85.1<!-- raw HTML omitted --> Date: 2026, July 12th</p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/bcgit/bc-java/commits">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> |
||
|
|
8990f55e50 |
feat(storage): encryption at rest for stored files (per-team envelope encryption) (#7155)
# Description of Changes
PR1 of the encrypt-at-rest initiative: user files stored by Stirling (My
Files, workflow files) are now AES-256 encrypted at rest across all
three storage backends, with keys that never leave the deployment.
**What was changed**
- New `EncryptingStorageProvider` decorator wraps whichever
`StorageProvider` backend is configured (local / database / S3). It
encrypts on `store` (Tink AES-256-GCM streaming AEAD, 1 MiB segments)
and transparently decrypts on `load`; legacy plaintext blobs are
detected by magic sniff and pass through untouched, so mixed state is
safe and no migration is required to enable.
- Envelope-encryption key hierarchy: each blob gets a random per-file
DEK, wrapped by a per-team KEK stored (master-key-wrapped) in a new
`file_encryption_keys` registry table; the master key resolves like the
existing credential key — `stirling.security.fileEncryptionKey`
property, `STIRLING_FILE_ENCRYPTION_KEY` env var, or an auto-generated
owner-only `file-encryption.key` in the config dir (cluster mode
requires an explicit shared key, fail-fast).
- Self-describing blob format (`SPDFEAR1` header) carrying the key id,
plaintext length, and the wrapped DEK; the header prefix is bound as GCM
associated data to both the DEK wrap and the payload, so headers cannot
be transplanted between blobs.
- Enabled via `storage.encryption.enabled=true`, gated on a
Pro/Enterprise licence — **write side only**: decryption activates
whenever key rows exist, so switching the flag off or a lapsed licence
can never make previously encrypted files unreadable.
- Key status lifecycle (`ACTIVE`/`RETIRED`/`DISABLED`): `DISABLED` is a
reversible per-team kill switch that fails closed on read; no API path
deletes key material. A revoked download surfaces as **403 Forbidden**
("access revoked"), not a 500, since it is a deliberate policy state
rather than a fault.
- Startup self-check: a master key that cannot unwrap existing key rows
refuses to boot rather than silently starting a second key hierarchy.
- S3 presigned download URLs are suppressed for decorated storage (they
would serve ciphertext); the controller already falls back to
app-streamed downloads.
- `StoredFile`/`StoredObject` gain a nullable `encryption_key_id`
(ddl-auto, no migration); persisted sizes remain plaintext sizes so
quotas and UI are unchanged.
**Why**
Enterprise security questionnaires (and HIPAA/GDPR/CMMC buyers) require
encryption at rest with documented key management; files were previously
plaintext in every backend. Design doc and vendor/standards research
(Purview, Box KeySafe, Google CSE, ISO 32000-2) informed the approach.
## Manually tested end-to-end
Beyond the automated suite, the full flow was exercised against a
running backend (local provider, `storage.encryption.enabled=true`,
login enabled) via the storage API:
1. **Startup** — master key auto-generated with the "back this up"
warning; logs `master key initialised (AES-256-GCM, fingerprint …)` and
`Storage encryption at rest active (writes encrypted)`.
2. **Encrypted at rest** — uploaded a PDF containing a known marker
string; the blob on disk (371 B vs 219 B plaintext) began with the
`SPDFEAR1` header + key id + ciphertext, contained **no `%PDF` signature
and no marker** — not openable as a PDF straight off disk.
3. **Transparent access** — downloading the file through the API
returned it **byte-identical** to the original, marker intact; stored
`sizeBytes` stayed the plaintext size.
4. **Kill switch + reversibility** — set the team key's status directly
in the DB and restarted:
- `DISABLED` → download **failed closed** (`403`, "access to this
content is revoked"), zero plaintext served.
- `ACTIVE` again → file **fully recovered, byte-identical**. Disabling
is a reversible switch on a preserved key row, not destruction.
(The 403 mapping in step 4 was added in this PR after the manual run
first surfaced it as a generic 500.)
## Coming in later PRs
- **PR2 — ops & lifecycle:** audit events for
encrypt/decrypt/key-lifecycle; admin endpoints for the kill switch
(disable/enable) and key status; a background "encrypt existing files"
migration job for turning the feature on over pre-existing plaintext;
master-key rotation (re-wrap KEK rows). Also plans a
key-backup/fingerprint verification command.
- **PR3 — admin UI:** settings section (status, per-team key list with
disable/enable), encrypted-file badge in My Files, i18n.
- **Later:** per-**source** encryption for the Processor pipeline (the
`SOURCE` key scope is already reserved in the schema); pluggable
external KMS / BYOK master-key backends (Vault, AWS/Azure/GCP KMS);
optional FIPS-validated crypto module build for CMMC; and encrypted
egress (PDF-native AES-256) for files leaving the platform.
**Reviewer notes**
- New dependency: `com.google.crypto.tink:tink:1.23.0` (Apache-2.0, pure
Java — bundled in the boot jar, no Docker changes). Pulls protobuf-java
4.33.6, which clears the Aikido-flagged CVE-2024-7254. `./gradlew
checkLicense --no-parallel` passes.
- The `file-encryption.key` file is generated in the config dir on first
use and must be backed up; losing it makes encrypted files unrecoverable
(loud log warning + fingerprint exposed for backup verification).
- Tests cover round-trips on re-openable and one-shot (S3-style)
backends, multi-segment files, legacy passthrough, decrypt-only mode,
disabled-key fail-closed (now asserting the 403 mapping), header/payload
tamper rejection, key-creation races, and presigned-URL suppression.
---
## Checklist
### General
- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### Translations (if applicable)
- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)
### 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 run `task check` to verify linters, typechecks, and tests
pass
- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing)
for more details.
|
||
|
|
46865cd154 |
Add Auto Rotate tool that detects and fixes page orientation (#7152)
## What
New **Auto Rotate** tool: give it any PDF and it detects each page's
correct orientation and sets `/Rotate` so every page displays upright.
Lossless — only the page rotation metadata changes, content is never
re-rendered.
New endpoint: `POST /api/v1/misc/auto-rotate-pdf`, plus an editor tool
registered next to Rotate.
## How it works
Two-tier detection, per page, both expressed as an additive clockwise
`/Rotate` correction:
1. **Embedded-text fast path** (`AutoRotateDetection`): dominant glyph
direction via `PDFTextStripper`/`TextPosition.getDir()`. Trusted only
with >= 30 glyphs at >= 95% agreement. Near-instant for born-digital
PDFs and needs no external tools. Correction = `(glyphDir -
pageRotation) mod 360` — the sign conventions are pinned by
parameterized fixture tests covering all text-angle x `/Rotate`
combinations.
2. **Tesseract OSD fallback**: pages the text path can't decide are
rendered at 300 DPI grayscale and run through `tesseract --psm 0`.
Corrections apply only above a confidence threshold (default 14.0,
matching OCRmyPDF's `--rotate-pages-threshold`). Rendering honours the
existing `/Rotate`, so the verdict is always additive.
**Conservative by default**: blank pages, mixed-direction pages, and
low-confidence verdicts are skipped, never guessed — the failure mode to
avoid is making a correct page wrong.
### API surface
- `detectionMode`: `auto` (default) | `text` | `osd` — forcing one
method is useful for testing
- `confidenceThreshold`: minimum OSD confidence to apply a correction
- `dryRun=true`: returns a JSON per-page report instead of the PDF
- `pageRotations={"1":90,...}`: applies precomputed corrections without
detection
The frontend uses analyze-then-apply (dryRun, then pageRotations) so
detection runs exactly once per file, and the analysis report can be
shown in the UI.
### UI
The tool's results panel shows a **detection report** for
debugging/tuning: per page — method badge (Text / OCR / Skipped),
confidence score (glyph-dominance % for text, raw OSD score for OCR),
applied rotation, and a skip reason (too little text, mixed directions,
below threshold, OCR not installed...). Settings expose detection mode
and the OSD threshold.
### Dependency handling
Registered in `PageOps` only — deliberately **not** gated on the
`tesseract` group, because the text path works without Tesseract. The
controller checks `isGroupEnabled("tesseract")` at runtime; when it's
missing, scanned pages are skipped with a visible `tesseractUnavailable`
note instead of the whole tool disappearing.
## Testing
- 14 detection unit tests: all text-angle x `/Rotate` fixture
combinations (pins the direction conventions), dominance/glyph-count
guards, OSD output parsing
- 6 controller tests: dryRun report, correction application, explicit
pageRotations, tesseract-unavailable reporting, input validation
- Frontend: typecheck (core/desktop/proprietary), ESLint, Prettier, all
i18n audit tests
- **Live, text path**: fixture with pages at `/Rotate` 0/90/180/270 ->
all pages return upright; report UI verified in the browser
- **Live, OSD path**: image-only "scan" fixture (no text layer) with
pages upright/180/90 -> all detected by OSD at conf ~15-17 and
corrected; closed-loop re-analysis of the output reports 0 pages to
rotate with *higher* confidence than the input
Out of scope: skew correction (that's the OCR tool's `--deskew`); this
fixes 90-degree-multiple orientation only.
|
||
|
|
3bee6d212e |
Change pipelines to have 1 input and 1 output (#7121)
# Description of Changes Change pipelines so that sources and triggers are grouped into a list of inputs, so you can have a different trigger for each source in the list. This is necessary because triggers are not universally supported by all source types. If you wanted to have a pipeline pull from both a folder and an S3 bucket, the current system allows you to choose "Folder Watch" as the trigger, which will either do nothing or crash when it's paired with the S3 bucket. I've got reservations about actually allowing different triggers for every source because it allows for user workflows that I don't believe exist, like "I want this folder to be polled every minute and this other one to be polled every hour, but they should run the same tools and should output to the same place". Because of this (with agreement from Connor, Anthony and Matt) I've changed this PR to artificially limit pipelines to having 1 input & output at this stage. The backend is still shaped to support multiple inputs & outputs so it should be trivial to re-add support for them in the future if we decide we want to, but the UI can be much simpler and easier to understand with just 1 input and output. <img width="1262" height="521" alt="image" src="https://github.com/user-attachments/assets/809e6803-9f99-436d-9aeb-52dddf0906ff" /> |
||
|
|
b4a264239c |
fix(saas): provision a new user and their personal team atomically (#7193)
New SaaS accounts were landing with `team_id = null`. That state is unrecoverable: portal access derives from leading a team, and signup is the only place one is assigned. Five things had to be fixed, all on the signup path. Only the last is a behaviour change you'd notice. ### 1. Shared-PK entity was routed to `merge()` `SaasUserExtensions` pre-sets its `@MapsId` id in the constructor, so Spring Data's id-nullness check treated a brand-new row as existing and `save()` failed with `AssertionFailure: null identifier`. Now implements `Persistable` and decides on the creation timestamp — the idiom already used by `ProcessedFileEntity` and `SourceDocCountEntity`. This was the blocker. It threw on every signup, and because the failure was swallowed (see 3) every new account was stranded. ### 2. User and team were committed separately `createUser()` is annotated `@Transactional` but is called as `this.createUser(...)`, and self-invocation bypasses the proxy — so the annotation did nothing. `saveUser()` and `ensurePersonalTeam()` each committed in their own transaction, leaving a window where a **committed user was visible with `team_id = null`**. Parallel requests entering that window each provisioned a team, producing duplicates (observed: teams 160/161 and 162/163 for one user). Both writes now happen in one transaction via `SaasTeamService.saveUserWithPersonalTeam()`. The window is gone, so there is nothing left to race over. ### 3. A failed team create was swallowed The old code logged at WARN and committed the user anyway. It now propagates: the shared transaction rolls the user back, the request 401s, and a retry starts clean. Nothing half-built is committed. This is the deliberate trade — a transient failure now surfaces instead of silently producing an account that can never reach the portal. ### 4. Per-request healing removed `recoverMissingTeam` (added in #7180) ran on **every authenticated request** whose user had no team, with no mutual exclusion. Under a burst of parallel requests it was itself a source of concurrent provisioning. Provisioning belongs to signup alone. ### 5. Policy seeding could not run `@TransactionalEventListener(AFTER_COMMIT)` leaves the *completed* transaction bound to the thread, so `JpaPolicyStore.save`'s `@Transactional` joined it instead of opening a live one — and its `FOR UPDATE` lock threw `TransactionRequiredException`. Now seeded in `BEFORE_COMMIT`: the lock has a live transaction, rollback safety is unchanged (a rolled-back team still leaves no policy), and it stays on a single pooled connection. ## Verified `:saas:test` green, both spotless gates green, on top of current `main`. Manually on a live signup: **one** team per user, and the concurrent-signup race resolves correctly through the pre-existing unique-constraint catch (`users_supabase_auth_id_key` violation → refetch the winner). 12 filter tests needed updating. Two of them asserted behaviour this PR deliberately removes (`personalTeamFailureSwallowed`, `assignsTeamWhenMissing`), so they were rewritten to assert the new contract rather than re-stubbed into passing. ## Not in scope - **Existing stranded accounts** are not repaired — with the healer gone, nothing fixes them on the request path. They need a one-off backfill or deletion. - **A DB-level invariant.** A partial unique index (`UNIQUE(created_by_user_id) WHERE is_personal`) would make duplicate personal teams impossible rather than merely unreachable. Wanted, but it is a Supabase migration in the SaaS repo, so it is deliberately separate. - **Per-request auth cost.** The filter still does two remote-Postgres round-trips per authenticated request; a frontend request storm makes that expensive. Being handled separately. |
||
|
|
999b5e5995 |
Add persistent outputs to Processor (#7071)
# Description of Changes <img width="1270" height="487" alt="image" src="https://github.com/user-attachments/assets/64894e2b-aab9-42ab-96c2-2c11ba427b52" /> Change policies to point towards a source for its output instead of a dynamically defined output location for the pipeline. This allows for easy reuse of outputs in different pipelines and makes it impossible to break complex pipelines by accidentally updating the source but not the output and vice versa. Also makes outputs a list to match the inputs, so it's possible for a pipeline to output to multiple locations. We should consider whether we want to continue calling these Sources since they're now being used as both inputs and outputs, but that decision is beyond the scope of this PR. Also updates the existing S3 DB migration script and adds a new one to migrate to the new schema. Neither of these scripts are possible with SQL since it involves parsing and restructuring JSON. I've updated them so that they only ever run once on startup and mark themselves as completed. |
||
|
|
66b80a80c0 |
fix(saas): personal teams must be allowed to share a name (new signups get no team) (#7180)
## The bug
Every SaaS signup after the very first one is created with `team_id =
NULL`, no team membership and no `home_team_id`. A brand-new account:
```
user_id | username | team_id | authenticationtype | home_team_id | memberships
952 | hedewot627@candaba.com | null | web | null | null
```
Since #7070 derives Processor access from leading a team, these accounts
are silently redirected out of the Processor and back to the editor.
## Cause
`SaasTeamService.createPersonalTeam` names every personal team the
literal `"My Team"`, and `stirling_pdf.teams.name` was unique — so the
insert throws a duplicate-key error for the second account onwards. Team
creation is best-effort (caught, logged at WARN), so the account is
created anyway, permanently team-less.
Migration `20251211000000` had already dropped that constraint for
exactly this reason, but it dropped it **by name** while the entity
still declared `@Column(unique = true)`. With Flyway retired for `:saas`
(#7100), `ddl-auto=update` reconciles the schema — so Hibernate
re-created the constraint on the next boot under a generated name the
old `DROP` could never match.
The data bug predates #7070; that PR only made it visible.
## Changes
- **`Team.name` no longer unique.** `TeamController` already enforces
uniqueness for admin-created teams (`existsByNameIgnoreCase` on create
and rename, 409), so nothing user-facing changes. `findByName` is only
used for the `Default`/`Internal` system teams.
- **Existing team-less accounts recover on authentication.** Signup is
the only other place a team is assigned and nothing back-fills
`team_id`, so without this they stay locked out. Guests excluded by
design; healthy accounts short-circuit on a null check (`team` is
`EAGER`).
- **Tests:** team recovered, existing team untouched, guest stays
team-less.
## Deploy order
Needs `20260806000000_teams_name_drop_unique` (SaaS repo, `v3`) to drop
the constraint from the live schema — **deployed after this**, or
Hibernate re-adds it on the next boot.
## Verification
`:saas:compileJava`, `:proprietary:compileJava`, spotless on both, and
the `:saas` team/auth-filter tests (`TeamRecovery`: 3 tests, 0
failures).
|
||
|
|
ba404d3f90 |
PAYG bundle: server-authoritative price (inline amount_off coupon) + #7032 review nits (#7156)
## What Follow-up to #7032. Makes the prepaid-bundle price **server-authoritative** and removes the percent-coupon rounding drift, by switching the 12-for-10 discount from a pre-made `percent_off` Stripe coupon to an **edge-function-computed inline `amount_off` coupon**. Also folds in Ethan's #7032 review nits. This is a money-mechanism change, so it was verified against the Deno tests and is ready for a V2-preview check before rollout. ## SaaS side — already on `v3` (purely additive) The edge fn + migration were pushed **directly to `v3`** (commit `4534ff1c1`), since the DB change is purely additive (a backward-compatible function replacement — no table/column/data changes): - `create-payg-bundle-quote`: retrieves the Stripe Price for the bundle, computes `subtotal = unit_amount x pool_credits` (falls back to `round(unit_amount_decimal x pool_credits)`), `discount = round(subtotal x 2 / 12)`, `total = subtotal - discount`; mints a single-use fixed-amount coupon (`amount_off`, `duration: once`, `max_redemptions: 1`, `redeem_by = valid_until`) and applies it instead of the stored percent coupon; persists `total` via `p_price_minor`. - Migration `20260803000000_payg_bundle_quote_stripe_price_minor.sql`: `payg_set_bundle_quote_stripe` gains `p_price_minor BIGINT DEFAULT NULL` → `price_minor = COALESCE(p_price_minor, price_minor)`. **Deploy choreography (important):** the migration must apply **before** the edge fn is deployed — the fn now calls the 4-arg `payg_set_bundle_quote_stripe`. #7032's own Supabase migration is already on `main`/`v3`. ## This PR (FE) - **Server-authoritative price:** `bundlePriceMinor` now computes `subtotal - round(subtotal x (granted-paid)/granted)` (round the discount, then subtract) — identical to the edge fn — so the pre-mint estimate matches the `amount_off` charged, and the persisted/frozen total, to the penny (they previously diverged by a minor unit on exact-half ties). Tie-case test added. ### Ethan's #7032 review nits - **1** — comments in `ActivationChoiceModal` / `FreePlanView` no longer assert the metered subscription is auto-provisioned off the saved card; they describe it as a known, not-yet-wired follow-up. - **2** — corrected the price-authority narrative (`stripe.ts`, `BundleCheckoutModal`): the client-sent `p_price_minor` is a pre-mint **display estimate only**; the edge fn overwrites `price_minor` with the server total once the quote is minted. **Verified** the edge fn builds the Stripe line from `bundle_price_id x pool_credits` with `amount_off` from the retrieved Price — it never uses the client price. - **4** — `ensureStripeQuote`'s reuse key now includes the posture/size/pipeline ids (`buildStripeQuoteSig`), not just pool+PO, so a same-pool sizing edit re-mints and re-persists instead of leaving stale sizing on the row. - **5** — `SpendLimitPicker`: a cleared field (maps to `0`) can no longer proceed as a `$0` cap — the cap-step Continue is disabled and `handleContinue` guards on it (empty = incomplete, distinct from the explicit `null` "No limit"). - **6** — `"prepaid PDFs"` code fallbacks aligned to the `"prepaid credits"` TOML (`usageMeters`, `PrepaidCapacityCard`). ## Testing - SaaS Deno: **25/25** (coupon `amount_off == round(subtotal*2/12)`, `p_price_minor == total` persisted, `unit_amount_decimal` fallback, exact-half tie, zero-discount path, price/coupon failure paths). - FE vitest: **50** billing/format tests pass; prettier + eslint clean; tsc clean for all changed files. - Pending: manual V2-preview check that the invoice shows a concrete `-$X.00` discount line (labelled "12 months for the price of 10") equal to the in-app total. Closes the residual half of #7032 review finding #2 — once merged/deployed, the in-app total, the persisted value, and the Stripe invoice all agree. --------- Co-authored-by: Reece Browne <74901996+reecebrowne@users.noreply.github.com> |
||
|
|
3813ca360e |
PAYG prepaid usage bundles (#7032)
# Prepaid usage bundles Teams on pay‑as‑you‑go can **buy a year of PDF processing up front, at a discount** — *"12 months for the price of 10."* You pre‑buy a pool of credits; they're spent **before** any metered billing and sit **outside** the monthly spend limit; unused capacity expires after 12 months. --- ## What this PR delivers **Buy → quote → invoice → pay (Stripe‑Quotes‑native).** - A team lead sizes the pool in the calculator (persisted as a quote row), which doubles as the quote page with a **"Download quote (PDF)"** — the PDF is **Stripe's own rendered quote** (same mechanism procurement uses), not an app‑generated document. - **Finalise** turns the accepted quote into an invoice; the lead can **download the invoice** or **pay online** (Stripe hosted invoice). **Card and bank‑transfer / PO** are both supported (payment‑method fork), on net terms. - The billing page loads the in‑flight quote/invoice on open, so the CTA resumes the right step (**View quote** / **Pay invoice to complete**) and offers **Cancel purchase** (voids the invoice + quote and restarts). **Prepaid is usable on its own — no subscription required.** The entitlement gate honours a live prepaid pool in both cases: - *Unsubscribed*: once the one‑time free grant is spent, a live pool keeps the team **fully entitled** (all feature gates) rather than degraded. - *Subscribed*: a team **at/over its metered cap** but holding a live pool stays fully entitled — prepaid draws are netted out of metered spend, so the pool genuinely sits outside the cap. Only when the free grant **and** the prepaid pool are both empty do billable categories stop. **Coordinated SaaS change (ships with this — `Stirling-PDF-SaaS` `v3` branch):** the `invoice.paid` webhook credits the pool idempotently (keyed on the invoice id) and settles the quote. Metered‑subscription provisioning is best‑effort and **classified** — a permanent Stripe 4xx (the single‑use hosted‑invoice card can't be attached) is a claimed no‑op (HTTP 200, no retry) so Stripe doesn't redeliver forever; only transient errors (5xx / connection / rate‑limit) retry. A failed credit now retries rather than silently dropping a paid bundle. ### Flow 1. Lead sizes the pool and agrees to the terms → the browser sends team + capacity + consent, **never a price**. 2. A leader‑gated server function looks up the price and creates a Stripe **quote** (line quantity = capacity). 3. Lead **finalises** → the quote becomes a Stripe **invoice**; download it or pay online (card or bank transfer). 4. On `invoice.paid`, the webhook **credits the prepaid pool** (idempotent) and settles the quote. 5. Usage then draws **free grant → prepaid pool → meter**; the pool is usable with no subscription. <img width="1280" height="920" alt="01-activation-fork" src="https://github.com/user-attachments/assets/e8981dc7-809d-4fc5-bfde-71e619096b7b" /> <img width="1280" height="920" alt="02-calculator" src="https://github.com/user-attachments/assets/81f6b886-1797-4c54-ba18-97d126be78e2" /> <img width="1120" height="600" alt="03-free-plan" src="https://github.com/user-attachments/assets/c4057fde-d965-47dd-93e8-f2f0f612aa73" /> <img width="1105" height="1285" alt="04-subscribed-prepaid" src="https://github.com/user-attachments/assets/9d1d0ef1-29d0-4bd4-9c17-ac5f68f88ca8" /> --- ## In a follow‑up (not this PR) 1. **Authoritative price via an inline fixed‑amount coupon** *(in progress in a separate PR).* Replace the percentage 12‑for‑10 coupon with an edge‑function‑computed **`amount_off`** coupon: the invoice shows a concrete "−$X.00" discount line, the total is deterministic (no percentage‑rounding drift), and the persisted price becomes **server‑authoritative**. Money‑mechanism change — needs validation against the Stripe test env, so it warrants its own testable PR. 2. **Metered auto‑resume when the pool empties.** Save the paying card at invoice time (`setup_future_usage`) for card payers → real `charge_automatically`; a cardless `send_invoice` subscription for bank‑transfer / PO. This makes the "processing continues at the metered rate" promise true for everyone. 3. **Provisioning idempotency hardening** (SaaS repo). Idempotency key on subscription creation + a conditional link RPC, so a webhook redelivery or link‑RPC failure can't create duplicate or orphaned subscriptions. 4. **Repo‑wide "credits" copy** across *all* of usage & billing (this PR only makes its own additions consistent). --- ## Known edges (current state) - **Cardless teams degrade when the pool empties.** An unsubscribed bundle team that runs the pool dry hits DEGRADED (metered paused), not automatic metered continuation — because no metered subscription gets provisioned off a hosted‑invoice card. The consent copy states processing "continues at the metered rate"; that promise is intentionally **ahead of the mechanism** (follow‑up 2), and the 12‑month term is the runway to deliver it. The prepaid capacity itself stays fully usable in the meantime. - **In‑app total vs charge can differ by ≤1¢** until follow‑up 1 lands. The **shared approval document (the Stripe quote PDF) and the actual invoice are already Stripe‑authoritative**; the persisted price shown in‑app is still a front‑end estimate (percentage‑coupon rounding), so it can differ from Stripe by a rounding cent. Resume‑time drift is fixed (frozen to persisted); exact‑to‑the‑penny parity arrives with the authoritative‑price follow‑up. - **Provisioning idempotency is latent, not live.** The duplicate/orphan‑subscription window only becomes reachable once card‑linking (follow‑up 2) makes provisioning actually run; hardening is tracked as follow‑up 3. - **One job can overshoot the spend cap via a near‑empty pool.** A subscribed team that has hit its metered cap but still holds a *nearly‑exhausted* pool is let through (the pool overrides the cap gate); if a job needs more than the pool has left, the pool drains to zero and the **remainder meters**, so that single job's remainder can bill just past the "never past your spend limit" ceiling. Bounded to one job's overshoot and only at the pool's tail; the alternative — blocking the job — would strand paid‑for capacity, so this is a deliberate trade. --- ## Testing - **Java** — `EntitlementServiceTest` (18) incl. unsubscribed‑live‑pool‑stays‑FULL, subscribed‑over‑cap‑with‑pool‑stays‑FULL, and lazy‑read guards. - **Frontend** — `useBundleFlowState` + `Usage` render tests; portal & SaaS `tsc`; i18n audit; `lint:colors`; toml‑sort; prettier. - **SaaS webhook** (`v3`) — Deno tests for terminal‑vs‑transient provisioning classification (rate‑limit treated as retryable), credit‑error‑retries, and an end‑to‑end no‑storm assertion on the unusable‑card path. *Preview:* the checkout runs in a Supabase function in `Stirling-PDF-SaaS` (`v3`); a live V2 preview is linked in the auto‑deploy comment below. Screenshots to be refreshed — the checkout modal changed since the originals. |
||
|
|
29002d0b82 |
Improve UX around source folders in Processor (#7101)
# Description of Changes Adds implicitly defined folders to the list of locations that folder sources can look in, including the legacy watchedFolder folders, and the server storage location (if enabled). Also adds a settings UI for defining the list of allowed folders instead of having to manually edit `settings.yml` (please excuse the styling, that's the standard styling of the Processor, hoping it gets fixed by one of the styling PRs). <img width="888" height="786" alt="image" src="https://github.com/user-attachments/assets/cf6d0705-adcf-463c-8e80-6901a652068b" /> <img width="1103" height="713" alt="image" src="https://github.com/user-attachments/assets/b7244592-249a-4149-994f-3a2b750f25f9" /> |
||
|
|
1e2895a79f |
Add external-API integrations plus pipeline steps (#7098)
New Generic API mode with examples and integrations setup around it - Adds an integration operations catalogue so external-API connections (e.g. Microsoft Purview) can be used as policy pipeline steps - New generic external-API step calls a configured connection during a policy run, with a verdict gate to pass/fail documents on the response - Purview sensitivity-labelling step applies labels to processed documents, gated behind the Purview connection being configured (WIP to be changed later) --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
8de94ff152 |
Ai customization settings (#7069)
# Description of Changes AI settings customisation in settings menu, as part of this also tested and fixed ollama and other 3rd party AI integrations - Adds an admin AI settings UI for customizing AI behaviour, including per-provider model and API-key configuration - Backend pushes AI config changes to the Python engine at runtime via a config-push bridge, so changes apply without a restart - Config-push is gated off in SaaS; engine now drains background tasks on shutdown instead of cancelling them --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
b3875d3149 |
Add heuristic classification (#7050)
# Description of Changes - Adds a non-AI heuristic classification engine that classifies documents client-side in the browser when AI is disabled - Classification is billed as a policy run via a fast, non-blocking meter endpoint; a default Classification policy is seeded per team - Enables the policy engine by default --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
357eb77f94 |
Portal: multiple named personal API keys with per-key usage tracking (#6961)
# Description of Changes Multiple named **personal** API keys per user, replacing the single opaque per-user key. - Create (name + one-time secret), list, and revoke named keys from the portal Infrastructure → API Keys tab. Works self-hosted and SaaS (`X-API-KEY`). - Per-key usage stats (today / trailing 30 days / lifetime); API-processed documents are attributed to the specific key in the processor's Documents feed. - The legacy single per-user key keeps working and is lazily represented as a named key. Rotating it revokes its migrated shadow row so the old secret stops authenticating. - Per-user (not per-key) rate limiting plus a per-user active-key cap, so minting keys can't multiply the daily quota. Name-length cap; race-safe migration and usage recording. Keys are strictly personal: one owner, full access, no sharing. Team-shared / scoped keys and per-key access levels were intentionally left out of this PR to keep it small and easy to review; they can follow as a separate, focused change. > Note: the screenshots from the original revision showed an earlier team-scoped design and need refreshing. --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. --------- Co-authored-by: EthanHealy01 <80844253+EthanHealy01@users.noreply.github.com> |
||
|
|
3bf0019d7c |
Webhook policy source (#7051)
# Description of Changes Create custom webhooks as a source, allows file pushes toa custom made endpoint with custom auth ID - Adds webhook as a policy source: external systems push documents to a receiver endpoint, which stages the files locally and triggers the policy run - Requests are authenticated with HMAC signatures; receiver hardened with bounded body reads and server-minted IDs - Uses the same team-scoped IntegrationConfig connection model as the S3 source, with matching portal UI (source type, icon, wizard) - Includes a policies-gated Cucumber feature covering the receiver end-to-end --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
b5b9cc443f |
build(deps): bump com.github.junrar:junrar from 7.5.10 to 7.6.0 in /app/common (#7090)
Bumps [com.github.junrar:junrar](https://github.com/junrar/junrar) from 7.5.10 to 7.6.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/junrar/junrar/releases">com.github.junrar:junrar's releases</a>.</em></p> <blockquote> <h2>Release v7.6.0</h2> <h2>Changelog</h2> <h2>🚀 Features</h2> <ul> <li>support random access for files in solid RAR4 archives (<a href="https://github.com/junrar/junrar/commits/e0874d2">e0874d2</a>)</li> </ul> <h2>🏎 Perf</h2> <ul> <li>replace RarCRC.checkCrc with java.util.zip.CRC32 (<a href="https://github.com/junrar/junrar/commits/5270d23">5270d23</a>)</li> </ul> <h2>🛠 Build</h2> <p><strong>deps</strong></p> <ul> <li>bump gradle-wrapper to 9.5.1 (<a href="https://github.com/junrar/junrar/commits/cb4b7fd">cb4b7fd</a>)</li> <li>bump com.fasterxml.jackson.core:jackson-databind (<a href="https://github.com/junrar/junrar/commits/0bb56b3">0bb56b3</a>)</li> <li>bump com.fasterxml.jackson.datatype:jackson-datatype-jsr310 (<a href="https://github.com/junrar/junrar/commits/ca621b2">ca621b2</a>)</li> <li>bump org.jreleaser from 1.23.0 to 1.24.0 (<a href="https://github.com/junrar/junrar/commits/90f0548">90f0548</a>)</li> <li>bump commons-io:commons-io from 2.21.0 to 2.22.0 (<a href="https://github.com/junrar/junrar/commits/83a5d08">83a5d08</a>)</li> <li>bump com.github.ben-manes.versions from 0.53.0 to 0.54.0 (<a href="https://github.com/junrar/junrar/commits/d5abcdb">d5abcdb</a>)</li> </ul> <p><strong>unscoped</strong></p> <ul> <li>replace deprecated action (<a href="https://github.com/junrar/junrar/commits/338efcb">338efcb</a>)</li> </ul> <h2>Contributors</h2> <p>We'd like to thank the following people for their contributions: Gauthier, Gauthier Roebroeck, Robin Schimpf</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/junrar/junrar/blob/master/CHANGELOG.md">com.github.junrar:junrar's changelog</a>.</em></p> <blockquote> <h1><a href="https://github.com/junrar/junrar/compare/v7.5.10...v7.6.0">7.6.0</a> (2026-05-13)</h1> <h2>🚀 Features</h2> <ul> <li>support random access for files in solid RAR4 archives (<a href="https://github.com/junrar/junrar/commits/e0874d2">e0874d2</a>)</li> </ul> <h2>🏎 Perf</h2> <ul> <li>replace RarCRC.checkCrc with java.util.zip.CRC32 (<a href="https://github.com/junrar/junrar/commits/5270d23">5270d23</a>)</li> </ul> <h2>🛠 Build</h2> <p><strong>deps</strong></p> <ul> <li>bump gradle-wrapper to 9.5.1 (<a href="https://github.com/junrar/junrar/commits/cb4b7fd">cb4b7fd</a>)</li> <li>bump com.fasterxml.jackson.core:jackson-databind (<a href="https://github.com/junrar/junrar/commits/0bb56b3">0bb56b3</a>)</li> <li>bump com.fasterxml.jackson.datatype:jackson-datatype-jsr310 (<a href="https://github.com/junrar/junrar/commits/ca621b2">ca621b2</a>)</li> <li>bump org.jreleaser from 1.23.0 to 1.24.0 (<a href="https://github.com/junrar/junrar/commits/90f0548">90f0548</a>)</li> <li>bump commons-io:commons-io from 2.21.0 to 2.22.0 (<a href="https://github.com/junrar/junrar/commits/83a5d08">83a5d08</a>)</li> <li>bump com.github.ben-manes.versions from 0.53.0 to 0.54.0 (<a href="https://github.com/junrar/junrar/commits/d5abcdb">d5abcdb</a>)</li> </ul> <p><strong>unscoped</strong></p> <ul> <li>replace deprecated action (<a href="https://github.com/junrar/junrar/commits/338efcb">338efcb</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/junrar/junrar/commit/cb4b7fdb84269e95741fa22120e08ceb15a06152"><code>cb4b7fd</code></a> build(deps): bump gradle-wrapper to 9.5.1</li> <li><a href="https://github.com/junrar/junrar/commit/0bb56b3df4e3b6d14f73de92746a2eb47dd2d7a3"><code>0bb56b3</code></a> build(deps): bump com.fasterxml.jackson.core:jackson-databind</li> <li><a href="https://github.com/junrar/junrar/commit/ca621b22421f9e846df23112b32113230610c8d9"><code>ca621b2</code></a> build(deps): bump com.fasterxml.jackson.datatype:jackson-datatype-jsr310</li> <li><a href="https://github.com/junrar/junrar/commit/e0874d213832bbeaa8eb265c79479b01b5fa7392"><code>e0874d2</code></a> feat: support random access for files in solid RAR4 archives</li> <li><a href="https://github.com/junrar/junrar/commit/90f0548c728b0bf2e94f6e26f9e6396d9d0a7262"><code>90f0548</code></a> build(deps): bump org.jreleaser from 1.23.0 to 1.24.0</li> <li><a href="https://github.com/junrar/junrar/commit/83a5d085dc84991226229fef1515cc902d129241"><code>83a5d08</code></a> build(deps): bump commons-io:commons-io from 2.21.0 to 2.22.0</li> <li><a href="https://github.com/junrar/junrar/commit/338efcb5472be051989fbafd7179d561c71722eb"><code>338efcb</code></a> ci: replace deprecated action</li> <li><a href="https://github.com/junrar/junrar/commit/5270d235ade54d96dfc9958ab06f495fbbd169e7"><code>5270d23</code></a> perf: replace RarCRC.checkCrc with java.util.zip.CRC32</li> <li><a href="https://github.com/junrar/junrar/commit/d5abcdb9af988ddfa76e98d80787e15525488332"><code>d5abcdb</code></a> build(deps): bump com.github.ben-manes.versions from 0.53.0 to 0.54.0</li> <li><a href="https://github.com/junrar/junrar/commit/edadb28896962fdcf754029bafd0994aaff24530"><code>edadb28</code></a> chore(release): 7.5.10 [skip ci]</li> <li>See full diff in <a href="https://github.com/junrar/junrar/compare/v7.5.10...v7.6.0">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ludy <Ludy87@users.noreply.github.com> |
||
|
|
e24a30828b |
refactor(api): replace deprecated APIs with their modern equivalents (#6434)
# Description of Changes This PR resolves deprecation warnings and addresses compiler errors resulting from the transition to Spring Security 7.x., as well Jackson 3 and general Java. * Replaced all usages of `asText()`/`isTextual()` with `asString()`/`isString()` in JSON parsing logic across `FormPayloadParser.java`, `ApiEndpoint.java`, and `KeygenLicenseVerifier.java` to ensure consistent and type-safe string * Updated `CustomSaml2AuthenticatedPrincipal` to implement `Saml2ResponseAssertionAccessor`, added a `responseValue` field, and provided additional getter methods and type-safe attribute accessors. * Switched from constructing `URL` objects directly from strings to using `URI.create(...).toURL()` in `UIDataTessdataController.java` for improved URL safety and parsing. <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
e2ea720fc8 |
refactor(api): replace regex literals with compiled patterns for improved performance and readability (#6511)
# Description of Changes This pull request refactors several utility classes and controllers to replace inline regular expression usage with precompiled `Pattern` constants. This change improves performance, consistency, and maintainability by ensuring that regex patterns are compiled only once and reused throughout the codebase. Additionally, it enhances code clarity and security in filename and SQL content sanitization. <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [X] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [X] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [X] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [X] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. --------- Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> |
||
|
|
621731bda1 |
Validate RFC 3161 document timestamps and expose timestamping (#7095)
# Description of Changes Fixes timestamp issue and adds timestamp to the signing/security policiy --- ## 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/devGuide/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/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### 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 run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details. |
||
|
|
5e3e89ccb2 |
Fix existing teams logic (#7070)
# Description of Changes Paired with https://github.com/Stirling-Tools/Stirling-PDF-SaaS/pull/320 Fixes the following bugs we found when testing the SaaS release: - Existing users couldn't join teams - this was because they were the last leader of their team, so it'd be left orphaned). Users now have a 'home team', which can have no members if they join another team, but they can then go back to it later. - Existing leaders didn't have unlimited seats - `saas_teams_extensions` had no row for them, so the app fell back to `max_seats=1`. The migration script fixes it. - Members without Processor access could still access the Processor - It was just checking "Are you the leader of **any** team", instead of the user's active team. |
||
|
|
f60a75c253 |
chore(saas): remove unused Flyway migration system (#7100)
## What this PR does Removes the **Flyway** migration system from the SaaS build: - drops `flyway-core` + `flyway-database-postgresql` from `app/saas/build.gradle` - deletes all `Vxx__*.sql` files under `app/saas/src/main/resources/db/migration/` - removes the `spring.flyway.*` config from `application-saas.properties` - clears the now-inert `SPRING_FLYWAY_ENABLED` override and stale Flyway comments in `testing/compose/docker-compose-saas.yml` `ddl-auto` is **left on `update`** (unchanged) — that's a separate decision (see "Not in this PR"). ## Why — Flyway never actually ran anywhere From a full schema-management review (`notes/FLYWAY_MIGRATION_REVIEW.md`), verified against the live databases: - **Prod & dev (v3):** no `flyway_schema_history` table exists in any schema → Flyway has never executed. Schema is authored by the Supabase migrations in `Stirling-PDF-SaaS` and applied by that repo's **GitHub integration** (merge to `main` → prod). - **Tests:** the saas module has zero `@SpringBootTest`; the only real-DB integration tests (in `proprietary`) use `ddl-auto=create-drop` and don't have Flyway on the classpath. - **The mock-DB harness** (`testing/compose/docker-compose-saas.yml`, PAYG cucumber) *explicitly disabled* Flyway, because the `Vxx` migrations can't run against a clean Postgres — they assume Supabase has already provisioned `users`/`teams` (`V2` ALTERs `users`, `V5` references `teams`). So Flyway was dead weight, and its 4 duplicate versions (`V25/V26/V30/V31`) were a latent trap: re-enabling it would crash boot on the collision. Everything it contained (schema + seeds like the default pricing policy) is already mirrored by the Supabase migrations, and by `saas-seed.sql` for the cucumber stack. ## ⚠️ Required follow-up (item #1) — capture the Flyway-only tables into Supabase migrations **This is documentation of the next step, not done in this PR.** Seven tables were defined in Flyway with **no matching Supabase migration**. They exist in prod today only because `ddl-auto=update` created them from their entities. Before `ddl-auto` is ever tightened to `validate` (see #3 below), and so any fresh Supabase branch is complete, they must be added as Supabase migrations in `Stirling-PDF-SaaS/supabase/migrations/`. **Capture (CREATE) — 6 live tables** (definitions are visible in the deleted files in this PR's diff): | Table | Source (deleted here) | Backing entity | |---|---|---| | `resource_grants` | `V25__resource_grants.sql` | `ResourceGrant` | | `integration_configs` | `V26__integration_configs.sql` | `IntegrationConfig` | | `policy_sources` | `V22__policy_engine_tables.sql` | `SourceEntity` | | `policy_source_doc_counts` | `V23__policy_source_doc_counts.sql` | `SourceDocCountEntity` | | `policy_source_doc_totals` | `V23__policy_source_doc_counts.sql` | `SourceDocTotalEntity` | | `saas_user_extensions` | `V9__saas_user_team_extensions.sql` | `SaasUserExtensions` | Write them as `CREATE TABLE IF NOT EXISTS stirling_pdf.<name> (...)` (idempotent — no-op against the existing prod/v3 tables). Preserve column types/defaults/constraints from the deleted `Vxx` files. **Drop (do NOT recreate) — 1 orphaned table:** - `classification_labels` — created by `V30` and dropped by `V39` within Flyway; its `ClassificationLabel` is now a plain `record`, not a JPA entity. It lingers in prod only because Flyway's `V39` drop never ran. The follow-up should emit a `DROP TABLE IF EXISTS stirling_pdf.classification_labels` (mirroring `V39`'s intent), after confirming nothing reads it. ## Not in this PR (deliberately) - **#3 — flip saas `ddl-auto` `update` → `validate`.** Held pending team confirmation; it has boot-risk and should be gated by a CI "validate-boot against a fresh Supabase branch" first. Self-hosted stays on `update` regardless. - **#4 — `billing_subscriptions` split-brain** (prod `public` has 23,164 rows, `stirling_pdf` has 0, Java reads the empty one). Tracked separately. ## Verification - `:saas:compileJava` succeeds with Flyway removed (no code imports `org.flywaydb.*`). - No test or ArchUnit rule references Flyway or the migration files. - No runtime/data impact: Flyway never ran against any live database. --------- Co-authored-by: James Brunton <jbrunton96@gmail.com> |