Commit Graph

440 Commits

Author SHA1 Message Date
Dionisio Pozo e4bcab0488 Merge pull request #181 from vhsdream/fix/admin 2026-03-07 09:05:28 +01:00
arch Cloud User 228d698223 Fix(admin/csp) admin.html: restore removal of inline event handlers; use theme-init.js for theming 2026-03-06 20:54:18 -05:00
Dionisio 69d25d46d6 perf: eliminate redundant PG round-trip for blob_hash after image upload
upload_file_inner now returns (FileDto, blob_hash) tuple, propagating
the BLAKE3 hash computed during the hash-on-write spool directly to
upload_file_with_thumbnails. This removes the unnecessary
get_blob_hash() SQL query that was fetching a value already in memory.

Saves ~0.5-2ms + 1 PG pool connection per image upload.
2026-03-07 00:10:56 +01:00
Dionisio 1a1e635bd3 quick fix 2026-03-06 23:46:03 +01:00
Dionisio 625453a3c9 chore: track .cargo/config.toml (target-cpu=native build config)
- Update .gitignore to allow .cargo/config.toml while ignoring other .cargo contents
- Include the target-cpu=native config that was created in previous commit but missed due to .gitignore
2026-03-06 23:37:35 +01:00
Dionisio 6c34499bbe perf: enable target-cpu=native for SIMD-optimized BLAKE3/image processing
- Add .cargo/config.toml with target-cpu=native for x86-64 and aarch64
- Update Dockerfile to pass RUSTFLAGS in both cacher and builder stages
- Enables AVX2/AVX-512 (Intel/AMD) or NEON (ARM/Apple Silicon) codepaths
- 2-3x speedup for BLAKE3 hashing and image transcoding
2026-03-06 23:28:37 +01:00
Dionisio 42b242204c perf: enable HTTP compression (gzip + Brotli) with smart predicate
- Apply CompressionLayer globally with content-type filtering
- Compress: JSON, XML, HTML, CSS, JS (60-80% bandwidth savings)
- Skip: images, video, audio, PDF, ZIP, gzip, tar, octet-stream
- Min threshold 256 bytes to avoid CPU waste on tiny responses
- Compatible with future reverse proxy (Content-Encoding passthrough)
2026-03-06 22:59:48 +01:00
Dionisio fc78dcd35d perf: eliminate Vec<char> allocation in split_sql_statements
- Replace sql.chars().collect::<Vec<char>>() with direct byte-level
  iteration over sql.as_bytes()
- Saves ~140 KB heap allocation for 35 KB schema (4× input size)
- Remove unused _start variable
- SQL is ASCII-safe so byte comparison is sufficient for all delimiters
2026-03-06 22:44:04 +01:00
Dionisio 55936699ce perf: zero-alloc File → FileDto via into_parts() ownership transfer
- Add FileParts struct and File::into_parts() to yield owned fields
- Rewrite From<File> for FileDto to move id, name, path, folder_id,
  owner_id by ownership instead of .to_string() copies
- Eliminates ~5 heap allocations per file (5000 saved per 1000-file listing)
2026-03-06 22:37:31 +01:00
Dionisio d4a7cb0414 perf: replace blocking Path::exists() with async try_exists() in dedup hot path
- store_bytes: blob_path.exists() → fs::try_exists(&blob_path).await
- store_from_file: blob_path.exists() → fs::try_exists(&blob_path).await (2 sites)
- Prevents synchronous stat() syscalls from blocking Tokio worker threads
  during concurrent uploads
2026-03-06 22:27:43 +01:00
Dionisio 3d4156673c perf: use blake3 mmap_rayon for file hashing — zero heap allocation
- Replace std::fs::read() + update_rayon() with update_mmap_rayon()
  for file hashing, eliminating full-file heap allocation (500MB file
  no longer needs 500MB of RAM to hash)
- Enable blake3 'mmap' feature in Cargo.toml
- Lower hash_bytes rayon threshold from 10MB to 128KB
- Remove dead constants HASH_BLOCK_SIZE and RAYON_HASH_THRESHOLD
2026-03-06 22:14:43 +01:00
Dionisio Pozo 6e9c88f06c Fix formatting in architecture diagram in README 2026-03-06 21:46:58 +01:00
Dionisio Pozo eb8d64383c Merge pull request #178 from jaredwolff/fix/initial-load-race-condition 2026-03-06 20:34:09 +01:00
Jared Wolff 5b5a9173bc fix(auth): resolve race condition causing files not to load on initial visit
The cached-user-data path in checkAuthentication() fired resolveHomeFolder()
and loadFiles() concurrently with refreshUserData() using non-blocking .then()
chains. When the session cookie was expired, the folder/file API calls received
401 errors before the session could be refreshed. Now awaits session validation
before loading files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:01:08 -05:00
Dionisio 9aa35aa0ea quick fix 2026-03-06 13:18:36 +01:00
Dionisio Pozo 22b5c72d11 Merge pull request #177 from jaredwolff/fix/nc-android-discovery 2026-03-06 06:38:17 +01:00
Jared Wolff 4617c1c1df fix(ui): fix list view column spacing, rubber-band selection, and clippy warnings
Add column-gap to list header/items, prevent rubber-band selection from
swallowing clicks on list headers and file items, preserve rubber-band
selection after mouseup, and extract type aliases to resolve clippy
type_complexity warnings.
2026-03-05 21:36:14 -05:00
Jared Wolff d8eecbd9ca fix(nc): enable Nextcloud Android app connectivity and uploads
- Add /remote.php/dav discovery endpoint for Android app server detection
- Add /index.php/204 connectivity check endpoint (returns 204 No Content)
- Redirect login flow to nc:// deep link for mobile credential delivery
- Support GET/HEAD on folders (NC clients use as existence checks)
- Recursive MKCOL to create missing parent directories
- Fix single-file PROPFIND returning empty multistatus response
- Strip instance suffix from preview fileId (e.g. "00000326ocnca")
- Add recommendations stub endpoint
2026-03-05 20:42:54 -05:00
Dionisio Pozo 3fc408d72d Merge pull request #172 from jaredwolff/feat/exif-metadata
feat(photos): EXIF metadata extraction and Photos timeline view
2026-03-05 23:42:10 +01:00
Jared Wolff c53a0602ec fix: resolve clippy warnings (collapsible_if, manual_clamp)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:39:34 -05:00
Dionisio Pozo ce709cc58d Merge pull request #171 from jaredwolff/fix/webdav-path-translation
fix: WebDAV path translation, admin modals, cross-device storage, and MKCOL
2026-03-05 23:36:40 +01:00
Jared Wolff 6a84a5c44e fix(photos): fix SQL type mismatch, pagination panic, missing icons, and add day/month/year grouping
- Fix photos endpoint 500: remove ::uuid cast on user_id WHERE clause (VARCHAR column)
- Fix pagination underflow panic when total_pages is 0
- Add missing 'images' and 'play' icons to SVG icon registry
- Add day/month/year grouping toggle with localStorage persistence
- Improve grid spacing and group header styling per mode
- Add i18n translations for grouping labels (all 9 locales)
2026-03-05 17:32:28 -05:00
Jared Wolff 53e4f5afe6 feat(photos): add Photos timeline view with lightbox and infinite scroll
Backend: new GET /api/photos endpoint with cursor-based pagination that
queries image/video files sorted by EXIF captured_at (falling back to
created_at), joining file_metadata for sort dates.

Frontend: dense photo grid grouped by day with lazy-loaded thumbnails,
IntersectionObserver infinite scroll, multi-select with batch
download/delete, and a full-screen lightbox with prev/next navigation,
EXIF metadata display, and download/favorite/delete toolbar.

Includes navigation wiring, CSS (with dark theme), and i18n translations
for all 9 locales.
2026-03-05 17:32:28 -05:00
Jared Wolff 69fe3a8b07 feat(photos): add EXIF metadata extraction and storage
Extract EXIF orientation, GPS coordinates, camera info, and timestamps
from uploaded images using kamadak-exif. Store metadata in a new
file_metadata PG table. Apply EXIF orientation to thumbnail generation
so images display correctly. Add /api/files/{id}/metadata endpoint.
2026-03-05 17:32:28 -05:00
Jared Wolff cea7665a43 fix(storage): handle cross-device rename and MKCOL on existing folders
- dedup_service: fall back to copy+delete when rename() fails with
  EXDEV (os error 18), which occurs when temp and blob dirs are on
  different filesystems
- NC webdav_handler: return 405 instead of 500 when MKCOL targets an
  existing folder (RFC 4918 §9.3.1)
2026-03-05 17:32:21 -05:00
Jared Wolff 633c1bbe97 fix(admin): fix invisible modals and add missing button icons
- Add hidden class to quota, create-user, and reset-password modal divs
- Override opacity:0 from modals.css with opacity:1 in admin.css
- Add crown and ban SVG icons to icon registry for toggle-role and
  deactivate buttons
2026-03-05 17:20:07 -05:00
Jared Wolff f730f576e7 fix(webdav): translate paths for all operations and fix MKCOL recursive creation
- Add resolve_webdav_path() helper that prepends user's home folder
  prefix to raw WebDAV paths before routing to handlers
- Rewrite handle_mkcol to walk path segments, creating folders with
  proper parent_id chain instead of relying on path-only lookup
- Fix double path stripping in update_file_streaming where
  get_parent_folder_id received an already-stripped parent path
  instead of the full file path it expects

Fixes #165
2026-03-05 17:20:07 -05:00
Dionisio Pozo f20e8bf2dc Merge pull request #167 from jaredwolff/feat/app-passwords-ui
feat(profile): add App Passwords UI to profile page
2026-03-05 23:16:48 +01:00
Jared Wolff d5d69d9002 fix(profile): hide auth-error and main-content divs by default
Add hidden class to both #auth-error and #main-content so they only
show when JS explicitly displays them.
2026-03-05 17:07:48 -05:00
Jared Wolff 405cb54240 fix(profile): show active/revoked status for app passwords
Add Status column to app passwords table with Active/Revoked badge.
Only show the revoke button for active passwords.
2026-03-05 17:02:43 -05:00
Jared Wolff 9adcdc436f fix(auth): use middleware-based auth for app-password API endpoints
The Nextcloud integration added duplicate /api/auth/app-passwords
handlers that only accepted Bearer tokens, breaking cookie-authenticated
browser sessions (profile page). Remove the duplicates and mount the
original app_password_handler routes which use CurrentUser from the auth
middleware, supporting all auth methods (cookie, Bearer, Basic).
2026-03-05 16:56:45 -05:00
Jared Wolff 444b5260b5 feat(profile): add App Passwords UI to profile page
Add complete app password management UI with generate, copy, revoke
functionality. Includes auto-generated client session passwords section,
CSP-compliant event handlers, and dark mode support.
2026-03-05 16:41:04 -05:00
Dionisio Pozo c57d95e889 Merge pull request #170 from jaredwolff/fix/csp-inline-styles 2026-03-05 22:38:51 +01:00
Jared Wolff 4f7e79f605 style: collapse nested if-let in JWT secret config (clippy) 2026-03-05 16:37:47 -05:00
Jared Wolff a404342559 fix(pagination): prevent usize underflow when total_pages is zero
When there are no items, total_pages is 0 and `total_pages - 1`
causes a subtract-with-overflow panic on usize. Guard the subtraction
with a `total_pages > 0` check.
2026-03-05 16:28:28 -05:00
Jared Wolff c08926b817 fix(csp): remove all inline styles, scripts, and event handlers for strict CSP compliance
Replace ~50 inline style="" attributes with CSS classes, move 3 inline
<script> blocks to external JS files, replace all inline event handlers
(onclick, onerror) with addEventListener, and remove createElement('style')
from icons.js. All changes support the strict CSP policy (style-src 'self';
script-src 'self') without weakening it.
2026-03-05 16:28:28 -05:00
Dionisio f2d35ca792 feat: auto-persist JWT secret, remove setup token requirement
- JWT secret auto-generates and persists to <STORAGE_PATH>/.jwt_secret
- Remove setup token: first admin setup is open until system initialized
- Fix schema.sql: move CREATE EXTENSION pg_trgm/ltree to top
- Update login UI and auth.js to remove setup token fields
2026-03-05 22:12:53 +01:00
Dionisio Pozo c77ce202c6 Merge pull request #160 from zjean/feature/nextcloud-compatible-api
Feature/nextcloud compatible api
2026-03-05 21:51:20 +01:00
zjean 190527edfb style: apply rustfmt formatting to fix CI
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:28:51 +01:00
zjean aa666f5bbb fix: resolve clippy warnings for --all-features CI build
Update share_service test impl to match upstream trait changes
(requester_id params, verify_shared_link_password returns ShareDto).
Fix map_or, collapsible_if, dead_code, too_many_arguments warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:23:00 +01:00
Dionisio d0118bf07d quick fix 2026-03-05 21:21:45 +01:00
zjean 45c60faeb5 fix: resolve all clippy warnings for CI (async_fn_in_trait, collapsible_if, type_complexity, dead_code)
- Allow async_fn_in_trait lint crate-wide (internal project, 413 warnings)
- Add integration_tests feature to Cargo.toml to fix unexpected cfg warnings
- Collapse nested if statements into single conditions (13 locations)
- Add type_complexity allows on pg repository functions (12 locations)
- Fix dead code warnings in test modules with allow attributes
- Fix E0599 by gating new_stub() for integration_tests feature
- Add result_unit_err and result_large_err allows where appropriate
- Apply rustfmt formatting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:48:03 +01:00
zjean a7de63d80f fix: resolve clippy warnings (unused mut, from_str, result_large_err)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:47:09 +01:00
zjean e6e9c5149a style: apply rustfmt formatting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:46:28 +01:00
zjean fdbb144cd8 fix(nextcloud): detect MIME type via magic bytes instead of trusting client header
Nextcloud app uploads sent application/octet-stream as Content-Type,
causing images to not be recognized. Now both WebDAV PUT and chunked
upload paths call refine_content_type() which detects via magic bytes,
then extension, then falls back to the client header. Also fixes
update_file() which previously hardcoded application/octet-stream.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:46:28 +01:00
zjean 40b269c4eb fix: schema init, duplicate routes, and image preview bugs
- Move pg_trgm extension creation before CalDAV indexes that depend on it
- Remove duplicate app-password route registration that caused panic
- Fix missing comma in language selector array (Dutch entry)
- Await async canEdit() in file click handler (Promise was always truthy)
- Detect images by extension fallback when mime_type is octet-stream
  (files uploaded via Nextcloud WebDAV API lack correct mime types)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:46:28 +01:00
zjean 54eedf5483 feat(nextcloud): add Nextcloud-compatible API layer
Implement a complete Nextcloud client compatibility layer so that
Nextcloud desktop/mobile sync clients can connect to OxiCloud.

Key additions:
- Login Flow v2 (device auth) with OIDC bridge support
- WebDAV handler compatible with Nextcloud clients (PROPFIND, GET,
  PUT, DELETE, MKCOL, MOVE, COPY, HEAD, PROPPATCH)
- OCS API endpoints (user info, capabilities, notifications stubs,
  sharees, unified search)
- Basic Auth middleware with app password verification, account
  lockout integration, and blake3-keyed auth cache
- App password management: create, list, revoke via both native
  API (JWT-authenticated profile page) and Nextcloud OCS endpoints
- Nextcloud file ID mapping (oc:fileid) with persistent DB storage
- Chunked upload support (Nextcloud v2 chunking protocol)
- Trashbin WebDAV interface
- Avatar (SVG placeholder) and preview (redirect) handlers
- User profile page with app password management UI
- URL user validation on all DAV routes (403 on mismatch)
- Database schema for app_passwords and nextcloud_object_ids tables

All services are behind a `nextcloud.enabled` config flag and
cleanly separated under src/interfaces/nextcloud/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:46:07 +01:00
Dionisio Pozo ecd1a8148a Merge pull request #164 from jaredwolff/thumbnail-fix
fix: auth cookie support, thumbnail display
2026-03-05 20:35:41 +01:00
Jared Wolff aa96eecc79 fix: restore locales_path after removing embed-assets commit
The embed-assets commit made locales_path optional, and main.rs was
updated to pass None. Removing that commit restored the PathBuf
signature but left main.rs passing None. Restore the original
locales_path initialization.
2026-03-05 13:43:06 -05:00
Jared Wolff f2bd220307 fix(admin): support HttpOnly cookie auth in admin_guard
admin_guard() only checked Authorization: Bearer header, ignoring the
oxicloud_access HttpOnly cookie used by browser sessions. All admin
endpoints (dashboard, OIDC settings, etc.) returned 401 for logged-in
users. Fall back to cookie_auth::extract_cookie_value() when no Bearer
token is present.
2026-03-05 13:43:06 -05:00