Files
Jellystat/backend/routes/webhooks.js

425 lines
15 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];
let testData = req.body || {};
let success = false;
// Discord behaviour
if (webhook.url.includes('discord.com/api/webhooks')) {
console.log('Discord webhook détecté, préparation du payload spécifique');
// Discord specific format
testData = {
content: "Test de webhook depuis Jellystat",
embeds: [{
title: "Discord test notification",
description: "This is a test notification of jellystat discord webhook",
color: 3447003,
fields: [
{
name: "Webhook type",
value: webhook.trigger_type || "Not specified",
inline: true
},
{
name: "ID",
value: webhook.id,
inline: true
}
],
timestamp: new Date().toISOString()
}]
};
// Bypass classic method for discord
success = await webhookManager.executeDiscordWebhook(webhook, testData);
}
else if (webhook.trigger_type === 'event' && webhook.event_type) {
const eventType = webhook.event_type;
let eventData = {};
switch (eventType) {
case 'playback_started':
eventData = {
sessionInfo: {
userId: "test-user-id",
deviceId: "test-device-id",
deviceName: "Test Device",
clientName: "Test Client",
isPaused: false,
mediaType: "Movie",
mediaName: "Test Movie",
startTime: new Date().toISOString()
},
userData: {
username: "Test User",
userImageTag: "test-image-tag"
},
mediaInfo: {
itemId: "test-item-id",
episodeId: null,
mediaName: "Test Movie",
seasonName: null,
seriesName: null
}
};
success = await webhookManager.triggerEventWebhooks(eventType, eventData, [webhook.id]);
break;
case 'playback_ended':
eventData = {
sessionInfo: {
userId: "test-user-id",
deviceId: "test-device-id",
deviceName: "Test Device",
clientName: "Test Client",
mediaType: "Movie",
mediaName: "Test Movie",
startTime: new Date(Date.now() - 3600000).toISOString(),
endTime: new Date().toISOString(),
playbackDuration: 3600
},
userData: {
username: "Test User",
userImageTag: "test-image-tag"
},
mediaInfo: {
itemId: "test-item-id",
episodeId: null,
mediaName: "Test Movie",
seasonName: null,
seriesName: null
}
};
success = await webhookManager.triggerEventWebhooks(eventType, eventData, [webhook.id]);
break;
case 'media_recently_added':
eventData = {
mediaItem: {
id: "test-item-id",
name: "Test Media",
type: "Movie",
overview: "This is a test movie for webhook testing",
addedDate: new Date().toISOString()
}
};
success = await webhookManager.triggerEventWebhooks(eventType, eventData, [webhook.id]);
break;
default:
success = await webhookManager.executeWebhook(webhook, testData);
}
} else {
success = await webhookManager.executeWebhook(webhook, testData);
}
if (success) {
res.json({ message: 'Webhook executed successfully' });
} else {
res.status(500).json({ error: 'Error while executing webhook' });
}
} catch (error) {
console.error('Error testing webhook:', error);
res.status(500).json({ error: 'Failed to test webhook: ' + error.message });
}
});
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: "Monthly report sent successfully" });
} else {
res.status(500).json({ message: "Failed to send monthly report" });
}
});
// Get status of event webhooks
router.get('/event-status', authMiddleware, 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;