mirror of
https://github.com/BreizhHardware/Jellystat.git
synced 2026-01-18 16:27:20 +01:00
425 lines
15 KiB
JavaScript
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;
|