mirror of
https://github.com/Gimanh/taskview-community.git
synced 2026-09-11 13:29:17 +00:00
wip: notifications
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: Notifications
|
||||
description: Real-time and push notifications in TaskView - deadline alerts, assignment notifications, per-user preferences, and multi-channel delivery via WebSocket and Firebase Cloud Messaging.
|
||||
navigation:
|
||||
icon: i-lucide-bell
|
||||
---
|
||||
|
||||
TaskView notifies you when things happen in your projects. Notifications are delivered through multiple channels and can be customized per user.
|
||||
|
||||
## Notification types
|
||||
|
||||
| Type | When it fires | Delivery | Status |
|
||||
|---|---|---|---|
|
||||
| **Deadline** | When a task deadline is reached | Scheduled via background job (pgboss). If the deadline is already past when set, fires immediately. | Available |
|
||||
| **Assignment** | When you are assigned to a task | Immediate | Available |
|
||||
| **Mention** | When someone mentions you | Immediate | Planned |
|
||||
| **Comment** | When someone comments on your task | Immediate | Planned |
|
||||
| **Status change** | When a task status changes | Immediate | Planned |
|
||||
|
||||
## Delivery channels
|
||||
|
||||
Notifications can be sent through two channels (with email planned for the future):
|
||||
|
||||
| Channel | Description | Required configuration |
|
||||
|---|---|---|
|
||||
| **Push** | Native push notifications on iOS/Android via Firebase Cloud Messaging | `FIREBASE_CREDENTIALS_PATH` |
|
||||
| **In-app (WebSocket)** | Real-time delivery to the browser via Centrifugo | `CENTRIFUGO_API_URL`, `CENTRIFUGO_API_KEY`, `CENTRIFUGO_TOKEN_SECRET`, `CENTRIFUGO_PUBLIC_PORT` |
|
||||
|
||||
Both channels are optional. If Firebase is not configured, push notifications are silently skipped. If Centrifugo is not configured, in-app real-time delivery is skipped. Notifications are always saved to the database regardless of channel availability.
|
||||
|
||||
## User preferences
|
||||
|
||||
Each user can control which notifications they receive and through which channels. Settings are available in **Account Settings > Notification Settings**.
|
||||
|
||||
Preferences follow an **opt-out model**: everything is enabled by default. Users explicitly disable what they do not want.
|
||||
|
||||
### Global and per-project settings
|
||||
|
||||
Preferences support two levels:
|
||||
|
||||
- **Global** applies to all projects
|
||||
- **Project overrides** apply to a specific project and are merged on top of global settings
|
||||
|
||||
For example, a user can enable push for all deadline notifications globally, but disable push for deadlines in a specific project.
|
||||
|
||||
### Deadline intervals (planned)
|
||||
|
||||
::callout{icon="i-lucide-construction" color="warning"}
|
||||
Deadline intervals are defined in the preferences structure but not yet active. Currently, deadline notifications fire once at the moment of the deadline. Multiple interval support is planned for a future release.
|
||||
::
|
||||
|
||||
The preferences structure supports the following intervals (minutes before the deadline):
|
||||
|
||||
| Interval | Description |
|
||||
|---|---|
|
||||
| `0` | At the moment of the deadline |
|
||||
| `15` | 15 minutes before |
|
||||
| `30` | 30 minutes before |
|
||||
| `60` | 1 hour before |
|
||||
| `1440` | 1 day before |
|
||||
|
||||
Each interval can be independently enabled or disabled.
|
||||
|
||||
## How it works
|
||||
|
||||
1. An event occurs (task created, deadline changed, assignees changed, etc.)
|
||||
2. **NotificationDispatcher** listens to the event bus and determines the notification type, recipients, and whether to send immediately or schedule a background job
|
||||
3. For deadlines, **DeadlineScheduler** creates a pgboss job that fires at the right time
|
||||
4. When it is time to deliver, **NotificationService** checks the user's preferences, saves the notification to the database, and sends it through enabled channels only
|
||||
5. **Providers** (FCMProvider, CentrifugoProvider) handle the actual delivery
|
||||
|
||||
## Viewing notifications
|
||||
|
||||
Click the bell icon in the sidebar to open the notification panel. From there you can:
|
||||
|
||||
- See all your notifications with type icons and timestamps
|
||||
- Click a notification to navigate to the related task
|
||||
- Mark individual notifications as read
|
||||
- Mark all notifications as read
|
||||
- Load older notifications via pagination
|
||||
|
||||
Notifications older than 1 day are automatically cleaned up by a daily background job.
|
||||
|
||||
## Configuration
|
||||
|
||||
See [Environment Variables](/docs/configuration/environment-variables#notifications) for the full list of notification-related variables.
|
||||
|
||||
## API endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|---|---|---|
|
||||
| `GET` | `/module/notifications` | Fetch notifications (cursor pagination) |
|
||||
| `PATCH` | `/module/notifications/read` | Mark a notification as read |
|
||||
| `PATCH` | `/module/notifications/read-all` | Mark all notifications as read |
|
||||
| `GET` | `/module/notifications/preferences` | Get user notification preferences |
|
||||
| `PUT` | `/module/notifications/preferences` | Save user notification preferences |
|
||||
| `GET` | `/module/notifications/connection-token` | Get WebSocket connection token |
|
||||
| `POST` | `/module/notifications/device/register` | Register a device for push notifications |
|
||||
| `POST` | `/module/notifications/device/unregister` | Unregister a device |
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
title: Webhooks
|
||||
description: Configure webhooks in TaskView to receive real-time HTTP notifications when tasks are created, updated, deleted, or reassigned. Includes HMAC-SHA256 signature verification, automatic retries, and delivery history.
|
||||
navigation:
|
||||
icon: i-lucide-webhook
|
||||
---
|
||||
|
||||
Webhooks let you receive HTTP POST requests when events happen in your projects. Use them to integrate TaskView with external systems - CI/CD pipelines, Slack bots, custom dashboards, or any service that can accept HTTP requests.
|
||||
|
||||
## Supported events
|
||||
|
||||
| Event | When it fires |
|
||||
|---|---|
|
||||
| `task.created` | A new task is created in the project |
|
||||
| `task.updated` | A task is updated (description, status, priority, deadline, etc.) |
|
||||
| `task.deleted` | A task is deleted |
|
||||
| `task.assigneesChanged` | Task assignees are added or removed |
|
||||
|
||||
## Setup
|
||||
|
||||
1. Open a project in TaskView
|
||||
2. Right-click the project in the sidebar → **"Webhooks"**
|
||||
3. Click **"Add Webhook"**
|
||||
4. Enter the URL where you want to receive events
|
||||
5. Select which events to subscribe to
|
||||
6. Click **"Add"**
|
||||
7. Copy the secret and store it securely - it will not be shown again
|
||||
|
||||
## Payload format
|
||||
|
||||
Every webhook delivery is an HTTP POST with `Content-Type: application/json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "task.updated",
|
||||
"timestamp": "2026-03-22T12:00:00.000Z",
|
||||
"task": {
|
||||
"id": 123,
|
||||
"goalId": 774,
|
||||
"description": "Fix login bug",
|
||||
"complete": false,
|
||||
"statusId": 5,
|
||||
"priorityId": 2,
|
||||
"tags": [1, 3],
|
||||
"assignedUsers": [10, 22],
|
||||
"subtasks": []
|
||||
},
|
||||
"changes": {
|
||||
"statusId": 5
|
||||
},
|
||||
"initiatorId": 1
|
||||
}
|
||||
```
|
||||
|
||||
The `changes` field is only present on `task.updated` events and contains only the fields that changed.
|
||||
|
||||
## Signature verification
|
||||
|
||||
Every request includes an `X-Webhook-Signature` header with an HMAC-SHA256 signature of the request body:
|
||||
|
||||
```
|
||||
X-Webhook-Signature: sha256=5d41402abc4b2a76b9719d911017c592...
|
||||
```
|
||||
|
||||
Always verify the signature before processing the payload. Example in Node.js:
|
||||
|
||||
```javascript
|
||||
const crypto = require('crypto')
|
||||
|
||||
function verifySignature(body, signature, secret) {
|
||||
const expected = 'sha256=' + crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(body)
|
||||
.digest('hex')
|
||||
return signature === expected
|
||||
}
|
||||
|
||||
// In your HTTP handler:
|
||||
const body = req.body // raw string, not parsed JSON
|
||||
const signature = req.headers['x-webhook-signature']
|
||||
const isValid = verifySignature(body, signature, YOUR_SECRET)
|
||||
```
|
||||
|
||||
::callout{icon="i-lucide-shield-alert" color="warning"}
|
||||
Never process webhook payloads without verifying the signature. Without verification, anyone who knows your webhook URL can send fake events.
|
||||
::
|
||||
|
||||
## Retries
|
||||
|
||||
If your server responds with a non-2xx status code or doesn't respond within 10 seconds, TaskView retries the delivery:
|
||||
|
||||
| Attempt | Delay |
|
||||
|---|---|
|
||||
| 1st retry | ~10 seconds |
|
||||
| 2nd retry | ~20 seconds |
|
||||
|
||||
After 3 total attempts (1 original + 2 retries), the delivery is marked as **failed**.
|
||||
|
||||
## Auto-deactivation
|
||||
|
||||
If a webhook accumulates **10 consecutive failed deliveries** (after all retries are exhausted), it is automatically deactivated. A single successful delivery resets the failure counter.
|
||||
|
||||
To reactivate a webhook, toggle it back on from the webhooks page. The failure counter is not reset automatically - the next successful delivery will reset it.
|
||||
|
||||
## Delivery history
|
||||
|
||||
The webhooks page shows delivery history for each webhook:
|
||||
- **Event** - which event was delivered
|
||||
- **Status** - success, failed, or pending
|
||||
- **HTTP code** - response status code from your server
|
||||
- **Attempts** - how many attempts were made
|
||||
- **Payload** - click to view the full JSON payload
|
||||
|
||||
Failed deliveries can be retried manually from the delivery history.
|
||||
|
||||
## Managing webhooks
|
||||
|
||||
From the webhooks page you can:
|
||||
- **Toggle** webhooks on/off
|
||||
- **Edit** the URL and subscribed events
|
||||
- **Test** - sends a test payload to verify connectivity
|
||||
- **Rotate secret** - generates a new secret (the old one stops working immediately)
|
||||
- **Delete** - removes the webhook and all delivery history
|
||||
- **View deliveries** - see delivery history with status filter
|
||||
|
||||
## Secret rotation
|
||||
|
||||
If your secret is compromised, rotate it:
|
||||
|
||||
1. Click the key icon on the webhook
|
||||
2. Confirm that you want to rotate
|
||||
3. Copy the new secret
|
||||
4. Update the secret in your receiving application
|
||||
|
||||
The old secret stops working immediately. Any in-flight deliveries signed with the old secret will fail signature verification on your end.
|
||||
|
||||
## Testing locally
|
||||
|
||||
You can use a simple Node.js script to test webhook deliveries:
|
||||
|
||||
```javascript
|
||||
const http = require('http')
|
||||
const crypto = require('crypto')
|
||||
|
||||
const PORT = 4545
|
||||
const SECRET = 'your-secret-here'
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const chunks = []
|
||||
req.on('data', (chunk) => chunks.push(chunk))
|
||||
req.on('end', () => {
|
||||
const body = Buffer.concat(chunks).toString()
|
||||
const signature = req.headers['x-webhook-signature'] || ''
|
||||
const expected = 'sha256=' + crypto
|
||||
.createHmac('sha256', SECRET)
|
||||
.update(body)
|
||||
.digest('hex')
|
||||
|
||||
console.log(signature === expected ? 'Valid' : 'INVALID')
|
||||
console.log(JSON.stringify(JSON.parse(body), null, 2))
|
||||
|
||||
res.writeHead(200)
|
||||
res.end('OK')
|
||||
})
|
||||
})
|
||||
|
||||
server.listen(PORT, () => console.log(`Listening on :${PORT}`))
|
||||
```
|
||||
|
||||
Run with `node webhook-receiver.js` and set the webhook URL to `http://localhost:4545`.
|
||||
Reference in New Issue
Block a user