immersive2/SHARING_PLAN.md
Michael Mainguy 1e174e81d3
Some checks failed
Node.js CI / build (push) Waiting to run
Build / build (push) Failing after 15m8s
Add local database mode for browser-only diagrams
- Add /db/local/:db path type that stores diagrams locally without syncing
- New diagrams now default to local storage (browser-only)
- Share button creates public copy when sharing local diagrams
- Add storage type badges (Local/Public/Private) in diagram manager
- Add GitHub Actions workflow for automated builds
- Block local- database requests at server with 404

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 18:13:43 -06:00

5.5 KiB

Self-Hosted Diagram Sharing with Express-PouchDB

Requirements (Confirmed)

  • Storage: In-memory (ephemeral) - lost on server restart
  • Content: Copy current diagram entities when creating share
  • Expiration: No expiration - links work until server restart
  • Encryption: None - keep it simple, anyone with link can access

Architecture

┌─────────────────────────────────────────────────┐
│           Express Server (port 3001)             │
├─────────────────────────────────────────────────┤
│  /api/share/*     → Share management API         │
│  /pouchdb/*       → express-pouchdb (sync)       │
│  /share/:uuid     → Client share route           │
│  /api/*           → Existing API routes          │
│  /*               → Vite static files            │
└─────────────────────────────────────────────────┘
         │
         └── In-Memory PouchDB (per share UUID)

Implementation Steps

Phase 1: Server-Side Setup

1.1 Add Dependencies

npm install express-pouchdb pouchdb-adapter-memory

1.2 Create PouchDB Server Service

New file: server/services/pouchdbServer.js

  • Initialize PouchDB with memory adapter
  • Track active share databases in a Map
  • Export getShareDB(shareId), shareExists(shareId), createPouchDBMiddleware()

1.3 Create Share API

New file: server/api/share.js

  • POST /api/share/create - Generate UUID, create in-memory DB, copy entities
  • GET /api/share/:id/exists - Check if share exists
  • GET /api/share/stats - Debug endpoint for active shares

1.4 Update API Router

Edit: server/api/index.js

  • Add import shareRouter from "./share.js"
  • Mount at router.use("/share", shareRouter)

1.5 Mount Express-PouchDB

Edit: server.js

  • Import createPouchDBMiddleware from pouchdbServer.js
  • Mount at app.use("/pouchdb", createPouchDBMiddleware())

Phase 2: Client-Side Integration

2.1 Update URL Parsing

Edit: src/util/functions/getPath.ts

  • Add getPathInfo() function returning { dbName, isShare, shareId }
  • Detect /share/:uuid pattern

2.2 Update PouchDB Persistence Manager

Edit: src/integration/database/pouchdbPersistenceManager.ts

In initLocal():

  • Call getPathInfo() to detect share URLs
  • If share: use share-{uuid} as local DB name, call beginShareSync()

Add new method beginShareSync(shareId):

  • Check share exists via /api/share/:id/exists
  • Connect to ${origin}/pouchdb/share-${shareId}
  • Set up presence with share-${shareId} as DB name
  • Begin live sync (no encryption)

2.3 Add React Route

Edit: src/react/webRouter.tsx

  • Add route { path: "/share/:uuid", element: <VrExperience isShare={true} /> }
  • No ProtectedRoute wrapper (public access)

2.4 Add Share Button Handler

Edit: src/react/pages/vrExperience.tsx

  • Add isShare prop
  • Add handleShare() function:
    1. Get all entities from local PouchDB
    2. POST to /api/share/create with entities
    3. Copy resulting URL to clipboard
    4. Show confirmation

Phase 3: Presence Integration

The WebSocket presence system already routes by database name. Since shares use share-{uuid} as the database name, presence works automatically.

Edit: server/server.js (WebSocket server)

  • Update originIsAllowed() to allow localhost for development

Files to Modify

File Action Purpose
package.json Edit Add express-pouchdb, pouchdb-adapter-memory
server.js Edit Mount /pouchdb middleware
server/api/index.js Edit Add share router
server/services/pouchdbServer.js Create PouchDB memory initialization
server/api/share.js Create Share API endpoints
server/server.js Edit Allow localhost origins
src/util/functions/getPath.ts Edit Add getPathInfo()
src/integration/database/pouchdbPersistenceManager.ts Edit Add share sync logic
src/react/webRouter.tsx Edit Add /share/:uuid route
src/react/pages/vrExperience.tsx Edit Add share button handler
src/util/featureConfig.ts Edit Enable shareCollaborate feature

User Flow

Creating a Share

  1. User has a diagram open at /db/public/mydiagram
  2. Clicks "Share" button
  3. Client fetches all entities from local PouchDB
  4. POSTs to /api/share/create with entities
  5. Server creates in-memory DB, copies entities, returns UUID
  6. Client copies https://server.com/share/{uuid} to clipboard
  7. User shares link with collaborators

Joining a Share

  1. User navigates to https://server.com/share/{uuid}
  2. React Router renders VrExperience with isShare=true
  3. PouchdbPersistenceManager detects share URL
  4. Checks /api/share/:uuid/exists - returns true
  5. Creates local PouchDB share-{uuid}
  6. Connects to /pouchdb/share-{uuid} for sync
  7. Entities replicate to local, render in scene
  8. Presence WebSocket connects with share-{uuid} as room

Future Authentication (Not Implemented Now)

Structure allows easy addition later:

  • express-pouchdb middleware can be wrapped with auth middleware
  • Share API can require JWT/session tokens
  • Could add password-protected shares
  • Could add read-only vs read-write permissions