Edouard Vanbelle
fd103c46e6
feat(manifests-consistency): add safe repair mode
2026-08-23 23:50:39 +02:00
Edouard Vanbelle
d57400f7d3
fix(i18n): fix too literal translation with jobs
2026-08-23 13:27:09 +02:00
Edouard Vanbelle
06e4df5318
fix(upload): fix race condition in front-end
2026-08-22 08:09:21 +02:00
Edouard Vanbelle
6a11036d96
feat(admin): show active session/users on dashboard
2026-08-21 23:00:49 +02:00
Edouard Vanbelle
2049535516
feat(session): admin UI showing online sessions
2026-08-20 10:42:09 +02:00
Edouard Vanbelle
0d5a726ef4
feat(oidc): explicit rejection reason
...
Show explicitly login rejection (for example when a user does not have a valid
email reported from OIDC but email verification is set)
2026-08-14 13:38:24 +02:00
Edouard Vanbelle
27ab392a35
i18n: fix uncontexted strings
...
For example jobs where translated into "employment" in other language
Remove english text in other languages
2026-08-13 00:30:55 +02:00
Edouard Vanbelle
2be2928ddc
i18n: update all languages
2026-08-09 20:37:09 +02:00
Edouard Vanbelle
a7df46f8f8
feat(sessions): show session origin in admin panel + test
2026-08-09 16:36:42 +02:00
Edouard Vanbelle
321f3ad733
i18n(session): add missing translations
2026-08-09 14:04:42 +02:00
Edouard Vanbelle
bbef8afb7b
fix(logout): reduce unwanted API call during logout
...
- stop trying to refresh session
- display "successfully signed out" rather "your session is expired"
2026-08-09 13:51:35 +02:00
Edouard Vanbelle
9d80bddb5f
feat(oidc): apply i18n
2026-08-08 20:49:09 +02:00
Edouard Vanbelle
21607e3e7f
feat(opaque): improve password change
...
- rebuild the opaque envoloppe
- revoke all other useer's sessions
- send a security email to user
2026-08-06 20:52:17 +02:00
Edouard Vanbelle
015f2da0f7
refactor(backend): normalize naming convention to backend rather storage
...
no ambiguity with the backend rather storage
2026-08-02 14:56:29 +02:00
Edouard Vanbelle
e12d8bcb90
feat(job): show if a job is recverable
2026-08-02 02:40:33 +02:00
Edouard Vanbelle
a58351b7ad
feat(storage key rot): add admin panel
2026-08-02 02:40:33 +02:00
Edouard Vanbelle
bbfb106a32
feat(maintenance): add a maintenance notification during backend migration
2026-08-01 18:46:17 +02:00
Edouard Vanbelle
48cbec8fae
feat(admin ui): too many tabs, change display
2026-07-30 01:14:35 +02:00
Edouard Vanbelle
9fe47a53ea
feat(jobs): add admin call to purge jubs result
2026-07-30 00:39:11 +02:00
Edouard Vanbelle
5527d09618
feat(recoverable-job): check if old blob (no cdc) still remains: notice only
2026-07-29 22:47:07 +02:00
Edouard Vanbelle
0f12399a48
feat(recoverable-job): fix files_consistency to check blob chunk consistency
2026-07-29 22:17:15 +02:00
Edouard Vanbelle
5881968f50
feat(recoverable-job): add progress view
2026-07-29 08:41:19 +02:00
Edouard Vanbelle
4336eca4d1
feat(recoverable-job): add admin page
2026-07-29 08:03:43 +02:00
Edouard Vanbelle
3b31b8911b
feat(breadcrumb): build breadcrumb in 1 API call
...
add /api/folders/{id}/ancestors
this API to iterate parent up to the drive root or the shared folder
this will help UI to build the breadcrumb in 1 API call
and to identify the root element (is it a drive users has access to or
a shared folder ?)
ui: now only 1 API call is now required to build the breadcrumb
2026-07-27 01:33:09 +02:00
Edouard Vanbelle
7718970e75
i18n(search): apply i18n on all filters
2026-07-26 18:18:15 +02:00
Edouard Vanbelle
5a034d390e
i18n: apply missing translation on context menu
2026-07-26 18:18:15 +02:00
Edouard Vanbelle
0cc77f7a36
feat(ui): show a notification if user try to drop a file in another section than /files
2026-07-20 23:19:44 +02:00
Edouard Vanbelle
c286eed3b2
feat(ui): add a dropzone when uploading files from system
...
- add a dropzone on the whole screen
- correct z-index according design system
2026-07-20 23:19:12 +02:00
Edouard Vanbelle
a520afcf7c
feat(ui:items): uploading an item with a swimlane display
...
restore the legacy display with new element uploaded, when swimlane is in place
as the sort is done by server side just add new element in a "new elements" swimlane.
if user continue to scroll down in cursor/pages and item is found from server,
UI remove it from the new element and restore the position
other option: is user refresh it's page, server will restore the natural order
2026-07-20 22:11:00 +02:00
Edouard Vanbelle
5b8fb68b30
feat(items): clarify column names
2026-07-20 21:25:52 +02:00
Edouard Vanbelle
63589e595e
refactor(ui): recent: add a quick button to remove item from recent
2026-07-20 12:26:45 +02:00
Edouard Vanbelle
75ae837f1a
refator(ui): merge of ResourceList part1
2026-07-20 12:26:45 +02:00
Edouard Vanbelle
35f4984463
i18n: add missing key in admin > users section
2026-07-19 16:26:11 +02:00
Edouard Vanbelle
2a08fe83ae
feat(user): admin can promote external user + security on deletion
...
promotion by admin of external user into internal possible
deletion of a user request admin to enter it's email, this is to prevent any miss click
2026-07-19 16:26:11 +02:00
Edouard Vanbelle
e003a8c55b
feat(admin): display external users for security reasons
2026-07-19 16:26:11 +02:00
Edouard Vanbelle
5982efd783
i18n(drive): correct locales for drive sections
2026-07-19 16:26:07 +02:00
Edouard Vanbelle
05dfda9aa3
feat(drive): add quota update handler
...
per today: admin only can update quota
shared drive can have quota updated (personal drive's quota belong to user's quota)
2026-07-19 16:25:41 +02:00
Edouard Vanbelle
a6427fc028
feat(drive): add readonly policy
...
permmit admin to freeze a drive, trash janitor background job is also disabled for this drive
2026-07-16 01:02:15 +02:00
Edouard Vanbelle
1fa1966fbe
feat(upgrate): add i18n for account upgade
2026-07-14 11:40:16 +02:00
Dionisio Pozo
5ae551a93d
Merge pull request #577 from EdouardVanbelle/feat/users-perfs-and-filter-dotfiles
...
feat: users prefs server side + filter dotfiles + filter shares by resource type
2026-07-14 09:07:09 +02:00
Edouard Vanbelle
e94063d96a
test(login/register): via password or magic-link
...
Password login
┌─────┬────────────────────────────────────────────────────┬────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────┐
│ # │ Case │ Where │ Assertion │
├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ L1 │ Login by username │ auth_login.hurl Case 1 │ 200 + access_token, user.email match │
├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ L2 │ Login by email (dispatch on @) │ auth_login.hurl Case 2 │ 200, same session shape as L1 │
├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ L3 │ Bad password on username path │ auth_login.hurl Case 3 │ 403 anti-enum │
├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ L4 │ Bad password on email path │ auth_login.hurl Case 4 │ 403 anti-enum (same shape as L3) │
├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ L5 │ Unknown username │ auth_login.hurl Case 5 │ 403 anti-enum (same shape as L3) │
├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ L6 │ Unknown email │ auth_login.hurl Case 6 │ 403 anti-enum (same shape as L3) │
├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ L7 │ /api/auth/oidc/providers reports methods correctly │ auth_login.hurl Case 7 │ password_login_enabled: true, magic_link_login_enabled: true, require_verified_email: false │
└─────┴────────────────────────────────────────────────────┴────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘
Password registration
┌─────┬───────────────────────────────────────────────────┬──────────────────────────────┬─────────────────────────────────────────────────────────┐
│ # │ Case │ Where │ Assertion │
├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼─────────────────────────────────────────────────────────┤
│ R1 │ Classic username + email + password → uniform 200 │ registration.hurl Step 2 │ anti-enum message contains "request received" │
├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼─────────────────────────────────────────────────────────┤
│ R2 │ Login after register works │ registration.hurl Step 2b │ 200 + session for the new user │
├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼─────────────────────────────────────────────────────────┤
│ R3 │ Email collision → uniform 200 (no rewrite) │ registration.hurl Steps 8-10 │ attacker password doesn't work; original account intact │
├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────┤
│ R4 │ Username collision → uniform 200 │ registration.hurl Step 11 │ same anti-enum shape │
├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────┤
│ R5 │ Off-domain rejection │ registration.hurl Step 12 │ 403 RegistrationDomainNotAllowed │
├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────┤
│ R6 │ Case-insensitive domain match │ registration.hurl Step 12b │ uniform 200 on charlie@EXAMPLE.COM │
└─────┴───────────────────────────────────────────────────┴──────────────────────────────┴────────────────────────────┘
Magic-link registration (email-only signup)
┌─────┬──────────────────────────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────┐
│ # │ Case │ Where │ Assertion │
├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ MR1 │ Email-only signup → welcome mail queued │ registration.hurl Step 3 │ uniform 200 + browser-binding cookie set │
├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ MR2 │ Welcome mail contains magic-link URL │ registration.hurl Step 4 │ captured from mock SMTP │
├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ MR3 │ PR 22 cross-browser confirmation page │ registration.hurl Step 5a │ 200 HTML "different browser" │
├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ MR4 │ Cookie-bound redemption lands on SPA │ registration.hurl Step 5b │ 302 → /files (SvelteKit route, post-migration) │
├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ MR5 │ email_verified_at stamped after redemption │ registration.hurl Step 6 │ field present on /api/auth/me │
├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ MR6 │ Second magic-link post-signup │ registration.hurl Step 7 │ uniform 200 │
├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤
│ MR7 │ Profile PATCH — no-op, name set, empty-string rejected, username-taken 409, claim-once 409, etc. │ registration.hurl Steps 6a–6i │ full profile lifecycle │
└─────┴──────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────┘
Magic-link login (existing account)
┌─────┬──────────────────────────────────────────────────────────┬──────────────────────────────────────┬───────────────────────────────────────┐
│ # │ Case │ Where │ Assertion │
├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤
│ ML1 │ Baseline password login still works │ auth_magic_link_login.hurl Steps 1-2 │ 200 │
├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤
│ ML2 │ magic-link/send with email identifier │ auth_magic_link_login.hurl Step 3 │ uniform 200 + cookie │
├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤
│ ML3 │ magic-link/send with username identifier (dispatch on @) │ auth_magic_link_login.hurl Step 4 │ uniform 200 │
├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤
│ ML4 │ Password-user policy: mail actually sent │ auth_magic_link_login.hurl Step 5 │ SMTP capture proves permit_magic_link_for_password_users in effect │
├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤
│ ML5 │ Redemption creates a session │ auth_magic_link_login.hurl Steps 6-7 │ 302 → /files, /api/auth/me returns the same user │
├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤
│ ML6 │ Anti-enum on unknown identifier │ auth_magic_link_login.hurl Step 8 │ same uniform 200 shape as ML3 │
└─────┴──────────────────────────────────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────┘
OIDC
┌─────┬────────────────────────────────────────────────────────────────────────┬───────────────────┬────────────────────────────────────────────────────────────────────────────────────────────┐
│ # │ Case │ Where │ Assertion │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O1 │ Setup local admin (bootstrap) │ oidc.hurl Step 1 │ 201 │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O2 │ Providers endpoint — OIDC visible │ oidc.hurl Step 2 │ enabled: true, provider_name: MockSSO, password_login_enabled: true, magic_link_login_enabled: false (OIDC-master rule) │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O2b │ Magic-link/send refused (endpoint layer) │ oidc.hurl Step 2b │ 403 MagicLinkLoginDisabled — proves the policy gate fires, not a 503 SMTP-unwired │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O3 │ Authorize redirect includes PKCE + state │ oidc.hurl Step 3 │ 307 to fake IdP │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O4 │ IdP round-trip + JIT provisioning │ oidc.hurl Step 4 │ Callback lands on /login?oidc_code=… │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O5 │ Code exchange → session cookies │ oidc.hurl Step 5 │ 200 + all three cookies │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O6 │ JIT profile mapping (name, given/family, picture, groups → admin role) │ oidc.hurl Step 6 │ every claim reflected on /api/auth/me │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O7 │ Refresh rotation on OIDC session │ oidc.hurl Step 7 │ new access/refresh/CSRF cookies │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O8 │ Refreshed cookies authenticate │ oidc.hurl Step 8 │ 200 on /api/auth/me │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O9 │ Repeat login = same local user (no dup) │ oidc.hurl Step 9 │ user_id stable │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O10 │ Anti-takeover: unverified email → refused │ oidc.hurl Step 10 │ 401/403 │
├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤
│ O11 │ One-time code replay refused │ oidc.hurl Step 11 │ second /exchange → 401 │
└─────┴────────────────────────────────────────────────────────────────────────┴───────────────────┴────────────────────────────────────────────────────────────────────────────────────────────┘
test
2026-07-14 03:16:25 +02:00
Edouard Vanbelle
5aaf49859e
feat(user-perf): add ui user-perf + dotfile filter
...
- add resource kind filter (file, folder, drive) in shared section (localStorage stored)
- add user preferences serverside store
- add client side dotfile filter (show/hide dotfiles) (user perf stored, default: dotfiles are shown)
for security trashed dotfile are always displayed
protection added: if a folder has only hidden items, a notification invite user to display it
if a user rename or create a hidden item, a notification tells it to user
2026-07-13 20:27:52 +02:00
moduvoice
a3801f5836
i18n: complete Korean (ko) locale
...
The ko.json locale existed but was missing 377 of 1392 keys (~27%),
covering entire feature areas added since the initial translation:
admin plugin management, storage/OIDC/SMTP settings, photos, music
playlists, device pairing, search filters, share dialogs, and more.
Filled in all missing keys with natural Korean translations matching
the existing tone and terminology in the file (파일/폴더/공유/업로드
등), verified full key parity with en.json (1392/1392) and matching
{{placeholder}} interpolation tokens on every translated string.
2026-07-11 23:10:35 +07:00
Edouard Vanbelle
0870990e1b
fix(locale): correct IT i18n
2026-07-05 23:31:48 +02:00
albanobattistella
bde40c6042
Update Italian translation
2026-07-05 13:49:59 +02:00
Edouard Vanbelle
09339ea63f
feat(drive): UI: show policiesto drive's members
...
and add tests
2026-07-01 22:35:08 +02:00
Edouard Vanbelle
26d3c692ba
feat(drive): policies management from UI
2026-06-26 18:57:35 +02:00
DioCrafts
db97a88956
feat(upload): skip unreadable files (FIFOs/sockets) + auto-reload on new deploy
...
Two robustness fixes behind the recurring "folder upload stuck at ~93%" reports.
1. Skip non-regular files up front. A copied s6/runit service tree contains
FIFOs (e.g. supervise/control named pipes) that report a size but BLOCK
FOREVER when the browser reads them — the deterministic ~8-files-short that
no retry/watchdog tweak could fix. uploadBatch/uploadTree now probe each
file's first chunk against a 3 s timeout (partitionReadable), upload only the
readable ones, and report the rest: "N uploaded · M skipped (not regular
files)". Progress runs over the uploadable count, so it reaches 100% instead
of parking at 93% while a lane hangs on a pipe.
2. Auto-reload on a new deploy. svelte.config.js polls _app/version.json
(60 s); the root layout reloads itself when the deployed build changes —
unless an upload is in flight — so an open tab can't keep running stale code
after a rebuild (the recurring "my fix isn't applied" trap).
npm run check: 0 errors, 58 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-21 12:08:29 +02:00
DioCrafts
6123843dd0
fix
2026-06-20 19:13:18 +02:00
DioCrafts
2ebc4e82b2
feat(upload): batch dedup-check + instant uploads + resilient parallel uploads
...
Backend:
- POST /api/dedup/check-batch — returns the subset of submitted whole-file
BLAKE3 hashes the caller already owns, in one query (user-scoped,
anti-enumeration via idx_files_blob_hash). Lets a client learn which of N
files it can skip with a single round trip.
(dedup_service::user_owned_blob_references, dedup_handler, routes) + tests.
Frontend — upload pipeline:
- Instant ("by-hash") upload for content the caller already owns: hash every
in-band file, ONE /api/dedup/check-batch, create the owned ones with zero
content bytes, upload only the rest. Covers all sizes below the 8 MB delta
threshold (delta handles larger files). vendor/hashWasm computes the
whole-file BLAKE3 on the main thread.
- Resilient parallel uploads: bounded concurrency (4) + a per-file deadline,
so one stuck/slow/failing file no longer freezes the whole batch — it blocks
only its own lane and times out / is skipped while the rest proceed. Quota
exhaustion stops the run early; partial results are reported ("N uploaded,
M failed").
- Folder uploads (uploadTree) show live bell progress + a final result and go
through the same dedup + parallel pipeline.
- Storage bar ("Almacenamiento") refreshes after uploads/deletes
(session.refresh) instead of showing the stale login value.
Frontend — i18n / UI fixes:
- Fix literal {{count}} and {{percentage}}/{{used}}/{{total}} (param-name
mismatches) in the selection toolbar and storage line; add es strings.
- Remove the underline on user-menu link rows.
Benchmark (uploadStrategies.bench.test.ts) compares baseline / per-file / batch:
the batch collapses N per-file probes into one check (e.g. a WAN 1000-file run
drops from 1700 to 1001 round trips) while matching per-file's byte savings.
Also includes in-progress group virtual-description i18n work present in the
working tree (groups.ts, ResourceList, locale `groups` keys).
Verified: cargo clippy -D warnings (clean), backend 448 tests; frontend
npm run check (clean), 58 unit tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-20 17:03:30 +02:00