chore: Fix app

This commit is contained in:
2026-07-14 00:41:05 +02:00
parent 3e090933fd
commit edc4d935d8
98 changed files with 2878 additions and 155 deletions
+118
View File
@@ -0,0 +1,118 @@
# Attachments
> **Area:** attachments
> **Status:** Active
> **Last updated:** 2026-07-05
## Overview
Attachments move file bytes peer-to-peer over the WebRTC ordered data channel using a announce → request → chunk protocol. Chat and DMs attach metadata to messages; the signaling server does not store or relay file payloads. Sibling devices learn attachment **metadata** via `account_sync` `chat-sync-batch` but must still download bytes from a peer that has them.
Domain internals: [`toju-app/src/app/domains/attachment/README.md`](../../toju-app/src/app/domains/attachment/README.md).
## Responsibilities
- Chunked P2P transfer with flow control and cancel semantics.
- Auto-download when policy allows; disk streaming on Electron/Capacitor.
- Ownership vs "shared from your device" UI rules.
- Persist attachment rows + filesystem paths on desktop/mobile.
This area does **not** own:
- Message envelopes or delivery states → [messaging.md](messaging.md).
- WebRTC negotiation → [voice-webrtc.md](voice-webrtc.md).
## Key concepts
- **Announce** — sender advertises `fileId`, name, size, mime without sending bytes.
- **Mirror host** — peer that holds a complete copy and can serve chunks.
- **Buffered send** — waits for data-channel back-pressure (4 MB high / 1 MB low water marks on chat channel).
---
## P2P protocol
| type | Purpose |
|------|---------|
| `file-announce` | Metadata only |
| `file-request` | Receiver starts download |
| `file-chunk` | Base64 chunk (`index`, `total`, `data`) |
| `file-chunk-ack` | Per-chunk flow control |
| `file-cancel` | Abort in flight |
| `file-not-found` | Host lacks bytes |
**Chunk size:** `FILE_CHUNK_SIZE_BYTES` = **64 KB** (`attachment-transfer.constants.ts`).
**Electron send path:** reads one chunk at a time from disk via IPC (`append-file-bytes` / read chunk) to avoid loading whole files into renderer memory.
---
## Persistence
| Runtime | Metadata | Bytes |
|---------|----------|-------|
| Browser | In-memory / optional save | Below **10 MB** auto-save cap (`MAX_AUTO_SAVE_SIZE_BYTES`) |
| Electron | SQLite `attachments` + CQRS | `user/<username>/…` via `AttachmentStorageService` / IPC |
| Capacitor | SQLite | App-private attachment directory — [mobile-capacitor.md](mobile-capacitor.md) |
---
## Download / export to user location
`AttachmentDownloadService.downloadToUserLocation` picks the runtime-appropriate export path:
| Runtime | Behavior |
|---------|----------|
| Electron | `saveExistingFileAs` (disk-backed) or `saveFileAs` (blob) native save dialog |
| Browser | Anchor `download` click on the object URL |
| Capacitor | `CapacitorAttachmentExportService.exportToDevice`: copies the disk file from `Directory.Data` into `Directory.Documents` (or fetches the object URL and writes base64) using `buildAttachmentExportFileName` (timestamp suffix so exports never collide — Android 11+ rejects overwrites of files the app did not create). Anchor downloads do nothing in the Android WebView. |
## Multi-device
`chat-sync-batch` in `account_sync` carries an `attachments` map (local paths stripped). Sibling devices discover files exist; P2P `file-request` still required for bytes.
---
## Business rules and invariants
- Transfers are between connected peers only (no server CDN).
- Receive strategy is decided once at request time by `canReceiveAttachment` (`attachment.logic.ts`): ≤ 10 MB assembles in memory everywhere; > 10 MB streams to disk on Electron/Capacitor, assembles in memory on the browser up to its 50 MB persist cap, and is rejected with a visible `fileTooLarge` error beyond that. `handleFileChunk` must accept whatever the request gate admitted — a stricter chunk-time size cap silently drops chunks and stalls the transfer.
- Visibility-based blob lifecycle on desktop: revoke `blob:` URLs when messages scroll off-screen if disk can rehydrate.
- "Shared from your device" badge only when bytes are local to the viewing user.
---
## Technical implementation
- Facade: `AttachmentFacade``AttachmentManagerService`
- Protocol: `AttachmentTransferService` + `AttachmentTransferTransportService`
- Electron IPC: `read-file-chunk`, `append-file-bytes`, `write-file`, `delete-file`, etc.
---
## Testing
- Domain logic specs under `attachment/`
- E2E: `e2e/tests/chat/chat-message-features.spec.ts`, `local-attachment-persistence.spec.ts`, `multi-device-attachment-sharing.spec.ts`, `large-generic-file-transfer.spec.ts` (browser receiver, generic file above the 10 MB auto-save cap)
---
## Security considerations
- No server-side virus scanning; peers trust senders they are connected to.
- Files stay in user data directories (Electron path jail).
---
## Related features
- [messaging.md](messaging.md) — message + attachment metadata coupling
- [authentication.md](authentication.md) — `account_sync` batches
- [mobile-capacitor.md](mobile-capacitor.md) — mobile storage
## Changelog
| Date | Change |
|------|--------|
| 2026-07-13 | Capacitor download/export to public `Documents` via `CapacitorAttachmentExportService` |
| 2026-07-05 | Expanded to full contract style |