mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-27 06:05:54 +08:00
feat: add desktop notifications and skills updates
This commit is contained in:
127
server/modules/notifications/notifications.routes.ts
Normal file
127
server/modules/notifications/notifications.routes.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import express from 'express';
|
||||
|
||||
import { notificationChannelEndpointsDb, notificationPreferencesDb } from '@/modules/database/index.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function readText(value: unknown): string {
|
||||
return typeof value === 'string' ? value.trim() : '';
|
||||
}
|
||||
|
||||
function sanitizeEndpoint(endpoint: any) {
|
||||
return {
|
||||
id: endpoint.id,
|
||||
channel: endpoint.channel,
|
||||
endpointId: endpoint.endpoint_id,
|
||||
label: endpoint.label,
|
||||
metadata: notificationChannelEndpointsDb.parseMetadata(endpoint.metadata_json),
|
||||
enabled: Boolean(endpoint.enabled),
|
||||
lastSeenAt: endpoint.last_seen_at,
|
||||
createdAt: endpoint.created_at,
|
||||
updatedAt: endpoint.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
function readUserId(req: express.Request): number {
|
||||
const userId = Number((req as any).user?.id);
|
||||
if (!Number.isInteger(userId) || userId <= 0) {
|
||||
throw new Error('Authenticated user is missing');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
function updateChannelPreference(userId: number, channel: string): unknown {
|
||||
const currentPrefs = notificationPreferencesDb.getPreferences(userId);
|
||||
const hasEnabledEndpoint = notificationChannelEndpointsDb.getEnabledEndpoints(userId, channel).length > 0;
|
||||
return notificationPreferencesDb.updatePreferences(userId, {
|
||||
...currentPrefs,
|
||||
channels: { ...currentPrefs.channels, [channel]: hasEnabledEndpoint },
|
||||
});
|
||||
}
|
||||
|
||||
router.get('/endpoints', (req, res) => {
|
||||
try {
|
||||
const channel = readText(req.query.channel);
|
||||
if (!channel) {
|
||||
return res.status(400).json({ error: 'channel is required' });
|
||||
}
|
||||
|
||||
const userId = readUserId(req);
|
||||
const endpoints = notificationChannelEndpointsDb
|
||||
.getEndpoints(userId, channel)
|
||||
.map(sanitizeEndpoint);
|
||||
return res.json({ success: true, endpoints });
|
||||
} catch (error) {
|
||||
console.error('Error fetching notification endpoints:', error);
|
||||
return res.status(500).json({ error: 'Failed to fetch notification endpoints' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/endpoints/current', (req, res) => {
|
||||
try {
|
||||
const { channel, endpointId, label, metadata = {}, enabled = true } = req.body || {};
|
||||
const normalizedChannel = readText(channel);
|
||||
const normalizedEndpointId = readText(endpointId);
|
||||
if (!normalizedChannel || !normalizedEndpointId) {
|
||||
return res.status(400).json({ error: 'channel and endpointId are required' });
|
||||
}
|
||||
|
||||
const userId = readUserId(req);
|
||||
const endpoint = notificationChannelEndpointsDb.upsertEndpoint({
|
||||
userId,
|
||||
channel: normalizedChannel,
|
||||
endpointId: normalizedEndpointId,
|
||||
label,
|
||||
metadata: metadata && typeof metadata === 'object' ? metadata : {},
|
||||
enabled: enabled !== false,
|
||||
});
|
||||
|
||||
const preferences = updateChannelPreference(userId, normalizedChannel);
|
||||
return res.json({ success: true, endpoint: sanitizeEndpoint(endpoint), preferences });
|
||||
} catch (error) {
|
||||
console.error('Error registering notification endpoint:', error);
|
||||
return res.status(500).json({ error: 'Failed to register notification endpoint' });
|
||||
}
|
||||
});
|
||||
|
||||
router.patch('/endpoints/:channel/:endpointId', (req, res) => {
|
||||
try {
|
||||
const { channel, endpointId } = req.params;
|
||||
const { enabled } = req.body || {};
|
||||
if (typeof enabled !== 'boolean') {
|
||||
return res.status(400).json({ error: 'enabled must be a boolean' });
|
||||
}
|
||||
|
||||
const userId = readUserId(req);
|
||||
const updated = notificationChannelEndpointsDb.setEndpointEnabled(userId, channel, endpointId, enabled);
|
||||
if (!updated) {
|
||||
return res.status(404).json({ error: 'Notification endpoint not found' });
|
||||
}
|
||||
|
||||
const endpoint = notificationChannelEndpointsDb.getEndpoint(userId, channel, endpointId);
|
||||
const preferences = updateChannelPreference(userId, channel);
|
||||
return res.json({ success: true, endpoint: endpoint ? sanitizeEndpoint(endpoint) : null, preferences });
|
||||
} catch (error) {
|
||||
console.error('Error updating notification endpoint:', error);
|
||||
return res.status(500).json({ error: 'Failed to update notification endpoint' });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/endpoints/:channel/:endpointId', (req, res) => {
|
||||
try {
|
||||
const { channel, endpointId } = req.params;
|
||||
const userId = readUserId(req);
|
||||
const removed = notificationChannelEndpointsDb.removeEndpoint(userId, channel, endpointId);
|
||||
if (!removed) {
|
||||
return res.status(404).json({ error: 'Notification endpoint not found' });
|
||||
}
|
||||
|
||||
const preferences = updateChannelPreference(userId, channel);
|
||||
return res.json({ success: true, preferences });
|
||||
} catch (error) {
|
||||
console.error('Error removing notification endpoint:', error);
|
||||
return res.status(500).json({ error: 'Failed to remove notification endpoint' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user