0fab4ce17d
Phase 0 of the delta-sync plan. Re-uploading a file the user already has (another device, a restore, a duplicate) used to transfer every byte just for the server to discard them as a dedup hit. The frontend now computes the file's BLAKE3 locally and, on a hit, registers the file with a single ~150-byte metadata call. Server — POST /api/files/by-hash: - All checks live in the application service per the AuthZ rule: Create permission on the target folder via the authorization engine, hash ownership via the existing user-scoped query (a non-owned hash returns 404 — same shape as "no such blob" — and emits an instant_upload.rejected audit event), quota on the logical size. - On success: one ref_count bump + the existing save_file_with_blob row registration (compensation included); is_new_blob=false so lifecycle hooks skip thumbnail regeneration. ~10 ms warm. - The storage-usage service is now built before the application services and injected, instead of only living on AppState. Client — WASM BLAKE3 + worker: - wasm/oxicloud-hash: the exact same blake3 crate the server uses, compiled with WASM SIMD128 (~660 MB/s measured) so browser hashes match server content addresses bit for bit. Built by scripts/build-wasm.sh; the artifacts (45 KB wasm + 8 KB glue) are vendored like pdf.js — no npm dependencies, no wasm toolchain needed for regular builds. - static/js/workers/hashWorker.js streams the File in 8 MiB slices off the main thread (constant RAM at any file size). - features/files/instantUpload.js orchestrates: threshold (8 MiB — below it the round-trips cost more than the bytes), user-scoped /api/dedup/check, by-hash registration, and silent fallback to the normal byte upload on any miss, race or unsupported environment. Wired into both uploadFiles and uploadFolderEntries. - biome.json vendors exclusion fixed to cover nested directories (previous vendors were .mjs and never matched the *.js include). Verified end-to-end against PostgreSQL 16: node-driven WASM hash equals the server's content_hash for a 20 MB file; by-hash returns 201 in ~10 ms warm with a 151-byte request (vs 20,971,873 bytes for the byte upload); the copy downloads byte-identical and the manifest ref_count goes 1→2; a second user probing the same hash gets exists:false and 404 plus the audit line; duplicate name → 409, malformed hash → 400; worker and wasm are served with correct MIME (application/wasm). https://claude.ai/code/session_01WdNenpnujNR2sc32XVvwfS
31 lines
1.0 KiB
TOML
31 lines
1.0 KiB
TOML
[package]
|
||
name = "oxicloud-hash-wasm"
|
||
version = "0.1.0"
|
||
edition = "2021"
|
||
description = "BLAKE3 hashing for the OxiCloud web frontend — compiled from the exact same crate the server uses, so client-side hashes match server-side content addressing bit for bit."
|
||
publish = false
|
||
|
||
# Standalone workspace root: this crate is built only by
|
||
# scripts/build-wasm.sh (wasm32 target) and must not join the server
|
||
# workspace — `cargo test --workspace` / clippy on the host have nothing
|
||
# useful to do with a wasm cdylib.
|
||
[workspace]
|
||
|
||
[lib]
|
||
crate-type = ["cdylib"]
|
||
|
||
[dependencies]
|
||
# Same major as the server's Cargo.toml. `wasm32_simd` enables the WASM
|
||
# SIMD128 kernels (~3-4× over the portable implementation); every
|
||
# evergreen browser since 2023 supports it, and the frontend falls back
|
||
# to a plain byte upload when instantiation fails.
|
||
blake3 = { version = "1.8.4", default-features = false, features = ["wasm32_simd"] }
|
||
wasm-bindgen = "0.2"
|
||
|
||
[profile.release]
|
||
# Hashing throughput is the whole point of this module.
|
||
opt-level = 3
|
||
lto = "fat"
|
||
codegen-units = 1
|
||
strip = true
|