170 lines
7.1 KiB
Markdown
170 lines
7.1 KiB
Markdown
# Nextcloud Talk Integration — Design Document
|
|
|
|
## Overview
|
|
|
|
Add a read-only Nextcloud Talk widget to the Feuerwehr Dashboard showing the user's 2-3 most recent chats, unread message counts, and clickable links to open conversations in Nextcloud Talk. Users connect their Nextcloud account once via Login Flow v2, which generates an app password stored in the database.
|
|
|
|
## Architecture
|
|
|
|
### Auth Flow (Login Flow v2 — App Passwords)
|
|
|
|
Each user connects their Nextcloud account through the Nextcloud Login Flow v2. This generates an app-specific password that the backend stores and uses for all subsequent Nextcloud API calls via HTTP Basic Auth. No Authentik session cookies are involved.
|
|
|
|
**Data flow:**
|
|
```
|
|
Frontend → Dashboard Backend (JWT auth) → Nextcloud OCS API (Basic Auth with stored app password)
|
|
```
|
|
|
|
1. User clicks "Mit Nextcloud verbinden" in the widget
|
|
2. Frontend calls `POST /api/nextcloud/connect` to initiate Login Flow v2
|
|
3. Backend requests a login token from Nextcloud and returns the `login` URL + `poll` endpoint/token
|
|
4. Frontend opens the Nextcloud login URL in a new tab; user authorizes the app
|
|
5. Frontend polls `POST /api/nextcloud/poll` until Nextcloud returns credentials
|
|
6. Backend stores the app password (encrypted) and Nextcloud username in the user's DB record
|
|
7. Subsequent `GET /api/nextcloud/talk/rooms` calls use the stored credentials with Basic Auth
|
|
8. User can disconnect via `DELETE /api/nextcloud/connect`, which revokes the app password
|
|
|
|
### Components to Build
|
|
|
|
- **Backend:** Four endpoints under `/api/nextcloud`
|
|
- **Frontend:** One widget component (`NextcloudTalkWidget`) with connect/disconnect flow
|
|
- **Database:** Migration adding `nextcloud_username` and `nextcloud_app_password` columns to users table
|
|
- **Config:** One env var: `NEXTCLOUD_URL`
|
|
|
|
---
|
|
|
|
## Backend: Nextcloud Endpoints
|
|
|
|
### `GET /api/nextcloud/talk/rooms`
|
|
|
|
Fetches the user's recent conversations using their stored Nextcloud credentials.
|
|
|
|
1. Reads the user's `nextcloud_username` and `nextcloud_app_password` from the database
|
|
2. Calls Nextcloud OCS API with Basic Auth and `OCS-APIRequest: true` header
|
|
3. Filters out `type: 4` (changelog) conversations
|
|
4. Sorts by `lastActivity` descending, limits to top 3
|
|
5. Calculates `totalUnread` sum across all conversations
|
|
6. Pre-builds deep link URLs
|
|
|
|
**Response shape:**
|
|
```typescript
|
|
{
|
|
success: true,
|
|
data: {
|
|
totalUnread: 12,
|
|
conversations: [
|
|
{
|
|
token: "abc123",
|
|
displayName: "Einsatzgruppe 1",
|
|
unreadMessages: 5,
|
|
lastActivity: 1709142000,
|
|
lastMessage: {
|
|
text: "Einsatz morgen um 8:00",
|
|
author: "Max Mustermann",
|
|
timestamp: 1709142000
|
|
},
|
|
type: 2,
|
|
url: "https://cloud.feuerwehr-rems.at/call/abc123"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### `POST /api/nextcloud/connect`
|
|
|
|
Initiates Login Flow v2 by requesting a login token from Nextcloud. Returns the login URL (for the user to open in a new tab) and the poll endpoint/token (for the frontend to poll).
|
|
|
|
### `POST /api/nextcloud/poll`
|
|
|
|
Polls the Nextcloud Login Flow v2 endpoint. When the user has authorized the app, Nextcloud returns the app password and username. The backend stores these credentials in the user's DB record.
|
|
|
|
### `DELETE /api/nextcloud/connect`
|
|
|
|
Disconnects the Nextcloud account: revokes the app password via the Nextcloud provisioning API and removes the stored credentials from the database.
|
|
|
|
**Error handling:**
|
|
- No stored credentials → `401` with `nextcloud_not_connected` error
|
|
- Invalid/revoked app password → `401`
|
|
- Nextcloud unreachable → `502`
|
|
|
|
---
|
|
|
|
## Frontend: NextcloudTalkWidget
|
|
|
|
### State: Not Connected
|
|
|
|
```
|
|
┌─────────────────────────────────┐
|
|
│ Nextcloud Talk │
|
|
├─────────────────────────────────┤
|
|
│ │
|
|
│ Mit Nextcloud verbinden │ ← Button triggers Login Flow v2
|
|
│ │
|
|
└─────────────────────────────────┘
|
|
```
|
|
|
|
Clicking the button calls `POST /api/nextcloud/connect`, opens the returned login URL in a new tab, and starts polling `POST /api/nextcloud/poll` until credentials are obtained.
|
|
|
|
### State: Connected
|
|
|
|
```
|
|
┌─────────────────────────────────┐
|
|
│ Nextcloud Talk (12) │ ← Header + total unread badge
|
|
├─────────────────────────────────┤
|
|
│ Einsatzgruppe 1 (5) │ ← Chat name + unread count
|
|
│ Max: Einsatz morgen um 8:00 │ ← Last message preview
|
|
│─────────────────────────────────│
|
|
│ Ausbildung (4) │
|
|
│ Anna: Termin verschoben │
|
|
│─────────────────────────────────│
|
|
│ Kommando (3) │
|
|
│ Peter: Protokoll angehängt │
|
|
├─────────────────────────────────┤
|
|
│ Trennen ⋮ │ ← Disconnect option
|
|
└─────────────────────────────────┘
|
|
Each row clickable → opens /call/{token} in new tab
|
|
```
|
|
|
|
**Behavior:**
|
|
- React Query with 30s refetch interval
|
|
- SkeletonCard during loading
|
|
- Error state: "Nextcloud nicht erreichbar"
|
|
- Clickable rows with hover effect, open deep link in new tab
|
|
- MUI Badge/Chip for unread counts, hidden when zero
|
|
- Relative timestamps via date-fns (e.g., "vor 5 Min.")
|
|
- MUI Grid item: `xs={12} md={6} lg={4}`
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
**New env var:**
|
|
```
|
|
NEXTCLOUD_URL=https://cloud.feuerwehr-rems.at
|
|
```
|
|
|
|
Added to `.env.example`, `docker-compose.yml`, and `backend/src/config/environment.ts`.
|
|
|
|
---
|
|
|
|
## Files to Create/Modify
|
|
|
|
### New Files
|
|
- `backend/src/routes/nextcloud.routes.ts` — Route definitions (4 endpoints)
|
|
- `backend/src/controllers/nextcloud.controller.ts` — Request handlers
|
|
- `backend/src/services/nextcloud.service.ts` — Login Flow v2 + OCS API logic
|
|
- `backend/src/database/migrations/013_add_nextcloud_credentials.sql` — Add `nextcloud_username` and `nextcloud_app_password` columns
|
|
- `frontend/src/components/dashboard/NextcloudTalkWidget.tsx` — Widget with connect/disconnect flow
|
|
- `frontend/src/services/nextcloud.ts` — Frontend API service (connect, poll, disconnect, getRooms)
|
|
- `frontend/src/types/nextcloud.types.ts` — TypeScript interfaces
|
|
|
|
### Modified Files
|
|
- `backend/src/app.ts` — Register nextcloud routes
|
|
- `backend/src/config/environment.ts` — Add NEXTCLOUD_URL
|
|
- `backend/src/models/user.model.ts` — Add `nextcloud_username` and `nextcloud_app_password` fields to User interface
|
|
- `backend/src/services/user.service.ts` — Add methods to store/retrieve/clear Nextcloud credentials
|
|
- `frontend/src/pages/Dashboard.tsx` — Add widget to grid
|
|
- `.env.example` — Add NEXTCLOUD_URL
|
|
- `docker-compose.yml` — Pass NEXTCLOUD_URL to backend
|