Files
dashboard/plans/2026-02-28-nextcloud-talk-integration-design.md
Matthias Hochmeister 681acd8203 add now features
2026-03-01 11:50:27 +01:00

7.1 KiB

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:

{
  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