Commit Graph

1550 Commits

Author SHA1 Message Date
Edouard Vanbelle 324fcd486f fix(oidc): fix user's picture copy when OIDC login 2026-06-28 23:20:02 +02:00
Orville Bennett f99764fcff fix: remove panic=abort so catch_unwind guards PDF extraction. Fixes #530 2026-06-28 14:14:19 -04:00
Dionisio Pozo 8ed8fe8a20 Merge pull request #527 from EdouardVanbelle/feat/drive 2026-06-26 22:44:56 +02:00
M.Schmidt b5afa1a855 feat(webdav): implement RFC 4918 dead property store
Add an in-memory DeadPropertyStore backed by RwLock<HashMap> that
stores arbitrary client-supplied XML properties per resource path.
Wire it into AppState so PROPPATCH can persist dead props and PROPFIND
can retrieve them across requests.
2026-06-26 21:19:03 +02:00
Edouard Vanbelle 9697c63210 deps(security): bump pdf-extract → 0.12.0 to pull lopdf 0.42.0
lopdf <0.42.0 has an unbounded-recursion stack overflow on deeply
nested PDF objects (advisory 2026-06-21). The vector through OxiCloud
is the search-index text extractor — anyone who can upload a file can
ship a malicious PDF, and the existing catch_unwind in
text_extractor::extract_pdf does not save us: a stack overflow aborts
the process, it is not a panic.

pdf-extract 0.12.0 requires lopdf ^0.42 which adds the depth bound;
the only consumer call (`extract_text_from_mem`) is API-compatible,
no source changes needed.
2026-06-26 18:57:44 +02:00
Edouard Vanbelle 26d3c692ba feat(drive): policies management from UI 2026-06-26 18:57:35 +02:00
Edouard Vanbelle 66f2aaa250 feat(drive): add drive policie
- add policy forbid_external_sharing
    - add policy forbid_sharing
    - add polocy forbid_cross_drive_move
    - add policy forbid_owner_role_change
2026-06-26 14:04:36 +02:00
Edouard Vanbelle ddb131da8b feat(drive): add policy forbid_public_links 2026-06-26 13:59:02 +02:00
Edouard Vanbelle cfd783cbd3 feat(drive): personal drives have the user's quota in commun 2026-06-26 13:59:02 +02:00
Dionisio Pozo 996ace8a1d Merge pull request #524 from EdouardVanbelle/fix/cascading-deletion-and-file-life-cycle
fix(fileLifeCycle): add missing on_file_deleted() with cascading deletion
2026-06-26 12:35:25 +02:00
Dionisio Pozo 2fb2bcb8f3 Merge pull request #526 from EdouardVanbelle/feat/recent
feat(recent): update recent list on server side
2026-06-26 07:35:32 +02:00
Edouard Vanbelle f4032e9e23 feat(recent): update recent list server side
initially the recent was done client side
    recent files are now directly updated on serverside when accessing a file

    note: nextcloud and webdav voluntary not included
2026-06-26 01:44:19 +02:00
Edouard Vanbelle fefba83cfc fix(fileLifeCycle): add missing on_file_deleted with cascading del
on_file_deleted() was missing on folder deletion or on cascading deletion
2026-06-25 23:36:50 +02:00
M.Schmidt 5a001c4120 fix(webdav): remove spurious calendar_rest_handler module declaration
The declaration was brought in by a cherry-pick but that handler does
not exist on this branch, causing a compilation error.
2026-06-25 22:44:27 +02:00
Dionisio Pozo 11e34762e3 Merge pull request #520 from EdouardVanbelle/feat/drive-d1 2026-06-25 22:10:12 +02:00
Edouard Vanbelle 81d50bde33 fix(GC): race condition in dedup_service
Stamp `orphaned_at = now()` on the ref-0 row so it sits inside the
     GC grace window for the duration of this test. Without it,
     `orphaned_at IS NULL` is treated by `garbage_collect` as
     "pre-migration, immediately reapable" — and any sibling test in
     the shared pool that calls `garbage_collect()` (e.g.
     `garbage_collect_respects_grace_and_cross_checks`) would race
     with the pin below and delete the row first.
2026-06-25 19:00:19 +02:00
Edouard Vanbelle 4a99eb2453 chore(ci): make space on CI runners 2026-06-25 00:41:31 +02:00
Edouard Vanbelle 2604ac3309 refactor: format syntaxt 2026-06-25 00:07:16 +02:00
Edouard Vanbelle 2643484860 test(storage_cleanup_check.sh): add support of drive cleanup 2026-06-25 00:05:36 +02:00
Edouard Vanbelle 8387473b26 bigfix(gc): fix leak with GC and blob lifecycle
call fire_blob_hooks to respect lifecycle

    dedup_service::garbage_collect_with_grac must call fire_blob_hooks() once blob are dropped
    so other services like thumbnail can proceed to their cleanup
2026-06-24 23:51:25 +02:00
Edouard Vanbelle 90474aa885 chore(test): blob lifecyclc with thumbnail cleanup
renable thumbnail test, ensure that blob lifecycle correctly
    trigger thumbnail cleanup on blob deletion

    need to call `/api/admin/internal/trigger-gc?force=true`
2026-06-24 23:50:37 +02:00
M.Schmidt 5913fff047 feat(webdav): RFC 4918 prop classification and 404 propstat 2026-06-24 23:08:51 +02:00
Edouard Vanbelle 6b8e2ba49c test(drive): check quota calculus 2026-06-24 23:01:34 +02:00
Edouard Vanbelle a9a8604322 feat(drive): add quota calculus per drive
- quota per drive
    - add 2 internal API endpoints to test purpose
        disabled by default, enable it via `OXICLOUD_ENABLE_ADMIN_INTERNAL_ENDPOINTS=true`

        this enable:

            /api/admin/internal/trigger-sweep
                to sweep the trash and recalculated quota

            /api/admin/internal/trigger-gc
                to garbage orphan blobs

        use full for end to end tests and validate lifecycles
2026-06-24 22:28:31 +02:00
Edouard Vanbelle b73f176024 feat(drive): permanent deletion per drive 2026-06-24 21:56:27 +02:00
Edouard Vanbelle 7d24015fc4 feat(drive): add drive deletion
- conditions: drive must be empty
    - deletion forbidden on main personal drive
2026-06-24 21:17:27 +02:00
Edouard Vanbelle 33cfa876d0 chore(frontend/test): normalize test environement
normalize environment to avoid such issues during tests
    on non EN local machine:

    ```
    AssertionError: expected 'il y a 2 ans' to match /year/

   ❯ src/lib/utils/time.test.ts:24:60
       22|
       23|  it('formats past times in the largest matching unit', () => {
       24|   expect(relativeTimeAgo(Date.now() - 2 * 31_536_000_000)).toMatch(/year/);
    ```
2026-06-24 20:53:03 +02:00
Dionisio Pozo 1d0fa27991 Merge pull request #519 from EdouardVanbelle/feat/drive-d1 2026-06-24 08:26:00 +02:00
Edouard Vanbelle 754e6239da fix(drive): correct integration test 2026-06-24 08:02:40 +02:00
Edouard Vanbelle 338915a912 security(quinn-proto): bump to 0.11.15 (RUSTSEC-2026-0185 patched) 2026-06-24 02:41:35 +02:00
Edouard Vanbelle 289cf19270 feat(drive): user can rename drive
- only owners can rename root folders name (aka the drive name)
    - add UI to rename drive's name
2026-06-24 02:03:21 +02:00
Edouard Vanbelle 6034dd47d9 feat(drive): improve drive edition from owners 2026-06-24 02:03:17 +02:00
Edouard Vanbelle d77846119f feat(drive): UI: add drive edition for admin 2026-06-24 01:20:44 +02:00
Edouard Vanbelle a5b24a7453 feat(drive): add /api/drive
- permit shared drive creation from oxicloud admin (for now)
    - prepare other personal drive creation (Not implemented), need to validate
    quota policies and strategy first
    - add hurl test to verify permissions
2026-06-23 23:50:57 +02:00
Edouard Vanbelle 184520c17a feat(drive): plug trash to drives 2026-06-23 22:04:20 +02:00
Edouard Vanbelle 062bcb701b feat(drive): UI: prepare right management 2026-06-23 20:26:34 +02:00
Edouard Vanbelle 6f1f44f962 feat(drive): prepare E2E tests on drive membership 2026-06-23 20:26:34 +02:00
Edouard Vanbelle 4eb9707774 feat(drive): check user grants on drive
use drive's grant whant handling a file or folder
2026-06-23 20:26:31 +02:00
Edouard Vanbelle 2fe9c9ffeb feat(drive): tell UI the caller role per Drive 2026-06-23 00:09:18 +02:00
Edouard Vanbelle a82bae4136 feat(drive): api to handle Drive grants 2026-06-22 23:53:00 +02:00
M.Schmidt f9999cdd0f fix(webdav): RFC 4918 litmus compliance 2026-06-22 21:37:14 +02:00
Dionisio Pozo 4d9329be19 Merge pull request #518 from BCNelson/bcn/playwright-e2e-ci 2026-06-22 19:05:42 +02:00
Bradley Nelson 0a93123859 style(bench): rustfmt the blob/pool/runtime bench examples
These example targets landed unformatted on main and fail the Rustfmt CI
check (`cargo fmt --all --check`); reformat them so this PR's checks pass.
No logic changes.
2026-06-22 10:37:57 -06:00
Bradley Nelson 02335c0680 test(e2e): run the SvelteKit SPA Playwright suite in CI
The e2e CI job ran the legacy `scenarios/*` specs against the vanilla `static/`
frontend that upstream has since removed, so it could never pass. Point CI at
this repo's SvelteKit SPA suite (tests/e2e/spa) and wire up what it needs:

- CI: build the release binary with `--features plugins` (the admin Plugins-tab
  specs exercise the WASM runtime) and run `npm run test:coverage`, building the
  instrumented SPA with COVERAGE=1 VITE_E2E=1 so the server serves the
  data-testid-instrumented build the specs drive.
- Coverage harness: target 127.0.0.1 instead of `localhost` (which resolves to
  ::1 first on CI runners while the server binds IPv4, so readiness never
  connected) and poll `/ready` for webServer readiness; tee start-server-spa.sh
  output to a log surfaced by an always-run CI step for diagnostics.
- Files page: restore a persistent breadcrumb home link (buildCrumbs returns
  only the path folders, so there was no "go home" affordance), and fix the
  `?file=` deep-link race where the viewer→URL effect stripped the param before
  the listing loaded — a bookmarked preview link now opens the viewer.

All 101 spa specs pass locally.
2026-06-22 10:34:47 -06:00
Dionisio Pozo 0a906341e1 Merge pull request #515 from AtalayaLabs/claude/vibrant-turing-wqj8w8 2026-06-22 11:52:32 +02:00
Claude 601fbdedf9 Revert "perf(blob): one syscall per new chunk in write_blob_bytes"
This reverts ee51b32. The create_new change showed no measurable throughput
benefit — three 9-rep interleaved runs on the same ext4 device swung −12%..+21%
at the 256 KiB CDC size (a negative stat on a warm dentry cache is ~µs, below
the shared-disk noise floor). Applying the same "no change without a measured
win" bar used for the pool-sizing revert: the idiom/TOCTOU angle is real but the
race is already prevented upstream by the PG pin-or-classify serialisation, so
it's defence-in-depth only — not enough to keep an unmeasured change. Reverts
the production change, the bench, and its doc together.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JG5yYZ9s868mJwqT2Qz7ez
2026-06-22 09:42:37 +00:00
Claude ee51b32ba9 perf(blob): one syscall per new chunk in write_blob_bytes (create_new)
Replace the try_exists(stat) + File::create(O_CREAT|O_TRUNC) pair with a single
OpenOptions::create_new (O_CREAT|O_EXCL), treating AlreadyExists as the existing
idempotent skip. One metadata syscall per new chunk instead of two (each a
spawn_blocking round-trip), and O_EXCL closes the check-then-create TOCTOU the
old pair left open (a racing writer could be truncated).

Honest measurement caveat (benches/BLOB-WRITE.md): the wall-clock throughput
effect is BELOW the noise floor of the test environment — three 9-rep
interleaved runs on the same ext4 device swing −12%..+21% at the 256 KiB CDC
size, because a negative stat on a warm dentry cache is ~µs, dwarfed by the
chunk's create+write+flush. So this is justified as a code-quality / correctness
change (canonical idiom, strictly fewer syscalls, closes a TOCTOU, zero
downside), NOT as a benchmarked perf win.

The sibling idea — reusing the File handle for the fsync sweep — is deliberately
NOT done: sync_blobs is a single end-of-stream sweep over all the upload's new
hashes, so retaining handles would hold thousands of FDs open (>ulimit) on a
large upload. The re-open sweep is a deliberate FD-frugal design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JG5yYZ9s868mJwqT2Qz7ez
2026-06-22 09:40:57 +00:00
Claude 18781022be Revert "perf(pools): size image/rayon pools to the CFS quota"
This reverts the image-pool migration (commit 5629ba6). The bench
(bench_pool_concurrency / POOL-CONCURRENCY.md) measured the one pool it could
isolate — the thumbnail decode semaphore — and found flat throughput, p99 AND
peak RSS (137 MiB) from K=1..16: shrink-on-load already makes each decode
RAM-cheap, so sizing it to the CFS quota gains nothing measurable. Adding code
without a measured benefit isn't worth it.

Kept: the effective_parallelism() helper (it has a *measured* win in the Tokio
runtime — benches/RUNTIME.md) and the benchmark itself (reusable). The ffmpeg
video fan-out has a plausible a-priori case (one OS process per permit) but is
left as a future, deliberately-measured change rather than shipped on
speculation. Doc updated to record the decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JG5yYZ9s868mJwqT2Qz7ez
2026-06-22 09:04:37 +00:00
Claude 013090a14e bench: measure peak RSS vs decode permits + record pool-concurrency results
Adds Part B (peak RSS for K concurrent decodes) to bench_pool_concurrency and
records the findings in benches/POOL-CONCURRENCY.md.

Honest result: under a 2-core quota the thumbnail decode pool shows flat
throughput, p99, AND peak RSS (137 MiB) from K=1..16 — shrink-on-load already
made each decode RAM-cheap, so over-permitting costs nothing measurable here.
The effective_parallelism() migration is therefore a correctness/consistency
change with no downside, mainly protecting the transcode + ffmpeg pools (and
extreme host-core/quota ratios) this box can't reproduce — not a throughput win.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JG5yYZ9s868mJwqT2Qz7ez
2026-06-22 09:00:19 +00:00
Claude a504578303 bench: add CPU pool concurrency benchmark (thumbnail decode under a quota)
bench_pool_concurrency drives the real service path (Semaphore(K) gating
spawn_blocking(bench_render_all)) with a gallery of concurrent callers and
sweeps the decode-permit count K, reporting throughput + p50/p99. Run under
taskset to model a CPU quota: it shows the effect of sizing the image pools to
effective_parallelism (K=cores) vs the host count (over-subscribed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JG5yYZ9s868mJwqT2Qz7ez
2026-06-22 08:46:24 +00:00