Fix Android WebView API access and mobile shell layout
This commit is contained in:
115
docs/superpowers/plans/2026-04-02-shared-file-blob-storage.md
Normal file
115
docs/superpowers/plans/2026-04-02-shared-file-blob-storage.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Shared File Blob Storage Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 将网盘文件模型改造成 `StoredFile -> FileBlob` 引用关系,让分享导入与网盘复制复用同一份底层对象,而不是再次写入物理文件。
|
||||
|
||||
**Architecture:** 新增独立 `FileBlob` 实体承载真实对象 key、大小和内容类型,`StoredFile` 只保留逻辑目录元数据并引用 `FileBlob`。网盘上传为每个新文件创建新 blob,分享导入和文件复制直接复用 blob;删除时按引用是否归零决定是否删除底层对象。补一个面向旧 `portal_file.storage_name` 数据的一次性回填路径,避免线上旧数据在新模型下失联。
|
||||
|
||||
**Tech Stack:** Spring Boot 3.3.8, Spring Data JPA, Java 17, Maven, H2 tests, local filesystem storage, S3-compatible object storage
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Define Blob Data Model
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/src/main/java/com/yoyuzh/files/FileBlob.java`
|
||||
- Create: `backend/src/main/java/com/yoyuzh/files/FileBlobRepository.java`
|
||||
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFile.java`
|
||||
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java`
|
||||
|
||||
- [ ] **Step 1: Write the failing tests**
|
||||
|
||||
Add/update backend tests that expect file copies and share imports to preserve a shared blob id rather than a per-user storage name.
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileShareControllerIntegrationTest`
|
||||
Expected: FAIL because `StoredFile` does not yet expose blob references and current logic still duplicates file content.
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
Add `FileBlob` entity/repository, move file-object ownership from `StoredFile.storageName` to `StoredFile.blob`, and add repository helpers for blob reference counting / physical size queries.
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileShareControllerIntegrationTest`
|
||||
Expected: PASS for data-model expectations.
|
||||
|
||||
### Task 2: Refactor File Storage Flow To Blob Keys
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/src/main/java/com/yoyuzh/files/FileService.java`
|
||||
- Modify: `backend/src/main/java/com/yoyuzh/files/storage/FileContentStorage.java`
|
||||
- Modify: `backend/src/main/java/com/yoyuzh/files/storage/LocalFileContentStorage.java`
|
||||
- Modify: `backend/src/main/java/com/yoyuzh/files/storage/S3FileContentStorage.java`
|
||||
- Test: `backend/src/test/java/com/yoyuzh/files/FileServiceTest.java`
|
||||
- Test: `backend/src/test/java/com/yoyuzh/files/FileServiceEdgeCaseTest.java`
|
||||
|
||||
- [ ] **Step 1: Write the failing tests**
|
||||
|
||||
Add/update tests for:
|
||||
- upload creates a new blob
|
||||
- share import reuses the source blob and does not store bytes again
|
||||
- file copy reuses the source blob and does not copy bytes again
|
||||
- deleting a non-final reference keeps the blob/object alive
|
||||
- deleting the final reference removes the blob/object
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileServiceEdgeCaseTest`
|
||||
Expected: FAIL because current service still calls `readFile/storeImportedFile/copyFile/moveFile/renameFile` for ordinary files.
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
Refactor file upload/download/import/copy/delete to operate on blob object keys. Keep directory behavior metadata-only. Ensure file rename/move/copy no longer trigger physical object mutations, and deletion only removes the object when the last `StoredFile` reference disappears.
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileServiceTest,FileServiceEdgeCaseTest`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 3: Backfill Old File Rows Into Blob References
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/src/main/java/com/yoyuzh/files/FileBlobBackfillService.java`
|
||||
- Modify: `backend/src/main/java/com/yoyuzh/files/StoredFileRepository.java`
|
||||
- Test: `backend/src/test/java/com/yoyuzh/files/FileShareControllerIntegrationTest.java`
|
||||
|
||||
- [ ] **Step 1: Write the failing test**
|
||||
|
||||
Add an integration-path expectation that persisted file rows still download/import correctly after the blob model change.
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileShareControllerIntegrationTest`
|
||||
Expected: FAIL because legacy `portal_file` rows created without blobs can no longer resolve file content.
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
Add a startup/on-demand backfill that creates `FileBlob` rows for existing non-directory files using their legacy object key and attaches them to `StoredFile` rows with missing blob references.
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test -Dtest=FileShareControllerIntegrationTest`
|
||||
Expected: PASS.
|
||||
|
||||
### Task 4: Update Docs And Full Verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `memory.md`
|
||||
- Modify: `docs/architecture.md`
|
||||
- Modify: `docs/api-reference.md` (only if API semantics changed)
|
||||
|
||||
- [ ] **Step 1: Document the new storage model**
|
||||
|
||||
Record that logical file metadata now points to shared blobs, that share import and copy reuse blobs, and that physical keys are global rather than user-path keys.
|
||||
|
||||
- [ ] **Step 2: Run full backend verification**
|
||||
|
||||
Run: `cd /Users/mac/Documents/my_site/backend && mvn test`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 3: Summarize migration requirement**
|
||||
|
||||
In the final handoff, call out that old production data must be backfilled to `FileBlob` rows before or during rollout; otherwise pre-existing files cannot resolve blob references under the new model.
|
||||
@@ -0,0 +1,115 @@
|
||||
# Transfer Simple Peer Integration Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace the hand-rolled online transfer WebRTC peer wiring with `simple-peer` while preserving the current pickup-code flow, backend signaling APIs, and offline transfer mode.
|
||||
|
||||
**Architecture:** Keep the existing product boundaries: Spring Boot remains a dumb signaling relay and session store, while the React frontend owns online-transfer peer creation and file streaming. Instead of manually managing `RTCPeerConnection`, ICE candidates, and SDP state across sender/receiver pages, introduce a thin `simple-peer` adapter that serializes peer signals through the existing `/api/transfer/sessions/{id}/signals` endpoints and reuses the current transfer control/data protocol.
|
||||
|
||||
**Tech Stack:** Vite 6, React 19, TypeScript, node:test, `simple-peer`, existing Spring Boot transfer signaling API.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Lock the new peer adapter contract with failing tests
|
||||
|
||||
**Files:**
|
||||
- Create: `front/src/lib/transfer-peer.test.ts`
|
||||
- Create: `front/src/lib/transfer-peer.ts`
|
||||
|
||||
- [ ] **Step 1: Write the failing test**
|
||||
|
||||
Add tests that assert:
|
||||
- local `simple-peer` signal events serialize into a backend-friendly payload
|
||||
- incoming backend signal payloads are routed back into the peer instance
|
||||
- peer connection lifecycle maps to app-friendly callbacks without exposing raw browser SDP/ICE handling to pages
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd front && npm run test -- src/lib/transfer-peer.test.ts`
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
Create a focused adapter around `simple-peer` with:
|
||||
- sender/receiver construction
|
||||
- `signal` event forwarding
|
||||
- `connect`, `data`, `close`, and `error` callbacks
|
||||
- send helpers used by the existing transfer pages
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd front && npm run test -- src/lib/transfer-peer.test.ts`
|
||||
|
||||
### Task 2: Replace online sender wiring in the desktop and mobile transfer pages
|
||||
|
||||
**Files:**
|
||||
- Modify: `front/src/pages/Transfer.tsx`
|
||||
- Modify: `front/src/mobile-pages/MobileTransfer.tsx`
|
||||
- Modify: `front/src/lib/transfer.ts`
|
||||
|
||||
- [ ] **Step 1: Write the failing test**
|
||||
|
||||
Add or extend focused tests for the new signaling payload shape if needed so the sender path no longer depends on manual offer/answer/ICE branches.
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd front && npm run test`
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
Use the adapter in both sender pages:
|
||||
- build an initiator peer instead of a raw `RTCPeerConnection`
|
||||
- post serialized peer signals through the existing backend endpoint
|
||||
- keep the current file manifest and binary chunk sending protocol
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd front && npm run test`
|
||||
|
||||
### Task 3: Replace online receiver wiring while keeping the current receive UX
|
||||
|
||||
**Files:**
|
||||
- Modify: `front/src/pages/TransferReceive.tsx`
|
||||
|
||||
- [ ] **Step 1: Write the failing test**
|
||||
|
||||
Add or update tests around receiver signal handling and data delivery if gaps remain after Task 2.
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `cd front && npm run test`
|
||||
|
||||
- [ ] **Step 3: Write minimal implementation**
|
||||
|
||||
Use the adapter in the receiver page:
|
||||
- build a non-initiator peer
|
||||
- feed backend-delivered signals into it
|
||||
- keep the current control messages, archive flow, and netdisk save flow unchanged
|
||||
|
||||
- [ ] **Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd front && npm run test`
|
||||
|
||||
### Task 4: Verification and release
|
||||
|
||||
**Files:**
|
||||
- Modify if required by validation failures: `front/package.json`, `front/package-lock.json`
|
||||
|
||||
- [ ] **Step 1: Install the dependency**
|
||||
|
||||
Run: `cd front && npm install simple-peer @types/simple-peer`
|
||||
|
||||
- [ ] **Step 2: Run frontend tests**
|
||||
|
||||
Run: `cd front && npm run test`
|
||||
|
||||
- [ ] **Step 3: Run frontend typecheck**
|
||||
|
||||
Run: `cd front && npm run lint`
|
||||
|
||||
- [ ] **Step 4: Run frontend build**
|
||||
|
||||
Run: `cd front && npm run build`
|
||||
|
||||
- [ ] **Step 5: Publish the frontend**
|
||||
|
||||
Run: `node scripts/deploy-front-oss.mjs`
|
||||
Reference in New Issue
Block a user