ultisuite-client/.cursor/plans/drive_suite_build_7921c055.plan.md
R3D347HR4Y 8a02c10ba3 Add environment configuration and update email view components
- Created a .cursorignore file to manage local environment files.
- Updated .env.example to reflect changes in the public app URL.
- Modified the gmail workspace configuration to include the drive-suite path.
- Enhanced email view components to support attachment handling and fallback for plain text bodies.
- Improved user experience by updating attachment display logic and integrating inline attachment support.
2026-06-04 00:12:43 +02:00

19 KiB
Raw Permalink Blame History

name overview todos isProject
Drive Suite Build Build UltiDrive as a Google Drivelike frontend in `drive-suite`, with all backend/infra in `ulti-backend` (ultid API, Nextcloud, OnlyOffice, Authentik, deploy). Mail stays frontend-only in `gmail-interface-clone`. Production routing: same domain at `/drive`.
id content status
phase0-provisioning Fix drive EnsurePrincipal + extend drive API (quota, shares CRUD, restore, favorite, create blank file) completed
id content status
phase0-onlyoffice Deploy OnlyOffice Document Server, enable NC onlyoffice app, add /api/v1/office session+callback endpoints completed
id content status
phase1-scaffold Scaffold drive-suite Next.js app (auth, API client, basePath /drive, app shell, URL routing) completed
id content status
phase2-mvp Build Google Drive file browser MVP wired to real /api/v1/drive (upload, browse, share, trash, recent, starred) completed
id content status
phase3-editor Integrate OnlyOffice editor embed for docx/xlsx/pptx via /drive/edit/{id} completed
id content status
phase4-chrome Google-like editor chrome: OnlyOffice theme first, then custom wrapper using drive-dump specs if needed completed
id content status
phase5-suite Wire suite integration: nginx /drive route, launcher hrefs, mail attachment picker, Authentik callbacks completed
false

UltiDrive (Drive Suite) — Build Plan

Repository roles

Repo Responsibility
ulti-backend All backend infrastructure and server-side code: custom Go API (ultid), Docker Compose, edge nginx, Authentik, Postgres/KeyDB/RustFS, headless Nextcloud, mail stack (IMAP/SMTP/sync), OnlyOffice Document Server, Jitsi, observability, deploy scripts, env templates, Authentik blueprints. This is the single ops + API hub for the suite.
gmail-interface-clone Frontend only — Ultimail. Next.js UI for mail (list, compose, settings, contacts). Talks to ulti-backend via /api/v1 proxy; no server infrastructure lives here.
drive-suite Frontend only — UltiDrive. Next.js UI for the Google Drivelike file browser and editor shell. Same pattern as mail: API calls go to ulti-backend; deploy wiring (nginx route, compose service) is defined in ulti-backend.

Suite frontends are thin clients; every persistent service and integration runs from ulti-backend.


Current state

Layer Status
drive-suite/ Empty placeholder — greenfield
ulti-backend Headless NC 30 deployed; /api/v1/drive proxy (list, upload, download, move, copy, rename, trash, recent, starred, shares)
gmail-interface-clone Auth stack, shadcn UI, app shell patterns; UltiDrive tile exists but has no href
ultimail/apps/web/src/components/drive/ Mock UI components (file-browser, share-dialog, upload-zone) — reference only
drive-dump/ Google Docs chrome research (titlebar, toolbar CSS specs) — useful for editor skinning, not for file browser
OnlyOffice Not deployed; mentioned only in project-plan/ultidrive.md

Critical backend bug to fix first: drive handlers pass claims.Sub as NC user ID, but NC OIDC maps preferred_username (email). Contacts already use EnsurePrincipal(email, sub, name) — drive must match or provisioning fails.


Target architecture

flowchart TB
  subgraph frontends [Frontend repos]
    MailFE["gmail-interface-clone"]
    DriveFE["drive-suite"]
  end

  subgraph ultiBackend [ulti-backend deploy and API]
    Nginx["edge nginx"]
    Ultid["ultid Go API"]
    Auth["Authentik"]
    NC["Nextcloud headless"]
    OO["OnlyOffice"]
    MailSvc["mail IMAP SMTP sync"]
    S3["RustFS S3"]
  end

  MailFE -->|"/mail"| Nginx
  DriveFE -->|"/drive"| Nginx
  Nginx --> Ultid
  Nginx --> Auth
  Ultid --> DriveAPI["/api/v1/drive"]
  Ultid --> OfficeAPI["/api/v1/office"]
  Ultid --> MailAPI["/api/v1/mail"]
  Ultid --> WS["/ws"]
  DriveAPI --> NC
  OfficeAPI --> NC
  OfficeAPI --> OO
  NC --> OO
  NC --> S3
  MailAPI --> MailSvc

Principles:

  • Browser never talks to Nextcloud or OnlyOffice directly — all via ultid in ulti-backend (auth, RBAC, stable API).
  • Nextcloud UI stays hidden (/cloud/ admin-only); users only see UltiDrive.
  • Same Authentik OIDC session; production path /drive on the suite domain (shared cookies with /mail).

Phase 0 — Backend hardening (ulti-backend only)

All work in this phase stays in ulti-backend (Go API + deploy/). No infrastructure or API logic belongs in drive-suite or gmail-interface-clone.

Fix and extend the existing drive layer before building UI.

0.1 User provisioning fix

In internal/api/drive/handlers.go and service.go, replace raw claims.Sub with:

userID, err := nc.EnsurePrincipal(ctx, claims.Email, claims.Sub, claims.Name)

Mirror the contacts pattern in internal/nextcloud/users.go.

0.2 API gaps (priority order)

Endpoint Purpose
GET /drive/quota Storage bar in UI
GET /drive/shares?path= List existing shares
PUT /drive/shares/{id} Update role / expiry / password
DELETE /drive/shares/{id} Revoke share
POST /drive/trash/restore Restore from trash
POST /drive/favorite Star/unstar
POST /drive/files/new Create empty doc/sheet/slide (NC template or OO blank)
GET /drive/search Full-text (v1: name filter; v2: Meilisearch)

Extend internal/nextcloud/drive.go with OCS share list/update/delete and favorite WebDAV properties.

0.3 WebSocket events

Add drive.file_changed, drive.share_updated to ultid WS hub so drive UI can invalidate TanStack Query cache (same pattern as mail events in gmail-interface-clone/lib/api/ws.ts).

0.4 OnlyOffice + Nextcloud deployment

New files under deploy/onlyoffice/:

Env vars to add to .env.example:

ONLYOFFICE_ENABLED=true
ONLYOFFICE_URL=http://onlyoffice:80
ONLYOFFICE_JWT_SECRET=...
ONLYOFFICE_PUBLIC_URL=https://{DOMAIN}/office

0.5 Office API (new package internal/api/office/)

Endpoint Role
POST /office/session Given { path, mode: view|edit }, return OnlyOffice editor config + signed JWT
GET /office/callback OnlyOffice save callback → NC WebDAV PUT
POST /office/create Create blank docx/xlsx/pptx in user folder

Flow: drive UI opens /drive/edit/{fileId} → backend builds OnlyOffice config pointing at NC file via internal URL → OnlyOffice loads document → saves via callback.


Phase 1 — Scaffold drive-suite (Google Drive shell)

Frontend-only repo. Copy UI/auth/client patterns from gmail-interface-clone (the mail frontend), not from ultimail (different router). Match stack: Next.js 16, React 19, Tailwind 4, shadcn new-york, Zustand 5, TanStack Query 5, pnpm.

1.1 Repo bootstrap

drive-suite/
├── app/
│   ├── layout.tsx              # QueryProvider + AuthProvider
│   ├── page.tsx                # redirect → /drive
│   ├── api/auth/               # copy OIDC routes from mail
│   ├── auth/complete/
│   ├── login/
│   └── drive/
│       ├── layout.tsx          # DriveAppShell
│       ├── [[...segments]]/page.tsx
│       ├── edit/[fileId]/page.tsx   # OnlyOffice embed (Phase 3)
│       └── settings/[[...section]]/
├── components/drive/           # file browser, sidebar, header, share, upload
├── lib/
│   ├── auth/                   # session, pkce, jwt-claims
│   ├── api/client.ts           # Bearer + 401 refresh
│   ├── api/hooks/use-drive-queries.ts
│   ├── drive-url.ts            # URL = source of truth
│   └── stores/                 # drive-ui-store, drive-settings-store
├── next.config.mjs             # basePath: '/drive' in prod; /api/v1 rewrite
└── Dockerfile                  # same multi-stage pattern as mail

basePath: '/drive' in production so assets and routes work behind nginx path routing.

1.2 Auth

Copy auth stack from mail; change:

  • Default returnTo/drive
  • Persist key → ultidrive-auth
  • Keep ulti_* cookie names (same origin)

1.3 App shell (Google Drive UX)

Three-zone layout mirroring mail-app-shell.tsx:

┌──────────────────────────────────────────────────────────┐
│ Header: search, view toggle, account, suite launcher    │
├──────────┬───────────────────────────────────────────────┤
│ Sidebar  │ Main: breadcrumb + file grid/list             │
│ My Drive │                                               │
│ Recent   │ (optional detail panel for file preview)      │
│ Starred  │                                               │
│ Trash    │                                               │
│ Storage  │                                               │
└──────────┴───────────────────────────────────────────────┘

Port/adapt from ultimail mock components:

Add --drive-* CSS tokens in globals.css parallel to mail's --mail-*.

1.4 URL routing

lib/drive-url.ts + use-drive-route.ts:

URL segment View
/drive My Drive root
/drive/folders/documents/page/2 Folder + pagination
/drive/recent Recent files
/drive/starred Favorites
/drive/trash Trash
/drive/search?q=report Search results
/drive/file/{id} Preview panel
/drive/edit/{id} Editor (Phase 3)

Wire real API from day one — no mock data.


Phase 2 — Core Drive features (MVP)

Ship a usable Google Drive clone before editors.

Feature Backend Frontend
Browse folders GET /drive/files/* Grid/list + breadcrumb
Upload (simple + chunked) POST /drive/files/* Progress, retry
Create folder POST /drive/folders/* New folder dialog
Rename / move / copy existing endpoints Context menu + drag-drop move
Delete / trash / restore DELETE + trash endpoints Trash view
Starred / recent existing endpoints Sidebar views
Share (link + users) shares CRUD Share dialog (owner/editor/viewer)
Download GET /drive/download/* Context menu
Preview download + mime routing Images, PDF inline; others download
Storage quota GET /drive/quota Sidebar bar
Multi-select bulk ops batch endpoints or sequential Toolbar actions

New file affordance (Google-style):

  • Dropdown: Blank document / spreadsheet / presentation / folder / upload
  • Creates file via POST /office/create or NC template, then opens editor

Phase 3 — OnlyOffice editor integration

3.1 Embed pattern

app/drive/edit/[fileId]/page.tsx:

  1. Fetch editor config from POST /api/v1/office/session
  2. Load @onlyoffice/document-editor-react (or iframe with DocsAPI)
  3. Full-viewport editor; minimal chrome initially

3.2 File type routing

MIME / extension Editor
.docx, .odt Document
.xlsx, .ods Spreadsheet
.pptx, .odp Presentation
Other Preview or download only

3.3 Save / co-editing

OnlyOffice handles real-time co-editing natively when connected to NC via the OnlyOffice app. Backend callback ensures saves land in NC WebDAV. No custom CRDT needed for v1.


Phase 4 — Google-like editor chrome (theme → wrapper → fork)

Decision gate after Phase 3 ships with stock OnlyOffice UI.

Step A — OnlyOffice theme (try first, ~12 weeks)

  • Custom JSON skin: toolbar colors, fonts (Roboto/Google Sans), icon set
  • CSS overrides via OnlyOffice customization config
  • Hide OnlyOffice branding; match light/dark to suite theme

Step B — Custom chrome wrapper (if theme insufficient)

Leverage drive-dump research:

  • docs/CHROME-SPEC.md — titlebar, menubar, toolbar DOM/CSS specs
  • web/components/chrome/ — React chrome components
  • Build suite-owned titlebar (doc title, share, star, account) above OnlyOffice iframe
  • Pass customization.layout to hide OnlyOffice's own header/toolbar
  • Separate chrome per app type: Docs, Sheets, Slides (toolbar sets differ)

Step C — Fork (last resort)

Only if theme + wrapper cannot reach Google parity on toolbars/menus. Fork OnlyOffice Document Server frontend (AGPL) — high maintenance cost. Defer until A+B evaluated.

Per drive-dump/docs/EDITOR-CORE-DECISION.md: do not rebuild the editing engine — OnlyOffice remains the core; only chrome is customized.


Phase 5 — Suite integration

5.1 Cross-app navigation

  • header-account-actions.tsx: add href: "/drive" to UltiDrive tile
  • Reciprocal launcher in drive-suite header back to /mail, Agenda, etc.
  • Shared suite favicon/branding via pnpm run brand:authentik pattern

5.2 Mail ↔ Drive

  • Mail UI (gmail-interface-clone): compose attachment picker "Insert from UltiDrive"; "Save attachment to Drive" on received mail
  • API (ulti-backend): mail attachment upload → POST /drive/files/... in ultid

5.3 Deploy wiring (ulti-backend)

All routing and compose changes live in ulti-backend deploy/. The drive-suite repo supplies the Docker image / build context only.

Update deploy/nginx/default.conf.template:

location /drive/ {
    proxy_pass http://drive-suite:3000/;
    # WebSocket + standard headers
}

Add a drive-suite service to ulti-backend compose (port 3000 internal; dev: build from ../drive-suite or host :3001). Mail frontend is deployed the same way today — gmail-interface-clone is not part of ulti-backend source tree, but its container is orchestrated from ulti-backend deploy.

Update Authentik blueprints / OIDC redirect URIs in ulti-backend for /drive/api/auth/callback.


Phase 6 — Advanced (post-MVP)

Feature Approach
Full-text search Meilisearch indexer on NC files + /drive/search
Version history NC versions API wrapper
Shared drives / team folders NC groupfolders app + API
Public link sharing page /drive/shared/{token} read-only view
Desktop mount rclone sidecar in Tauri (per ultidrive.md)
Offline uploads IDB queue pattern from mail offline-queue
Comments on files OnlyOffice comments (built-in) or NC comments API

Suggested delivery order

gantt
    title UltiDrive delivery phases
    dateFormat YYYY-MM-DD
    section ulti backend
    API and provisioning     :a1, 2026-06-02, 14d
    OnlyOffice deploy        :a2, after a1, 7d
    section drive suite
    Scaffold                 :a3, 2026-06-02, 7d
    Drive MVP                :a4, after a3, 21d
    Editor embed             :a5, after a2, 14d
    Google chrome            :a6, after a5, 21d
    section integration
    Suite wiring             :a7, after a4, 7d

Parallel tracks: a1→a2 (ulti-backend) runs alongside a3→a4 (drive-suite); a5 starts after OnlyOffice (a2); a7 (suite wiring) after Drive MVP (a4).

First shippable milestone (45 weeks): Phase 0 + 1 + 2 + 5 nginx/auth — full Drive file browser on real backend.

Second milestone (+3 weeks): Phase 3 — open and edit docs/sheets/slides in OnlyOffice.

Third milestone (+3 weeks): Phase 4 — Google-like editor chrome.


Key files to create/modify

Repo Role Files
ulti-backend Backend + all infra internal/api/drive/*, internal/api/office/*, deploy/onlyoffice/*, deploy/nginx/*, compose, Authentik blueprints
drive-suite Drive frontend only Entire Next.js app; copy auth + API client patterns from gmail-interface-clone
gmail-interface-clone Mail frontend only header-account-actions.tsx, compose drive picker (UI links to /drive)
Reference only ultimail/.../drive/*, drive-dump/docs/CHROME-SPEC.md

Risks and mitigations

Risk Mitigation
NC user ID mismatch Fix EnsurePrincipal in Phase 0 before any UI work
OnlyOffice JWT / NC connector misconfig Test with single doc open before building chrome
Chunk upload edge cases Reuse existing X-Upload-* protocol; add integration tests
Google UI parity scope creep Theme first; wrapper second; fork only as last resort
Path-based routing (/drive) asset breaks basePath in next.config from day one