- Successful SSO /auth/api/validate responses are cached per credential
and perm/renew URL for 10s, so watch websocket re-checks do not hammer
the auth backend. A POST to the logout endpoint purges all cached
entries for the request's credentials immediately, so logout/login
flows are not served stale successes.
- The watch websocket now re-validates auth before each forwarded
message and every 10s when idle (SSO and built-in sessions alike).
When the session is gone the client gets an auth error message and
the socket is closed, instead of streaming updates forever.
- Token-authenticated (API/share token) sockets are exempt from
re-validation; they are checked once at handshake.
Production symptom: previews of all types (pdf/pyvips/onlyoffice) start
hitting the 10s timeout and never recover until server restart, while the
rest of the server stays healthy.
Root cause (reproduced on Python 3.12): workers were spawned with
stderr=PIPE that nothing drained after startup. Once the OS pipe buffer
filled from accumulated worker tracebacks and library warnings, asyncio
flow control stopped the parent reading it and the worker blocked forever
mid-request on a stderr write. The 10s timeout then fired, but
_replace_worker hung forever in proc.wait() even after kill() — the
flow-control-paused pipe transport never sees EOF — permanently wedging
one dispatcher per stuck worker. Once all dispatchers were stuck, every
preview request timed out. Restart cleared it.
Fixes:
- Spawn workers with inherited stderr (stderr=None) so worker diagnostics
go straight to the server log and no undrained pipe can exist.
- Bound proc.wait() in worker kill() with a 5s grace timeout so a wedged
transport can never hang a dispatcher; log the worker pid instead.
- Guard the dispatch loop with an outer exception handler so a dispatcher
can never die silently and shrink pool capacity.
- Retry failed worker replacement spawns with 1s-30s backoff instead of
silently shrinking the pool.
- Fix latent crash: except-tuple referenced msgspec.json.DecodeError,
which does not exist in the installed msgspec; any protocol failure
would itself raise AttributeError. Use msgspec.DecodeError.
- Worker: redirect Python-level sys.stdout to stderr in persistent mode
and keep the raw buffer solely for the binary protocol, so a library
print() can never corrupt the command channel again (cf. the pymupdf
deprecation warning that crashed workers at startup).
- Worker: close the pymupdf document explicitly in process_pdf.
- Log worker pid on timeout/protocol/checksum failures, and log failed
kills and replacement retries, for future production diagnostics.
Add tests/test_preview_pool.py with an end-to-end regression recreating
the wedged-worker setup (piped, undrained stderr) plus kill-grace,
respawn-retry and dispatcher-survival tests.
- Add cista/util/diskspace.py with MIN_FREE_BYTES limit and cached
check_free_space() helper.
- Check available space in File.write() before ftruncate/write.
- Catch ENOSPC in File.write and re-raise as InsufficientStorageError.
- upload_file_chunk catches both proactive and ENOSPC errors and
returns HTTP 507 Insufficient Storage.
- Add tests for low-disk rejection and ENOSPC handling.
Replace Aspose.Words with OnlyOffice Document Server for generating
bitmap previews of office documents (Word, Excel, PowerPoint, etc.).
Backend:
- Add cista/onlyoffice.py conversion client
- Convert office docs directly to PNG via OnlyOffice, then AVIF via pyvips
- Make office previews optional based on OnlyOffice availability
- Remove Aspose.Words dependency and all related code
- Add spreadsheet and presentation format support
Frontend:
- Mark office files as previewable in Document.ts
- Add office extensions to MediaPreview.vue preview list
- Fix pre-existing @ts-ignore in HeaderMain.vue
Tests:
- Fix test_lrucache.py parameter name (open -> opener)
Also run ruff format across the codebase to satisfy linter checks.
Implement complete WebDAV file serving compatible with various clients from Windows File Explorer to more specialized sync tools. The old control WebSocket has been updated to part-DAV, part REST API instead. Implemented user:pass BASIC auth. Added UI and backend for creating tokens that avoid the need to use actual username and password for requests from CLI or DAV.
This is a major upgrade with assorted things included.
- Navigation flows improved, search appears in URL history, cleared when navigating to another folder
- More efficient file list format for faster loads
- Efficient updates, never re-send full root another time (except at connection)
- Large number of watching and filelist updates (inotify issues remain)
- File size coloring
- Fixed ZIP generation random glitches (thread race condition)
- Code refactoring, cleanup, typing fixes
- More tests