- 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.
19 KiB
| name | overview | todos | isProject | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Drive Suite Build | Build UltiDrive as a Google Drive–like 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`. |
|
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 Drive–like 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
/driveon 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/:
docker-compose.onlyoffice.yml— Document Server container- Update
deploy/nextcloud/init.sh—$OCC app:enable onlyoffice - Update
deploy/compose-up.sh— conditional include whenONLYOFFICE_ENABLED=true - Update
deploy/nginx/default.conf.template:/drive/→ drive-suite container/office/→ OnlyOffice (internal, JWT-protected; not exposed to users directly)
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:
file-browser.tsx— grid/list, sort, multi-selectbreadcrumb-nav.tsxupload-zone.tsx— drag-drop + chunked upload viaX-Upload-*headersshare-dialog.tsxquota-bar.tsx
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/createor NC template, then opens editor
Phase 3 — OnlyOffice editor integration
3.1 Embed pattern
app/drive/edit/[fileId]/page.tsx:
- Fetch editor config from
POST /api/v1/office/session - Load
@onlyoffice/document-editor-react(or iframe with DocsAPI) - 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, ~1–2 weeks)
- Custom JSON skin: toolbar colors, fonts (Roboto/Google Sans), icon set
- CSS overrides via OnlyOffice
customizationconfig - 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 specsweb/components/chrome/— React chrome components- Build suite-owned titlebar (doc title, share, star, account) above OnlyOffice iframe
- Pass
customization.layoutto 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: addhref: "/drive"to UltiDrive tile- Reciprocal launcher in drive-suite header back to
/mail, Agenda, etc. - Shared suite favicon/branding via
pnpm run brand:authentikpattern
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 (4–5 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 |