Files
Jellystat/backend/routes/webhooks.js
Félix MARQUET 280fa89c59 feat(webhooks): add support for playback and media notification events
- Implement event handlers for 'playback_started', 'playback_ended' and 'media_recently_added'
- Add API routes to manage event webhook status
- Create user interface components for easy webhook configuration
- Update webhook manager to properly trigger events
- Add translations for new UI elements
2025-05-23 08:29:08 +00:00

314 lines
10 KiB
JavaScript

const express = require('express');
const router = express.Router();
const dbInstance = require('../db');
const WebhookManager = require('../classes/webhook-manager');
const WebhookScheduler = require('../classes/webhook-scheduler');
const webhookScheduler = new WebhookScheduler();
const webhookManager = new WebhookManager();
// Get all webhooks
router.get('/', async (req, res) => {
try {
const result = await dbInstance.query('SELECT * FROM webhooks ORDER BY id DESC');
res.json(result.rows);
} catch (error) {
console.error('Error fetching webhooks:', error);
res.status(500).json({ error: 'Failed to fetch webhooks' });
}
});
// Get a specific webhook by ID
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const result = await dbInstance.query('SELECT * FROM webhooks WHERE id = $1', [id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Webhook not found' });
}
res.json(result.rows[0]);
} catch (error) {
console.error('Error fetching webhook:', error);
res.status(500).json({ error: 'Failed to fetch webhook' });
}
});
// Create a new webhook
router.post('/', async (req, res) => {
try {
const {
name,
url,
headers,
payload,
method,
trigger_type,
schedule,
event_type,
enabled,
retry_on_failure,
max_retries
} = req.body;
if (!name || !url || !trigger_type) {
return res.status(400).json({ error: 'Name, URL and trigger type are required' });
}
if (trigger_type === 'scheduled' && !schedule) {
return res.status(400).json({ error: 'Schedule is required for scheduled webhooks' });
}
if (trigger_type === 'event' && !event_type) {
return res.status(400).json({ error: 'Event type is required for event webhooks' });
}
const result = await dbInstance.query(
`INSERT INTO webhooks (name, url, headers, payload, method, trigger_type, schedule, event_type, enabled, retry_on_failure, max_retries)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING *`,
[
name,
url,
JSON.stringify(headers || {}),
JSON.stringify(payload || {}),
method || 'POST',
trigger_type,
schedule,
event_type,
enabled !== undefined ? enabled : true,
retry_on_failure || false,
max_retries || 3
]
);
// Refresh the schedule if the webhook is scheduled
if (trigger_type === 'scheduled' && enabled) {
await webhookScheduler.refreshSchedule();
}
res.status(201).json(result.rows[0]);
} catch (error) {
console.error('Error creating webhook:', error);
res.status(500).json({ error: 'Failed to create webhook' });
}
});
// Update a webhook
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const {
name,
url,
headers,
payload,
method,
trigger_type,
schedule,
event_type,
enabled,
retry_on_failure,
max_retries
} = req.body;
if (!name || !url || !trigger_type) {
return res.status(400).json({ error: 'Name, URL and trigger type are required' });
}
const result = await dbInstance.query(
`UPDATE webhooks
SET name = $1, url = $2, headers = $3, payload = $4, method = $5,
trigger_type = $6, schedule = $7, event_type = $8, enabled = $9,
retry_on_failure = $10, max_retries = $11
WHERE id = $12
RETURNING *`,
[
name,
url,
JSON.stringify(headers || {}),
JSON.stringify(payload || {}),
method || 'POST',
trigger_type,
schedule,
event_type,
enabled !== undefined ? enabled : true,
retry_on_failure || false,
max_retries || 3,
id
]
);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Webhook not found' });
}
// Refresh the schedule if the webhook is scheduled
await webhookScheduler.refreshSchedule();
res.json(result.rows[0]);
} catch (error) {
console.error('Error updating webhook:', error);
res.status(500).json({ error: 'Failed to update webhook' });
}
});
// Delete a webhook
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
const result = await dbInstance.query('DELETE FROM webhooks WHERE id = $1 RETURNING *', [id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Webhook not found' });
}
// Refresh the schedule if the webhook was scheduled
await webhookScheduler.refreshSchedule();
res.json({ message: 'Webhook deleted successfully', webhook: result.rows[0] });
} catch (error) {
console.error('Error deleting webhook:', error);
res.status(500).json({ error: 'Failed to delete webhook' });
}
});
// Test a webhook
router.post('/:id/test', async (req, res) => {
try {
const { id } = req.params;
const result = await dbInstance.query('SELECT * FROM webhooks WHERE id = $1', [id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Webhook not found' });
}
const webhook = result.rows[0];
const testData = req.body || {};
const success = await webhookManager.executeWebhook(webhook, testData);
if (success) {
res.json({ message: 'Webhook executed successfully' });
} else {
res.status(500).json({ error: 'Webhook execution failed' });
}
} catch (error) {
console.error('Error testing webhook:', error);
res.status(500).json({ error: 'Failed to test webhook' });
}
});
router.post('/:id/trigger-monthly', async (req, res) => {
const webhookManager = new WebhookManager();
const success = await webhookManager.triggerMonthlySummaryWebhook(req.params.id);
if (success) {
res.status(200).json({ message: "Rapport mensuel envoyé avec succès" });
} else {
res.status(500).json({ message: "Échec de l'envoi du rapport mensuel" });
}
});
// Get status of event webhooks
router.get('/event-status', async (req, res) => {
try {
const eventTypes = ['playback_started', 'playback_ended', 'media_recently_added'];
const result = {};
for (const eventType of eventTypes) {
const webhooks = await dbInstance.query(
'SELECT id, name, enabled FROM webhooks WHERE trigger_type = $1 AND event_type = $2',
['event', eventType]
);
result[eventType] = {
exists: webhooks.rows.length > 0,
enabled: webhooks.rows.some(webhook => webhook.enabled),
webhooks: webhooks.rows
};
}
res.json(result);
} catch (error) {
console.error('Error fetching webhook status:', error);
res.status(500).json({ error: 'Failed to fetch webhook status' });
}
});
// Toggle all webhooks of a specific event type
router.post('/toggle-event/:eventType', async (req, res) => {
try {
const { eventType } = req.params;
const { enabled } = req.body;
if (!['playback_started', 'playback_ended', 'media_recently_added'].includes(eventType)) {
return res.status(400).json({ error: 'Invalid event type' });
}
if (typeof enabled !== 'boolean') {
return res.status(400).json({ error: 'Enabled parameter must be a boolean' });
}
// Mettre à jour tous les webhooks de ce type d'événement
const result = await dbInstance.query(
'UPDATE webhooks SET enabled = $1 WHERE trigger_type = $2 AND event_type = $3 RETURNING id',
[enabled, 'event', eventType]
);
// Si aucun webhook n'existe pour ce type, en créer un de base
if (result.rows.length === 0 && enabled) {
const defaultWebhook = {
name: `Webhook pour ${eventType}`,
url: req.body.url || '',
method: 'POST',
trigger_type: 'event',
event_type: eventType,
enabled: true,
headers: '{}',
payload: JSON.stringify({
event: `{{event}}`,
data: `{{data}}`,
timestamp: `{{triggeredAt}}`
})
};
if (!defaultWebhook.url) {
return res.status(400).json({
error: 'URL parameter is required when creating a new webhook',
needsUrl: true
});
}
await dbInstance.query(
`INSERT INTO webhooks (name, url, method, trigger_type, event_type, enabled, headers, payload)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
[
defaultWebhook.name,
defaultWebhook.url,
defaultWebhook.method,
defaultWebhook.trigger_type,
defaultWebhook.event_type,
defaultWebhook.enabled,
defaultWebhook.headers,
defaultWebhook.payload
]
);
}
// Rafraîchir le planificateur de webhooks
await webhookScheduler.refreshSchedule();
res.json({
success: true,
message: `Webhooks for ${eventType} ${enabled ? 'enabled' : 'disabled'}`,
affectedCount: result.rows.length
});
} catch (error) {
console.error('Error toggling webhooks:', error);
res.status(500).json({ error: 'Failed to toggle webhooks' });
}
});
module.exports = router;