From 15fac6c472b27a296a943e21b8bf705eaaaed115 Mon Sep 17 00:00:00 2001 From: ElPouki <61667226+Nackophilz@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:33:48 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Ajout=20du=20support=20pour=20l'=C3=A9d?= =?UTF-8?q?ition/mise=20=C3=A0=20jour=20des=20clients=20torrent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nouveau helper updateClient et un endpoint PUT /api/torrent-clients/:uuid qui enregistre les modifications, journalise les mises à jour et renvoie le client. Mise à jour de l'UI des paramètres pour ajouter un bouton "Modifier" et réutiliser la modale d'ajout de client pour les éditions (afin d'assurer le suivi de l'uuid ), bascule méthode PUT pour les requêtes de mise à jour, et actualisation de la liste des clients ainsi que des notifications en cas de modification réussie. --- server/index.ts | 24 ++++++++++++-- server/torrent-clients/index.ts | 24 ++++++++++++++ src/views/settings/DownloadClientView.vue | 38 ++++++++++++++++++----- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/server/index.ts b/server/index.ts index 11bcf8a..8f8418c 100644 --- a/server/index.ts +++ b/server/index.ts @@ -7,7 +7,7 @@ import { readSettings, writeSettings } from './settings.js' import { registerDriver, getDriver, getAvailableClients, listClients, addClient, removeClient, getClient, - sanitizeClient, dispatchDownload, dispatchList + sanitizeClient, dispatchDownload, dispatchList, updateClient } from './torrent-clients/index.js' import qbittorrentDriver from './torrent-clients/qbittorrent.js' import transmissionDriver from './torrent-clients/transmission.js' @@ -71,11 +71,31 @@ app.delete('/api/torrent-clients/:uuid', requireAuth, (req, res) => { if (!ok) { res.status(404).json({ error: 'Client introuvable' }); return } res.json({ ok: true }) }) +app.put('/api/torrent-clients/:uuid', requireAuth, (req, res) => { + const { name, type, config } = req.body + if (!name || !type || !config) { res.status(400).json({ error: 'name, type et config requis' }); return } + if (!getDriver(type)) { res.status(400).json({ error: `Type inconnu : ${type}` }); return } + const updated = updateClient(String(req.params.uuid), name, type, config) + if (!updated) { res.status(404).json({ error: 'Client introuvable' }); return } + res.json(sanitizeClient(updated)) +}) app.post('/api/torrent-clients/test-config', requireAuth, async (req, res) => { - const { type, config } = req.body + const { type, config, uuid } = req.body if (!type || !config) { res.status(400).json({ error: 'type et config requis' }); return } const driver = getDriver(type) if (!driver) { res.status(400).json({ error: `Type inconnu : ${type}` }); return } + + if (uuid) { + const oldClient = getClient(uuid) + if (oldClient) { + for (const field of driver.definition.fields) { + if (field.type === 'password' && config[field.key] === '••••••••') { + config[field.key] = oldClient.config[field.key] + } + } + } + } + res.json(await driver.test(config)) }) app.post('/api/torrent-clients/:uuid/test', requireAuth, async (req, res) => { diff --git a/server/torrent-clients/index.ts b/server/torrent-clients/index.ts index bd0baa3..146c56d 100644 --- a/server/torrent-clients/index.ts +++ b/server/torrent-clients/index.ts @@ -103,6 +103,30 @@ export function addClient(name: string, type: string, config: Record): SavedClient | null { + const clients = loadClients() + const index = clients.findIndex(c => c.uuid === uuid) + if (index === -1) return null + + const oldClient = clients[index] + const definition = getDriver(type)?.definition + + // Conserver les mots de passe si "••••••••" est envoyé + if (definition) { + for (const field of definition.fields) { + if (field.type === 'password' && config[field.key] === '••••••••') { + config[field.key] = oldClient.config[field.key] + } + } + } + + const client: SavedClient = { uuid, name, type, config } + clients[index] = client + saveClients(clients) + logger.info('torrent-clients', `Client mis à jour : "${name}" (${type})`) + return client +} + export function removeClient(uuid: string): boolean { const clients = loadClients() const target = clients.find(c => c.uuid === uuid) diff --git a/src/views/settings/DownloadClientView.vue b/src/views/settings/DownloadClientView.vue index cf32815..e905d33 100644 --- a/src/views/settings/DownloadClientView.vue +++ b/src/views/settings/DownloadClientView.vue @@ -34,6 +34,7 @@
+
@@ -49,7 +50,7 @@
-

Ajouter un client

+

{{ modal.uuid ? 'Modifier un client' : 'Ajouter un client' }}

@@ -116,6 +117,7 @@ const healthStatus = ref>({}) const modal = ref({ open : false, + uuid : null as string | null, name : '', type : '', config : {} as Record, @@ -171,7 +173,20 @@ async function deleteClient(uuid: string) { } function openAddModal() { - modal.value = { open: true, name: '', type: '', config: {}, testing: false, saving: false, tested: false } + modal.value = { open: true, uuid: null, name: '', type: '', config: {}, testing: false, saving: false, tested: false } +} + +function editClient(client: any) { + modal.value = { + open : true, + uuid : client.uuid, + name : client.name, + type : client.type, + config : JSON.parse(JSON.stringify(client.config)), + testing: false, + saving : false, + tested : true, + } } function closeModal() { modal.value.open = false } @@ -193,7 +208,7 @@ async function testNewClient() { try { const res = await fetch('/api/torrent-clients/test-config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', - body: JSON.stringify({ type: modal.value.type, config: modal.value.config }), + body: JSON.stringify({ type: modal.value.type, config: modal.value.config, uuid: modal.value.uuid }), }) const { ok, message } = await res.json() modal.value.tested = ok @@ -209,8 +224,12 @@ async function saveClient() { if (!modal.value.tested) return modal.value.saving = true try { - const res = await fetch('/api/torrent-clients', { - method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', + const isEdit = !!modal.value.uuid + const method = isEdit ? 'PUT' : 'POST' + const url = isEdit ? `/api/torrent-clients/${modal.value.uuid}` : '/api/torrent-clients' + + const res = await fetch(url, { + method, headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ name : modal.value.name || clientLabel(modal.value.type), type : modal.value.type, @@ -219,10 +238,15 @@ async function saveClient() { }) if (res.ok) { const client = await res.json() - clients.value.push(client) + if (isEdit) { + const index = clients.value.findIndex(c => c.uuid === client.uuid) + if (index !== -1) clients.value[index] = client + } else { + clients.value.push(client) + } refreshHealth(client.uuid) closeModal() - toast('Client enregistré ✓', 'success') + toast(isEdit ? 'Client modifié ✓' : 'Client enregistré ✓', 'success') } else { const { error } = await res.json() toast(error ?? "Erreur lors de l'enregistrement", 'error')