- 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
- Add type aliases (FileRow, FolderRow, FolderRowPaginated, FolderRowOptUser) to reduce type complexity
- Simplify redundant closures in app_password_handler and webdav_handler
- Remove needless borrow in auth_handler
- Collapse nested if/let chains in login_lockout, webdav_lock, auth, rate_limit
- Box LockEntry in acquire() Err variant to fix large enum variant warning
- Rename DeviceCodeStatus::from_str to parse to avoid should_implement_trait lint
- Add #[allow(clippy::too_many_arguments)] and #[allow(clippy::result_unit_err)] where appropriate
- Convert integration_tests from cargo feature to custom cfg attribute
- Add check-cfg lint config in Cargo.toml for integration_tests cfg
Remove async-trait dependency and use native Rust async fn in traits.
Replace Arc<dyn Trait> with Arc<ConcreteType> throughout the codebase
to enable monomorphization and eliminate dynamic dispatch overhead.
Key changes:
- Remove write-behind cache (no implementation existed)
- Fix should_transcode static method call
- Use ContactStorageAdapter directly instead of dyn AddressBookUseCase
- Clean up unused trait imports across services and DI
https://claude.ai/code/session_01EbAFEfyJNLRmJHmmYDX3Tt
Replace list_files_in_subtree (fetch_all → Vec) with stream_files_in_subtree
that returns a Pin<Box<dyn Stream<Item = Result<File/FileDto>>>> backed by a
PostgreSQL cursor via sqlx::fetch().
Changes:
- FileReadPort::stream_files_in_subtree() returns streaming cursor (no default)
- FileRetrievalUseCase::stream_files_in_subtree() maps File→FileDto on the fly
- FileBlobReadRepository: async_stream::try_stream! + sqlx::fetch() cursor
- batch_operations: consume stream into HashMap incrementally
- zip_service: consume stream into HashMap incrementally
- All stubs/mocks updated (return empty stream)
Eliminates:
- Double allocation: Vec<(9-tuple)> + Vec<File> materialized simultaneously
- Unbounded RAM proportional to subtree size (was ~500 bytes × N files)
- Latency: callers blocked until last row fetched from PG
RAM is now O(folders) for the HashMap, not O(files).
Replace BFS traversal that issued 2 SQL queries per folder (list_files +
list_folders) with 2 total queries using PostgreSQL ltree <@ operator:
1. list_subtree_folders: single GiST-indexed scan for all folders
2. list_files_in_subtree: single GiST-indexed join for all files
Files are grouped by folder_id in a HashMap, then iterated in directory
order (folders pre-sorted by path from SQL).
Changes across 4 architecture layers:
- Domain: FolderRepository::list_subtree_folders (default impl)
- Application ports: FolderUseCase, FileRetrievalUseCase, FileReadPort
- Application services: FolderService, FileRetrievalService passthroughs
- Infrastructure: PG implementations + ZipService rewrite
Query count: O(N) → O(1). Latency for 100-folder tree: ~200 round-trips → 3.
ZipWriter<std::fs::File> performed every write_all() as a blocking
write(2) syscall on the Tokio worker thread, sequestering it for
10-100ms per file (0.8-12s for a 100-file ZIP).
Replace with async_zip::ZipFileWriter backed by a buffered
tokio::fs::File:
- All I/O (headers, deflate chunks, central directory) is fully async
- 256 KB BufWriter minimises syscall count
- Zero Tokio worker blocking during ZIP creation
- Streaming per-chunk writes keep RAM O(1) regardless of archive size
- Removed dead From<zip::result::ZipError> for DomainError impl
- zip crate retained for batch_operations.rs (separate concern)
Solution C - Hybrid temp-file streaming:
- ZipPort trait now returns NamedTempFile instead of Vec<u8>
- ZipService writes to a temp file via ZipWriter<std::fs::File> (O(1) RAM)
- Files are read in 64KB stream chunks via get_file_stream() instead of get_file_content()
- HTTP response streams the temp file via ReaderStream (never loads full ZIP in memory)
- Temp file auto-deleted on drop after response completes
- Removed dead imports (HeaderName, HeaderValue, Cursor, Read)
Standardize code formatting across all 173 Rust source files
using rustfmt. No functional changes - purely cosmetic.
This establishes a consistent code style baseline for the
project going forward.