Commit Graph

110 Commits

Author SHA1 Message Date
Paul Nothaft 8421b7b668 fix(setup): require Node 22.12 for sanitize-html 2026-09-07 09:00:02 +02:00
Paul Nothaft 0f426ef699 fix(security): bump sanitize-html to 2.17.7
Trivy flags the backend image on two sanitize-html advisories, both
fixed upstream:

- CVE-2026-63670 (fixed 2.17.6): a literal solidus after a raw-text end
  tag (`</textarea/>`) is treated as text by htmlparser2 and re-emitted
  unescaped, so disallowed markup passes when textarea or xmp is in
  allowedTags.
- CVE-2026-84371 (fixed 2.17.7): an SVG SMIL animation whose
  attributeName selects href lets the sibling values/from/to/by
  attributes carry URLs past the scheme policy.

2.17.5 -> 2.17.7, exact pin as before. The new version brings its own
htmlparser2 12 / domhandler 6 / domutils 4 / dom-serializer 3 /
entities 8 tree under node_modules/sanitize-html; nothing else in the
lock moves.

That tree is ESM-only, so the backend now needs unflagged require(esm):
Node 20.19+ or 22.12+. The image is node:22-alpine and CI runs 22, but
engines.node still admitted 22.0-22.11, where require('sanitize-html')
throws ERR_REQUIRE_ESM at startup (publicSiteService loads it during
initialisation). engines is now ^20.19.0 || >=22.12.0 and the native
setup script's Node check enforces the same range instead of accepting
any 22.x. On the supported versions the sanitiser behaves identically
to 2.17.5 on the tracker and newsletter fixtures.

Jest 29's CommonJS registry cannot evaluate ESM either, so every suite
importing a route or service that uses the sanitiser would fail at
import. jest.config.js now maps `sanitize-html` to jest.sanitizeHtml.js,
which hands that one module to Node's real loader via
process.getBuiltinModule('module') — a plain require('module') inside
Jest is Jest's wrapper and returns an empty object for this package.
Verified against a real 2.17.7 install: the sanitiser suites and a
settings route suite pass; without the mapper they fail with "Cannot
use import statement outside a module".
2026-09-06 23:06:45 +02:00
Paul Nothaft ed0a8e7656 chore(deps): apply non-breaking npm audit fixes
Stable twin of the main commit: backend qs/body-parser, frontend axios,
dompurify, linkify-it and the transitive set npm audit fix resolves without
a major bump. sanitize-html 2.17.7 (ESM-only parser tree, Jest 29 cannot
load it; the advisory needs svg tags no sanitizer config allows) and the
tiptap / react-router majors are left out, as on main.
2026-09-03 12:15:53 +02:00
Paul Nothaft 83290a0f1a chore(security): ignore unfixed CVEs in Trivy, override deepmerge-ts (#1085)
Stable twin of #1083, scoped to what exists on this branch.

docker-build.yml — set ignore-unfixed on both Trivy steps. Stable has
the backend and frontend legs only (no aio, no ml), so two steps here
against four on main. Base-image CVEs with no released fix are not
actionable: the Dockerfiles already run `apt-get upgrade -y` behind a
CACHEBUST, so a fix lands in the next build automatically. Reporting
them buries anything someone can actually act on.

backend — deepmerge-ts <8.0.0 has a stack-exhaustion advisory
(CVE-2026-40345, high) reached via mailparser -> html-to-text, which
pins ^7.1.5 so npm cannot get there alone. Stable carries the same
mailparser ^3.9.9 and the same 3-high exposure as main. Not reachable
in our code: html-to-text only feeds deepmerge-ts its options object,
never parsed email content. npm audit on this branch goes 3 high -> 0.

The ml/Dockerfile half of #1083 has no counterpart here — the face
sidecar does not exist on stable, so there is nothing to drift.

Verified on stable itself rather than assuming main's results carry:
npm audit 3 high -> 0, html-to-text exercised end-to-end through
simpleParser, and jest at 1577 passed. The 5 failing suites (20 tests)
fail identically on clean origin/stable with these changes stashed.

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-08-19 13:56:10 +02:00
Paul Nothaft cee0a380a6 fix(deps): bump nanoid and js-yaml out of two HIGH advisories (stable) (#1014)
Backport of #1013 to the curated channel. Both are production dependencies of the backend image (npm ci --omit=dev):

- nanoid 3.3.16 -> 3.3.18 (CVE-2026-67213, infinite loop in customAlphabet)
- js-yaml 4.3.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj, quadratic CPU in !!omap resolution)

Stable reported no open alerts only because its last Trivy scan ran on 2026-08-04 with v3.45.14, before either advisory was published — the vulnerable versions were present in the lockfile regardless.

Lockfile-only; the existing ^ ranges already permitted both fixes.
2026-08-10 11:00:03 +02:00
Paul Nothaft 0fe5792a7d fix(deps): bump ip-address, brace-expansion and postcss for open CVEs (stable) (#988)
Backport of #987. stable carried the same vulnerable versions.

  brace-expansion  5.0.8  -> 5.0.9   CVE-2026-69152 (high)
  ip-address       10.2.0 -> 10.4.0  CVE-2026-69192 (high), CVE-2026-54272,
                                     CVE-2026-69198 (medium) — SSRF and
                                     trust-boundary bypasses
  postcss          8.5.18 -> 8.5.23  CVE-2026-69153 (medium)

Lockfile holds exactly one entry per package, all at or above the fixed
version; the image installs via npm ci --omit=dev.
2026-08-04 14:36:10 +02:00
Paul Nothaft 7419c68337 fix(security): bump sanitize-html to 2.17.5 (CVE-2026-53606) (stable) (#938)
Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-08-01 17:36:40 +02:00
Paul Nothaft fc99e2b233 fix(uploads): prevent cross-photo contamination from filename collisions and non-atomic writes (#931) (stable) (#934)
* fix(uploads): prevent cross-photo contamination from filename collisions and non-atomic writes (#931)

* test: pin the suffixed photo filename format in the NFD pipeline suite (#931)

* chore(deps): promote p-limit to a direct dependency for the watermark limiter (#931)

* test: make the suffix-uniqueness check deterministic-in-practice (#931)

* fix(uploads): widen the anti-collision suffix to 48 bits (#931)

* fix(uploads): hide staging files from list() + share one watermark limiter process-wide (#931)

* fix(uploads): reclaim orphaned staging files + revalidate watermark settings in queued jobs (#931)

---------

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-08-01 12:29:23 +02:00
Paul Nothaft d868aac703 fix(security): close 5 Trivy alerts — postcss/tar bumps + drop npm from the runtime image (stable) (#879)
* fix(security): close the 5 open Trivy alerts — dep bumps + drop npm from the runtime image

Backend deps:
- postcss 8.5.10 -> 8.5.18 (CVE-2026-45623, GHSA-r28c-9q8g-f849; the pin
  exists to force sanitize-html's transitive copy onto a fixed version)
- tar pin/override >=7.5.16 -> >=7.5.21, resolves 7.5.22
  (GHSA-r292-9mhp-454m)

Runtime image:
- Remove the npm CLI from the final stage instead of upgrading it: npm's
  bundled node_modules ship tar 7.5.19 and brace-expansion 5.0.7 (no npm
  release bundles the fixed versions — checked 11.18.0 and 12.0.1), and
  npm never runs in production. wait-for-db.sh now invokes the migration
  runners via node directly. This ends the recurring npm-bundled-CVE
  alert class; the previous 'npm install -g npm@11' line was itself a
  patch for the last batch. (stable)

* fix(restore): run post-restore migrations via node — the image ships no npm

restoreService still shelled out to 'npm run migrate:safe' after a
restore; with npm removed from the runtime image that would ENOENT into
the non-fatal catch, silently leaving a restored older backup on a
schema behind the running code until the next container restart. Invoke
migrations/run-migrations-safe.js through node directly, matching
wait-for-db.sh. The PR #596 source-contract test now pins the new
invocation. (stable)
2026-07-27 09:54:43 +02:00
Paul Nothaft a27c705e39 fix(backup): make backup settings actually apply (#871) (stable) (#875)
* fix(backup): make backup settings actually apply (#871) (stable)

- Wire the What-to-Backup toggles into the walker: honor
  backup_include_thumbnails / backup_include_photos (opt-out,
  default ON) and accept the UI's backup_include_archives spelling
  for the archived gate (the engine expected _archived, so the
  Archives checkbox silently never worked).
- Fix the 167.6 TB dashboard size: file_size_bytes is a bigint that
  node-postgres returns as a string, and the S3 path concatenated it
  onto the byte counter; coerce to Number at the source.
- Compute the real next scheduled run (cron-parser) and return it as
  nextBackup; the UI read a field the API never sent and rendered a
  hardcoded 'Not scheduled'. A named schedule label now beats the
  stray default cron the UI always sent, which silently turned
  weekly schedules into daily 03:00 runs.
- Never back up filesystem noise (.nfs* silly-renames, .DS_Store,
  Thumbs.db) and honor backup_exclude_patterns in the walker
  (previously rsync-only).
- Remove the compression/encryption toggles from the configuration
  UI: no backend implementation exists, and collecting an encryption
  passphrase while uploading plaintext is a false promise.

* fix(backup): close the review gaps in the settings wiring (stable)

- The UI's backup_include_archives now beats the migration-seeded
  backup_include_archived: every install has the singular key seeded
  true, so the alias-only-when-absent lookup made unchecking Archives
  a no-op.
- rsync destinations now receive the de-selected What-to-Backup paths
  and the noise filters as anchored --exclude args; previously rsync
  synced the whole storage root and the walker's selection only shaped
  the manifest, which then misreported what was actually transferred.
- Escape regex metacharacters in the walker's glob matcher: '.nfs*'
  compiled to /^.nfs.*$/ whose leading dot matched any character, so
  files like anfs-photo.jpg were silently dropped from backups.
- The Backup Coverage report now uses the same gate as the walker
  (new 'skipped-by-setting' status) instead of re-implementing it
  without the opt-out toggles and the archives alias.

* fix(backup): make the coverage diagnostics agree with the walker

- The coverage table shows the alias-aware flag value the gate actually
  used, instead of the seeded backup_include_archived shadowed by the
  UI's plural key (true next to a 'Gated off' badge).
- skipped-by-setting paths are now counted in the coverage summary
  (backend, TS contract, summary card, EN/DE locales) so the totals
  reconcile again when Photos or Thumbnails is unchecked.
- The form's thumbnail default now matches the backend's never-saved
  fallback (include): the checkbox no longer shows 'off' while
  thumbnails are being backed up, and saving an unrelated setting no
  longer flips the backup scope. (stable)

* fix(backup): keep custom crons, exclude disabled rows from rsync, normalize flag display

- Saving a named schedule no longer wipes the stored custom cron: the
  backend already prefers the label, so the cron field stays inert for
  named schedules and is preserved for switching back to Custom. A
  custom schedule now validates the 5-field expression before saving
  (the backend silently fell back to daily 02:00 on a blank value).
- resolveExcludedBackupPaths now also returns rows disabled via
  include_in_default, so rsync excludes them; the enabled-only loader
  hid them and rsync transferred their contents anyway.
- The coverage table normalizes flag values like the walker does —
  Boolean('false') displayed true beside a gated-off badge. (stable)
2026-07-27 09:06:50 +02:00
Paul Nothaft 39696d42fe fix(security): bump backend deps to close all 14 open Trivy code-scanning alerts (stable) (#870)
* fix(security): bump backend deps to close all open Trivy code-scanning alerts (stable)

- axios 1.16.0 -> 1.18.1 (GHSA-gcfj-64vw-6mp9 high + 10 medium advisories)
- sharp 0.34.3 -> 0.35.3 (GHSA-f88m-g3jw-g9cj, inherited libvips CVEs)
- mailparser 3.9.9 -> 3.9.14 (pulls linkify-it 5.0.2, CVE-2026-59887)
- brace-expansion override >=5.0.6 -> >=5.0.7 (CVE-2026-13149)
- body-parser 1.20.4 -> 1.20.6 via lockfile refresh (CVE-2026-12590)

* fix(images): migrate removed sharp failOnError option and enforce Node >=20.9 (stable)

sharp 0.35 drops the deprecated failOnError constructor option, so
recoverably corrupt images would start failing upload validation and
thumbnail generation; use the failOn: 'none' equivalent instead.

sharp 0.35 also requires Node >=20.9: declare it in engines and make
picpeak-setup.sh compare the full version instead of only the major,
so native installs on Node 20.3-20.8 upgrade instead of breaking.

* fix(setup): align the Node floor with the whole dependency tree and gate native updates (stable)

html-to-text@10 needs Node >=20.19 and the glob/minimatch family excludes
Node 21, so declare engines as ^20.19.0 || >=22 and enforce the same range
in picpeak-setup.sh. Also run install_nodejs at the start of
update_native_installation so existing native installs on an old Node get
upgraded before the service is stopped, instead of restarting broken.

* fix(setup): make the update-path Node gate actually work (stable)

--update dispatches before detect_os, so install_nodejs saw an empty
PACKAGE_MANAGER, matched no install branch, and reported success on the
old runtime. Detect the OS on demand and re-verify the installed version
afterwards, failing loudly (before the service is stopped) when the
runtime still misses the engines range, e.g. a Node 21 that package
managers refuse to downgrade.
2026-07-26 20:38:16 +02:00
Paul Nothaft 72e2ef6721 feat(auth): admin TOTP MFA — enrollment, login challenge, recovery, CLI reset
Backend for #738. Real TOTP 2FA for admin accounts, all roles incl.
super_admin (closes #735).

- mfaService: otplib TOTP; AES-256-GCM encryption of the secret at rest
  (key derived from MFA_ENCRYPTION_KEY or JWT_SECRET); bcrypt-hashed,
  single-use recovery codes; otpauth URI + QR.
- Migration 151: adds two_factor_recovery_codes + two_factor_enrolled_at
  (secret/enabled columns already existed from legacy 016).
- Enrollment endpoints (behind adminAuth, per-user): GET /mfa/status,
  POST /mfa/{setup,enable,disable,recovery-codes}. Disable/regenerate
  require a current code so a hijacked session can't strip 2FA.
- Login challenge: /admin/login returns {mfaRequired, mfaToken} (no
  session) when 2FA is on; /admin/login/mfa exchanges a TOTP or recovery
  code for the session. Lockout counter is NOT reset until the second
  factor passes, so MFA brute-force is rate-limited too.
- CLI break-glass: scripts/reset-admin-mfa.js --email <e> | --all --yes,
  audit-logged, matches reset-admin-password.js convention.
- Docs + optional MFA_ENCRYPTION_KEY env.

Verified end-to-end on a live backend: enroll (super_admin), challenge,
TOTP + single-use recovery login, disable, and CLI reset.
2026-07-03 11:33:38 +02:00
Paul Nothaft 7546f104a3 chore(security): close 27 code-scanning alerts via dep + base-image bumps
Single PR closing every open code-scanning alert at
https://github.com/PicPeak/picpeak/security/code-scanning. Both repos go
from 27 open alerts → 0 across direct deps, transitive deps, and build-
time bundled deps.

## Backend (`backend/package.json` + overrides)

Direct dep bumps:
- axios          1.15.2   → 1.16.0       (closes 9 alerts: 7 high + 1 med + 1 low)
- nodemailer     8.0.10   → ^9.0.1       (closes 1 high — SSRF + file-read via raw option)
- multer         2.1.1    → 2.2.0        (closes 2 alerts: 1 high + 1 med)
- form-data      4.0.5    → 4.0.6        (closes 1 high)
- tar            ≥7.5.13  → ≥7.5.16      (closes 1 med)
- postcss        8.5.6    → 8.5.10       (closes 1 med)
- i18next-http-backend  3.0.2  → 3.0.5   (closes 1 med — backend lagged frontend)
- js-yaml        4.1.1    → ^4.2.0       (closes 1 med)
- joi            17.13.3  → ^17.13.4     (closes 1 med)

Overrides updated to match deps (npm rejected the install otherwise) +
nodemailer ^9.0.1 added as override so imapflow + mailparser transitive
bundling of older nodemailer is also fixed. Babel devDep auto-bumped via
`npm audit fix` (low-severity arbitrary file read).

Backend npm audit: 0 vulnerabilities.

## Frontend (`frontend/package.json`)

Direct dep bumps:
- axios                 1.15.2  → 1.16.0
- postcss               8.5.6   → 8.5.10
- i18next-http-backend  3.0.5   → 3.0.5  (already current — kept for parity)

`npm audit fix` swept up 12 transitive issues at the same time:
- vitest (1 critical — file read on UI server)
- vite (2 high — fs.deny bypass, NTLM hash via launch-editor)
- ws (2 high — uninitialized memory + DoS)
- dompurify (8 mod — multiple IN_PLACE / hook-pollution XSS vectors)
- react-router-dom + react-router (1 mod transitive)
- esbuild (1 mod — dev server file read)
- @babel/core (1 low)

Frontend npm audit: 0 vulnerabilities.

## Frontend Dockerfile

- Build stage: `node:20-alpine` → `node:22-alpine`

Closes the npm-bundled CVE class (picomatch, ip-address, brace-expansion,
@sigstore/core, tar) that came from Node 20's older bundled npm. Matches
the backend Dockerfile base. The nginx serving stage stays at
`nginx:1.28-alpine` — that tag is rolling, so the next build picks up
the fixed 1.28.3-r4 layer that closes the 4 nginx CVEs.

## Verification

- Backend: `npm audit` → 0 vulnerabilities 
- Frontend: `npm audit` → 0 vulnerabilities 
- Backend Jest (workflow engine, rounding, WhatsApp): 47/47 pass 
- Frontend Vitest: 84/84 pass 
- `frontend npm run build`: succeeds 
- nodemailer 9 sanity check: our usage is `createTransport({host,port,secure,auth})`
  + `sendMail({from,to,subject,html,text})` — we don't touch the `raw`
  option that 9.x tightened, so the major bump is API-compatible.
2026-06-29 23:26:32 +02:00
Paul Nothaft d705059d3c fix(deps): bump qs/brace-expansion overrides + add uuid override for node-cron
Code-scanning Trivy alerts on the open beta (PR #641). Of the 10 open
alerts, 6 are stale (lockfile already past the fix) or live in
floating-tag base images (`nginx:1.28-alpine`, `node:22-alpine`) which
auto-update on the next CI rebuild — no code change needed for those.

The 3 actually present in the current `backend/package-lock.json`:

- `qs 6.15.0 → 6.15.2` (CVE-2026-8723, alert #266). Bump override from
  `>=6.14.2` to `>=6.15.2`.
- `brace-expansion 5.0.5 → 5.0.6` (CVE-2026-45149, alert #264). Bump
  override from `>=5.0.5` to `>=5.0.6`.
- `uuid 8.3.2` transitively via `node-cron@3.0.3` (CVE-2026-41907,
  alert #265). Add top-level `uuid: ^11.1.1` override so node-cron's
  nested resolution collapses into our root uuid version. node-cron
  uses only `uuid.v4()` — API-stable across v8 → v11. Verified the
  scheduler still constructs tasks under the override.

Lockfile regenerated; net -9 lines (one fewer uuid copy).

Stale alerts that will close on next code-scan rebuild:
- #205 postcss (frontend lockfile already at 8.5.14)
- #221 i18next-http-backend (backend lockfile already at 3.0.6)

Auto-resolved on next image rebuild (no Dockerfile change — floating
tags):
- #267 nginx (frontend `nginx:1.28-alpine`)
- #223 ip-address, #156/#155 picomatch, #140 brace-expansion (all in
  the npm CLI shipped inside `node:22-alpine`)

Refs: code-scanning alerts #264, #265, #266
2026-06-19 09:46:19 +02:00
Paul Nothaft e4e79a0b3a fix(archives): stream-extract restore for >2 GiB + preserve original_filename via manifest (#640)
Two related backup-integrity fixes from 8digit's fork (issue #640 items
#3 + #4), bundled because they touch the same two files and ship better
together than apart.

### Stream-extract restore for >2 GiB archives

`adminArchives.js:170` was using `adm-zip`, which loads the entire ZIP
into a Node Buffer before extracting. Node has a hard 2 GiB Buffer cap,
so any restore over that limit fails with `ERR_FS_FILE_TOO_LARGE` — and
since the frontend `onError` toast is the generic "Something went wrong",
the cause stays invisible. Real-world wedding archives routinely cross
2 GiB; affected restores have likely been silent failures.

Swapped `adm-zip` for `node-stream-zip` which streams each entry to disk
as it's processed — no full-file Buffer, no 2 GiB ceiling. API shape:

```js
const zip = new StreamZip.async({ file: archivePath });
const entries = Object.values(await zip.entries());
await zip.extract(null, eventDir);
await zip.close();
```

Re-import logic (photos, categories, sizes) unchanged; only field rename
`entry.entryName` → `entry.name`. Credit: 8digit/picpeak@69033c6.

### Preserve `original_filename` via photos manifest

Archive → restore round-trip currently loses `original_filename` (the
post-#508 column tracking the camera-side name) because the gallery
filenames are renamed on upload and can't be derived from the extracted
files. This matters now that the Lightroom export (#623) depends on
`original_filename` — a restored event lost that signal.

- **`archiveService.js`**: writes `photos_manifest.json` into the archive
  containing per-photo `{filename, original_filename, type, uploaded_at,
  category_name}`. Non-fatal: a manifest write failure falls through to
  legacy behaviour (filename used as original_filename, same as before).
- **`adminArchives.js`**: reads the manifest on restore, builds a
  `Map<filename → manifest>`, and assigns
  `original_filename = manifest?.original_filename || filename`.
  Archives produced before this lands have no manifest — restore logs a
  one-shot notice and falls back to filename, preserving backward compat.

Credit: 8digit/picpeak@eb018aa.

### Deps

- Removed `adm-zip ^0.5.16`
- Added `node-stream-zip ^1.15.0`

### What's NOT in this PR

8digit's commit also fixed the production compose healthcheck (`curl`
isn't in our Alpine image); that's already been addressed upstream in
the meantime. The frontend `onError` swallow on the restore toast is a
separate small follow-up.

### Test plan

- [x] `node -c` on both files clean
- [x] `node-stream-zip` async API verified at load time
- [ ] Manual: archive a multi-GB event → restore → confirm photos
      re-import with original_filename preserved
- [ ] Manual: restore an archive produced before this lands → confirm
      fallback to filename works (no manifest path crashes)
- [ ] Manual: confirm the new photos_manifest.json is inside the
      generated archive (`unzip -l <archive>.zip | grep manifest`)
2026-06-18 22:18:54 +02:00
Luca 5645c304ab feat(email): incoming mail (IMAP) intake - backend + standalone flag
Adds a second mail config (incoming/IMAP) alongside the outgoing SMTP one, a
1-minute poller, and a received-emails log. Standalone `incomingMail` feature
flag (default off).

- deps: imapflow + mailparser (receive-side; picpeak only had nodemailer).
- migration 128: email_configs gains imap_* columns (same shape as smtp_*);
  seed incomingMail flag; new received_emails audit table.
- emailIntakeService: polls the mailbox every 60s when the flag is on AND a
  mailbox is configured (no-op otherwise); parses each unseen message
  (mailparser flattens forwarded/nested attachments), drops PDF/JPEG/PNG into
  the incoming-invoices inbox (inbound_documents, source='email'), logs each
  message in received_emails (dedupe by message-id; duplicate attachments
  caught by the existing SHA-256 guard), marks it \Seen.
- adminEmail: GET/POST /incoming-config (mirrors SMTP config, masks imap_pass,
  SSRF host guard) + GET /received (paginated log).
- server.js starts the poller at boot.

Verified: node -c, require-graph, migration-128 harness (imap columns, flag,
received_emails). Frontend (IMAP block under SMTP + Received tab + flag card)
follows.
2026-06-11 15:54:43 +02:00
Luca d543949188 feat(crm): backend code — services + routes + utilities + tests
Brings in the full backend CRM stack on top of the consolidated
migration (60abe8c).

Services (CRM)
  - quoteService — full lifecycle (draft → sent → accepted → converted
    to event/invoice), Skonto + Storno + reissue paths
  - invoiceService — spawnInstallmentInvoices, updateInstallmentPlan,
    monthly-billing accumulator, payment-check tokens, dunning ladder
  - contractService — block-composable contract editor, in-browser
    signature flow, wet-PDF upload path, integrity check, audit trail
  - customerHoursService — per-entry locking, billing integration
  - dealsService — cross-document lineage (deal_uuid)
  - taxReportService — quarterly aggregates + CSV/PDF export
  - eventReminderService — pre-event customer reminder cron pass
  - _renderContext — shared issuer/recipient blocks across PDF types
  - pdfService extensions — custom-font registration, font picker

Routes (admin + public)
  - adminQuotes, adminInvoices, adminContracts, adminCalendar,
    adminDeals, adminTaxReport, adminDev, adminBusinessProfile
  - publicQuotes (accept/decline), publicContracts (sign),
    publicPaymentCheck
  - Extensions on adminEvents, adminCustomers, adminSettings,
    adminEmail, adminFeatureFlags, adminThumbnails, adminPhotos,
    adminCategories, adminUsers, adminArchives, adminDashboard
  - server.js wires the new mounts (kept upstream's noStoreCache on
    customer routes per 3-way merge)

Utilities
  - schemaCache (cached hasColumn lookups across services)
  - documentSequences (atomic gap-free numbering — §14 UStG)
  - safePath (path-containment guards at fs stream boundaries)
  - clientIp (sanctioned XFF reader for audit logs)
  - publicTokenGuards (pre-multer token validation + attempt counters)
  - numericHelpers (ensureInt / ensureNumber consolidation)
  - dateFormatter (formatShortDate + dateInputLang)
  - dbCompat extensions, iban + pdfFilename helpers, resolveLogoFile

Infrastructure
  - Bundled PDF fonts (Comic-Neue / IBM-Plex-Sans / Inter / Jost /
    Montserrat / Noto-Sans / Playfair-Display / Poppins)
  - Backend package.json + lock updates (pdfkit, signature_pad,
    qrcode, et al.)
  - Sample storage layout under storage/business-docs/quote/

Tests
  - 14 new test files covering quote/invoice/contract lifecycle,
    installment plan reshape, line-item hierarchy, customer hours,
    payment check, tax report PDF, IBAN parsing, filename sanitiser
2026-05-26 18:18:51 +02:00
Paul Nothaft 6b6191a426 fix(security): scan triage cleanup — drop dead deps, harden Docker/nginx/postMessage
Triage of an external SAST/SCA scan run on 2026-05-06. Most loud findings
were already resolved by PR #412 (the 18-CVE backport); this PR addresses
the residual real items:

* Drop unused `handlebars` from backend deps. The runtime require was
  removed in PR #367 (#367) but the package.json line stayed. handlebars
  was the source of two flagged criticals (CVE-2026-33937 RCE,
  GHSA-2w6w-674q-4c4q AST injection) plus 8 highs — all now gone.

* `npm audit fix` on backend + frontend. Bumps transitive picomatch,
  flatted, postcss, brace-expansion via lockfile, and direct dompurify,
  lodash, vite, i18next-http-backend within their existing semver ranges.
  Both audits now report 0 vulnerabilities.

* Add `event.origin === window.location.origin` check to the THEME_PREVIEW
  message listener in PreviewPage. The branding page posts from the same
  origin, so nothing legitimate is rejected; without the check, any third
  party that window.open()'d the preview could push arbitrary
  branding/theme payloads (semgrep
  insufficient-postmessage-origin-validation).

* nginx: `proxy_hide_header` for X-Frame-Options, X-Content-Type-Options,
  Referrer-Policy, Content-Security-Policy, Permissions-Policy,
  Strict-Transport-Security at server level. nginx adds these itself, but
  helmet on the backend was also emitting them — clients were seeing
  duplicates (testssl flagged "Multiple X-Frame-Options / CSP /
  Permissions-Policy / Referrer-Policy headers" on the live origin).
  Single source of truth now.

* Dockerfile hardening (checkov):
  - HEALTHCHECK on backend/Dockerfile, backend/Dockerfile.dev,
    frontend/Dockerfile.dev. Frontend production Dockerfile already had
    one.
  - USER node in frontend/Dockerfile.dev (was running as root).

* GitHub Actions docker-build.yml: explicit top-level
  `permissions: contents: read`. Per-job blocks already declare
  `packages: write` where needed; this stops future steps from
  inheriting unintended privileges (CKV2_GHA_1).

Backend npm audit: 4 vulns -> 0.
Frontend npm audit: 6 vulns -> 0.
Backend unit tests: 13 suites, 131/132 passing (1 pre-existing skip).
Frontend type-check + lint: clean.

The pre-existing integration-test failures (live DB / S3 required) and
the ThemeCustomizerEnhanced QueryClientProvider failures are unrelated
and reproduce on origin/beta without these changes.
2026-05-07 22:31:51 +02:00
Paul Nothaft b7d6ca0b65 fix(security): patch 18 dependency CVEs (axios + transitives + nodemailer + i18next-http-backend)
Closes the open Trivy code-scanning alerts for app-side dependencies.
The npm-bundled CVEs in /usr/local/lib/node_modules/npm (picomatch,
brace-expansion, ip-address inside the Node image itself) are deferred
to a separate Node-base-image PR — they're build-environment-side and
need their own compatibility testing.

## Direct dependency bumps

| Package | From | To | CVEs cleared |
|---|---|---|---|
| axios (backend + frontend) | 1.14.0 | 1.15.2 | CVE-2026-42264 (HIGH), CVE-2026-42043 (HIGH), CVE-2026-42035 (HIGH), CVE-2026-42033 (HIGH), CVE-2026-42044, CVE-2026-42042, CVE-2026-42041, CVE-2026-42040, CVE-2026-42039, CVE-2026-42038, CVE-2026-42037, CVE-2026-42036, CVE-2026-42034, CVE-2026-40175, CVE-2025-62718 |
| nodemailer (backend) | ^7.0.13 | ^8.0.5 | GHSA-vvjj-xcjg-gr5g, GHSA-c7w3-x93f-qmm8 |
| i18next-http-backend (frontend) | ^3.0.2 | ^3.0.5 | CVE-2026-41691 |
| uuid (backend) | ^11.1.0 | ^11.1.1 | CVE-2026-41907 |
| postcss (frontend, devDep) | ^8.4.21 | ^8.5.10 | CVE-2026-41305 |

## Transitive bumps (npm overrides)

For transitives whose direct parents haven't released a version that
picks up the patched range, pinned via npm overrides:

| Package | Min | CVE |
|---|---|---|
| follow-redirects (backend + frontend) | >=1.16.0 | GHSA-r4q5-vmmm-2653 |
| fast-xml-parser (backend) | >=5.7.0 | CVE-2026-41650 |
| @tootallnate/once (backend) | >=3.0.1 | CVE-2026-3449 |
| ip-address (backend) | >=10.1.1 | CVE-2026-42338 |

## Why axios is now safe to bump past 1.14.0

PR #268 originally pinned axios to 1.14.0 to avoid a supply-chain
attack on a specific compromised version range. The 1.15.x series
are post-incident upstream releases — clean. Confirmed with the
maintainer before bumping.

## Verified

* `npx tsc --noEmit` (frontend) — clean
* `npx vite build` (frontend) — clean (~4s, existing bundle-size
  warning, not new)
* Backend module-load smoke test — all critical modules load
  (`auth`, `adminAuth` middleware, `emailProcessor`, `recaptcha`,
  `storage`) with the new axios + nodemailer
* Lockfile re-verification — every targeted CVE now resolves to
  the patched version range

## Remaining out of scope

* npm-bundled CVEs inside `/usr/local/lib/node_modules/npm/` —
  picomatch CVE-2026-33671 (HIGH), CVE-2026-33672, brace-expansion
  CVE-2026-33750, ip-address (npm-internal) CVE-2026-42338. These
  live in the Node base image and require a Node base image bump
  with its own compatibility testing — separate PR.

Targeting `beta` so the bumps go through the normal release-please
flow before promotion to `main`.
2026-05-07 13:51:55 +02:00
Paul Nothaft 96818c7ae8 fix(docker): install system ffmpeg on Alpine, drop broken bundled binary
Video uploads on production fail with "missing ffmpeg" because the
backend container ships nothing usable for the video pipeline.

Two compounding causes:

1. **Alpine + glibc mismatch.** The npm `@ffmpeg-installer/ffmpeg`
   dependency added with the video-support PR (commit 68a9dc5)
   ships per-platform binaries via optionalDependencies. The Linux
   binaries are built against glibc, but the backend image runs on
   `node:22-alpine` (musl libc) — known to either fail to execute
   or fail on shared-library lookups on Alpine.

2. **`ffprobe` missing entirely.** `@ffmpeg-installer/ffmpeg`
   bundles only the `ffmpeg` binary. There's a separate
   `@ffprobe-installer/ffprobe` package that the codebase never
   depended on. But `videoProcessor.js:21` calls
   `ffmpeg.ffprobe(videoPath, …)` — the very first step of the
   video pipeline shells out to a `ffprobe` binary that doesn't
   exist in the image. Even if (1) worked, every video upload
   would 500 here.

The fix is to install Alpine's `ffmpeg` package via apk. It ships
both `ffmpeg` and `ffprobe` built natively against musl, ~70MB
extra image size, single line in the Dockerfile, no per-arch
handling needed (apk pulls the right binary for both linux/amd64
and linux/arm64 — works with the multi-arch infra from #349).

- `backend/Dockerfile`: add `ffmpeg` to the apk install line.
- `backend/Dockerfile.dev`: same for dev parity.
- `backend/src/services/videoProcessor.js`: remove the
  `setFfmpegPath(require('@ffmpeg-installer/ffmpeg').path)` line
  — without removing it, fluent-ffmpeg would prefer the broken
  bundled binary over the working apk one. Letting fluent-ffmpeg
  fall back to PATH lookup picks up the apk binary in the
  container and the developer's locally-installed binary on dev
  hosts (Homebrew on macOS, apt on Debian).
- `backend/package.json`: drop the now-unused
  `@ffmpeg-installer/ffmpeg` dependency. `npm install` removes
  2 packages from the lockfile.

Verified: `videoProcessor.js` still loads cleanly (`node -e
"require('./src/services/videoProcessor')"`); lint clean.
2026-05-04 09:04:38 +02:00
Paul Nothaft 808b15bafb feat: public v1 API + token management + OpenAPI docs (#322)
Adds a long-lived bearer-token mechanism + scoped REST surface designed
for n8n-style automation: create a gallery, upload photos, fetch the
share URL — all via documented HTTPS endpoints instead of poking at the
admin UI's internal routes.

API
- Migration 081 adds `api_tokens` (hashed_token, scopes, owner FK,
  last_used/expires/revoked timestamps).
- New apiTokenAuth middleware: parses `Authorization: Bearer pp_live_…`,
  resolves to the owner admin user, attaches `req.admin` so existing
  permission decorators (events.create etc.) still work. Token-level
  scope check (read/write/admin) layers on top as defence in depth —
  a leaked read-only token cannot mutate even if its owner is super_admin.
- adminApiTokens route exposes list/create/revoke for admins (cookie-
  authed). Plaintext token is returned exactly once on creation.
- v1 surface mounted at /api/v1: POST/GET /events, GET /events/:id,
  POST /events/:id/photos (multipart, single file), GET
  /events/:id/share-link. Each endpoint annotated with @openapi JSDoc.

Documentation
- swagger-jsdoc + swagger-ui-express produce a live spec at
  /api/openapi.json and a Swagger UI at /api/docs (admin-gated).
- backend/scripts/generate-openapi.js writes docs/openapi.{json,yaml}
  to the repo so the spec is versioned.
- scripts/sync-api-docs.sh runs in pre-push: regenerates the spec and
  copies it into the picpeak-docs Nextra site at app/api/. Writes only,
  never commits or pushes the docs repo (PUSH_SKIP_DOCS=1 to bypass).

Frontend
- New Settings → API Tokens tab: generate, list, revoke. Plaintext
  tokens are shown once with a copy-to-clipboard control.
2026-04-27 22:38:00 +02:00
Paul Nothaft 83868ffe2f security: fix 20 dependency vulnerabilities (11 error, 7 warning, 2 note)
Update direct dependencies and overrides to address GitHub code scanning alerts:

- handlebars 4.7.8 -> 4.7.9 (5 CVEs: RCE, DoS, XSS, code execution)
- nodemailer 7.0.12 -> 7.0.13 (SMTP command injection)
- tar 7.5.11 -> 7.5.13 override (symlink/hardlink path traversal)
- fast-xml-parser >=5.3.8 -> >=5.5.10 override (entity expansion bypass)
- brace-expansion >=5.0.0 -> >=5.0.5 override (DoS via zero step)
- path-to-regexp 0.1.12 -> 0.1.13 override (ReDoS via malformed URL params)
- lodash 4.17.23 -> >=4.18.1 override (prototype pollution, code execution)

The picomatch CVEs are in npm's own node_modules inside the Docker image
and do not affect application code.
2026-04-08 09:04:06 +02:00
Paul Nothaft bec36fc99f security: pin axios to 1.14.0 to prevent supply chain attack
Axios versions 1.14.1 and 0.30.4 were compromised on March 31, 2026
with a RAT dropper (plain-crypto-js) attributed to North Korean threat
actor UNC1069/Sapphire Sleet. The malicious versions have been removed
from npm but our ^1.12.2 range could have pulled 1.14.1 on next install.

Pin to exact version 1.14.0 (latest safe release) in both frontend and
backend package.json and lock files to prevent any future resolution to
compromised versions.

References:
- https://github.com/axios/axios/issues/10604
- https://snyk.io/blog/axios-npm-package-compromised-supply-chain-attack-delivers-cross-platform/
2026-04-05 18:40:24 +02:00
Paul Nothaft 1f524f2358 fix: update dependencies to resolve code scanning security alerts
- Upgrade multer to 2.1.1 (CVE-2026-3520, DoS via malformed requests)
- Update tar override to >=7.5.11 (CVE-2026-31802, CVE-2026-29786)
- Upgrade Node base image from 20-alpine to 22-alpine to fix npm
  bundled tar/minimatch CVEs in the Docker image
2026-03-16 16:25:29 +01:00
Paul Nothaft cbecb9323c fix(security): resolve Docker image CVEs for code scanning alerts
- Upgrade nginx base from 1.27-alpine to 1.28-alpine (Alpine 3.23, OpenSSL 3.5.5)
- Upgrade npm to latest in backend production stage to fix tar, minimatch, brace-expansion CVEs
- Add brace-expansion and minimatch overrides for app-level transitive deps
- Remove incompatible body-parser v2 override (breaks Express 4 JSON parsing)
- Remove npm upgrade from builder stages (npm 11 breaks npm ci with existing lockfile)
2026-03-02 23:06:15 +01:00
Paul Nothaft 4272618b3f fix(security): resolve all npm audit vulnerabilities
Frontend (6 → 0 vulnerabilities):
- axios: update to fix DoS via __proto__ key in mergeConfig (CVE-2026-25639)
- swiper: update to fix prototype pollution (critical)
- rollup: update to fix arbitrary file write via path traversal
- minimatch: update to fix multiple ReDoS vulnerabilities
- ajv: update to fix ReDoS with $data option
- markdown-it: update to fix ReDoS

Backend (32 → 0 vulnerabilities):
- multer: update to fix DoS via incomplete cleanup and resource exhaustion
- minimatch: update to fix multiple ReDoS vulnerabilities
- Add npm overrides for transitive dependencies:
  - fast-xml-parser >=5.3.8 (fixes XSS, DoS, stack overflow via AWS SDK)
  - qs >=6.14.2 (fixes arrayLimit bypass DoS via Express)
  - tar >=7.5.8 (fixes path traversal and hardlink attacks via sqlite3)

Docker:
- Pin nginx base image to 1.27-alpine in Dockerfile.prod
- Update security comments in backend Dockerfile
- Existing apk upgrade --no-cache ensures OpenSSL/libexpat CVEs are
  patched at build time (OpenSSL 3.5.5, Alpine 3.23.3)
2026-03-02 10:36:47 +01:00
Paul Nothaft 50c09904a9 feat: add update instructions dialog, email notifications, and capture date sorting
- Add Update Instructions Dialog with environment-specific commands (Docker/Git/Standalone)
- Add email notification settings for new version alerts
- Add "Sort by Capture Date" option using EXIF metadata extraction
- Fix E2E tests by loading environment variables via dotenv
- Add test-images/ and backend/*.db to .gitignore

Closes #181
2026-02-16 16:23:57 +01:00
Paul Nothaft 8097a0cb53 fix: update packages to fix security vulnerabilities
- react-router-dom 6.30.2 → 6.30.3 (XSS via Open Redirects)
- react-router 6.30.2 → 6.30.3
- @remix-run/router 1.23.1 → 1.23.2
- lodash 4.17.21 → 4.17.23 (Prototype Pollution)
2026-01-27 11:40:18 +01:00
Paul Nothaft 892e47d017 feat: add multi-administrator support with RBAC and fix backup/restore for S3
## Multi-Administrator System
- Add role-based access control (RBAC) with predefined roles (Super Admin, Admin, Editor, Viewer)
- Add granular permissions system for all admin operations
- Add admin user management page with invite functionality
- Add email invitation system for new administrators
- Add permission middleware protecting all admin routes
- Add PermissionGate component for frontend permission checks
- Track event creator (created_by) for audit purposes

## Backup & Restore Fixes
- Fix S3 backup: endpoint URL handling, manifest loading, field name compatibility
- Fix S3 restore: add list-backups endpoint, transform S3 config from frontend format
- Fix PostgreSQL compatibility: add .returning('id') for insert operations
- Fix disk space check: use df command, handle unknown space gracefully
- Fix dry-run validation to not block on warnings
- Fix req.user → req.admin in restore routes

## Database Migrations
- 054: Add roles table with predefined roles
- 055: Add permissions table
- 056: Add role_permissions junction table
- 057: Add role_id to admin_users
- 058: Add admin_invitations table
- 059: Add admin email templates
- 060: Add created_by to events table

## Other Improvements
- Update .gitignore to exclude planning docs and local backup directory
- Remove SQLite database file from tracking
- Add i18n translations for user management (EN/DE)
2026-01-07 17:10:46 +01:00
Paul Nothaft 6033461be1 feat: add Apple Liquid Glass templates, image security settings, and automated releases
## New Features
- Apple Liquid Glass CSS template with iOS 26-inspired design
- Liquid Glass Dark theme with neon accents
- Image Security settings tab with per-event protection levels
- Release Please automation for versioning and changelog

## Improvements
- Update CSS template migration with final working templates
- Add search placeholder visibility fix for glass themes
- Update README roadmap (Download Protection, Gallery Templates, Filtering & Export now implemented)

## Infrastructure
- Add release-please.yml workflow for automated releases
- Add release-please-config.json and manifest
- Update docker-build.yml with Release Please integration comments
- Add comprehensive CHANGELOG.md

## Cleanup
- Add working/planning docs to .gitignore (CLAUDE.md, test-*.md, feature-*.md, etc.)
- Remove internal planning documents from git tracking (kept locally)

## Files Added
- .github/workflows/release-please.yml
- .release-please-manifest.json
- release-please-config.json
- CHANGELOG.md
- frontend/src/features/settings/tabs/ImageSecurityTab.tsx
2026-01-03 23:35:23 +01:00
Paul Nothaft f3c2cee362 security: Fix critical vulnerabilities and harden application
## Security Fixes

### CRITICAL: Command Injection (adminBackup.js)
- Replaced exec() with spawn() using argument arrays
- Added input sanitization for host, user, and ssh_key
- Added regex validation for hostname/IP format
- Added username format validation
- Added SSH key file existence check
- Prevents shell metacharacter injection attacks

### HIGH: Hardcoded Password (set-admin-password.js)
- Removed hardcoded 'admin123' password
- Now requires password as CLI argument or env variable
- Added password strength validation (8+ chars, mixed case, numbers, special chars)
- Added --help flag with usage instructions
- Invalidates existing sessions on password change

### MEDIUM: XSS Vulnerability (WelcomeMessageEditor.tsx)
- Added DOMPurify sanitization to getPreviewHtml()
- Strips all HTML tags before rendering preview
- Prevents script injection in admin preview

### LOW: Sample Password Exposure (EmailConfigPage.tsx)
- Replaced plaintext sample password with masked placeholder
- Uses '••••••••' instead of realistic password

## Dependency Updates
- Fixed npm audit vulnerabilities (jws, qs, express)
- Backend: 0 vulnerabilities
- Frontend: 0 vulnerabilities
2026-01-03 10:12:01 +01:00
Paul Nothaft 3424bd22ee refactor: Phase 1 code consolidation and service layer setup
Phase 1.1: Shared parsers utility
- Create backend/src/utils/parsers.js with parseBooleanInput, parseStringInput, etc.
- Create frontend/src/utils/parsers.ts with TypeScript equivalents
- Update routes to import from shared parsers

Phase 1.2: Auth routes consolidation
- Merge auth.js, auth-enhanced.js, auth-enhanced-v2.js into single auth.js
- Add password change and password strength endpoints
- Consolidate middleware (auth.js with token revocation support)
- Update all imports across 14+ route files

Phase 1.3: CreateEvent page consolidation
- Remove duplicate CreateEventPage.tsx (basic version)
- Rename CreateEventPageEnhanced.tsx to CreateEventPage.tsx
- Update exports and imports

Phase 1.4: CMS page consolidation
- Remove duplicate CMSPage.tsx (basic version)
- Rename CMSPageEnhanced.tsx to CMSPage.tsx
- Update exports and imports

Phase 1.5: Multer config factory
- Create backend/src/config/multerConfig.js
- Centralized upload configuration with presets for photos, logos, favicons
- Reusable helpers: createDiskStorage, createFileFilter, uploadTimeoutMiddleware

Phase 2.1: Event service layer
- Create backend/src/services/eventService.js
- Move event business logic out of routes
- Functions: createEvent, getAllEvents, updateEvent, deleteEvent, extendExpiration
2026-01-02 10:12:24 +01:00
Claude 8315c11d34 Update backend package-lock.json for npm compatibility
Regenerate lock file to include missing transitive dependencies
(encoding, iconv-lite) required by newer npm versions.
2025-11-28 17:54:36 +01:00
Claude 68a9dc5749 Add comprehensive video support to galleries
This commit implements full video upload, storage, streaming, and playback functionality
for the PicPeak photo sharing platform, allowing users to upload and view videos alongside
photos in galleries.

Backend Changes:
- Added video processing dependencies (fluent-ffmpeg, @ffmpeg-installer/ffmpeg)
- Created videoProcessor.js service for video metadata extraction and thumbnail generation
- Updated photoProcessor.js to handle both images and videos
- Modified adminPhotos.js to accept video files with 500MB size limit
- Enhanced gallery.js with HTTP range request support for video streaming
- Expanded fileSecurityUtils.js with video MIME types and magic number validation
- Added database migration for video support columns (media_type, duration, codecs, dimensions)

Frontend Changes:
- Updated TypeScript types to include video metadata fields
- Created VideoPlayer.tsx component with custom controls
- Modified PhotoUpload.tsx to accept video files (.mp4, .webm, .mov, .avi)
- Updated UserPhotoUpload.tsx for guest video uploads
- Enhanced PhotoGrid.tsx with video badges and duration display
- Modified PhotoLightbox.tsx to conditionally render VideoPlayer for videos

Database Schema:
- Added media_type column ('image' | 'video')
- Added mime_type, duration, video_codec, audio_codec columns
- Added width and height columns for media dimensions
- Migrated existing photos to media_type 'image'

Features:
- Video thumbnail generation from video frames
- Streaming support with range requests for efficient playback
- Video duration display on thumbnails
- Play button indicators on video items
- Full-featured video player with playback controls
- Support for MP4, WebM, MOV, and AVI formats
2025-11-28 17:53:56 +01:00
paul bce5f749b1 Merge remote-tracking branch 'upstream/main'
Build and Push Docker Images / build-backend (push) Failing after 13m5s
Build and Push Docker Images / build-frontend (push) Failing after 2m55s
Build and Push Docker Images / summary (push) Successful in 3s
2025-11-25 22:43:48 +02:00
Claude f327f4cbcd Update package-lock.json files to sync with security overrides 2025-11-25 20:40:29 +00:00
Gitea Actions Bot f29e9db99d chore: bump version to 1.1.15 (backend + frontend)
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-29 11:30:46 +00:00
paul 69538b86ea Fix mobile overlay and deps per #43
Test and Lint / backend-test (pull_request) Successful in 1m24s
Test and Lint / frontend-test (pull_request) Successful in 1m59s
continuous-integration/drone/pr Build is passing
2025-10-29 12:19:43 +01:00
Gitea Actions Bot b76e45cb54 chore: bump version to 1.1.14 (backend + frontend)
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-20 12:41:34 +00:00
Gitea Actions Bot 07759a0e40 chore: bump version to 1.1.13 (backend + frontend)
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-15 05:29:19 +00:00
Paul Nothaft 52f8f1f738 Upgrade nodemailer to 7.0.7 (GHSA-mm7p-fcc7-pg87)
Test and Lint / backend-test (push) Successful in 1m23s
Test and Lint / frontend-test (push) Successful in 1m53s
2025-10-13 21:11:38 +02:00
Paul Nothaft e731e7b47c Address tar-fs CVE-2025-59343
Test and Lint / backend-test (push) Successful in 1m21s
Test and Lint / frontend-test (push) Has been cancelled
2025-10-13 21:09:52 +02:00
Gitea Actions Bot f20472ca26 chore: bump version to 1.1.11 (backend + frontend) 2025-10-12 19:23:19 +00:00
Gitea Actions Bot 87f4526220 chore: bump version to 1.1.10 (backend + frontend) 2025-10-12 19:18:37 +00:00
Gitea Actions Bot d42a11680f chore: bump version to 1.1.9 (backend + frontend) 2025-10-06 13:18:43 +00:00
Gitea Actions Bot 38dd74b893 chore: bump version to 1.1.8 (backend + frontend) 2025-10-03 05:19:52 +00:00
Gitea Actions Bot 2fe32e9a69 chore: bump backend version to 1.1.5 2025-09-27 05:59:32 +00:00
Gitea Actions Bot fb739f221d chore: bump version to 1.1.4 (backend + frontend) 2025-09-24 15:39:34 +00:00
Gitea Actions Bot a4595e2ab2 chore: bump backend version to 1.1.3 2025-09-22 20:50:54 +00:00
Gitea Actions Bot cab5b0d795 chore: bump backend version to 1.1.2 2025-09-22 17:08:29 +00:00