From 2a46fcd13ac7b6ee24ab5b7dce03eb772083cbae Mon Sep 17 00:00:00 2001 From: jeanotx32 Date: Tue, 2 Jun 2026 19:23:54 -0400 Subject: [PATCH] Feat: add VPS export functionality and import JSON feature in UI --- .../backend/__pycache__/main.cpython-313.pyc | Bin 47740 -> 48688 bytes vps-monitor/backend/main.py | 17 ++ vps-monitor/docker-compose.yml | 19 +- vps-monitor/frontend/src/App.jsx | 8 +- vps-monitor/frontend/src/api/client.js | 5 + .../frontend/src/components/AddVpsModal.jsx | 175 +++++++++++++----- .../frontend/src/components/VpsCard.jsx | 19 +- 7 files changed, 187 insertions(+), 56 deletions(-) diff --git a/vps-monitor/backend/__pycache__/main.cpython-313.pyc b/vps-monitor/backend/__pycache__/main.cpython-313.pyc index 81e3f2e40fe6c412f50ece2cbc374d4702c1ba4c..9ac257c6bd56c89a925001695574f9f01f0937b0 100644 GIT binary patch delta 1216 zcmZWnZ)g)|7{9yZF6kv{RIG`AYR|Q`y9zyvrs}A5LuA$s6;6w_#d?deq{Jq?m$cUE z20907&5TdGW-hbUD5Pkl@el+*3XaVW>beiZG1$j}eu%^kQ5k#QOVUz3lIQpQ`Q7h% zpXa@|FS+TrTxG%K5*d6R1#0>}JDsTXvXi-n4=n7cvBPqYWye6^x3Z(KgO`_&s{5BQ zeO4c(<3skTqiPq;kWN@&_ZZjtzghM>BW}CKLA+v>`{X=!gmS7*`PkyyE18vyI<9W) zZdAX9HnGNbnzH*?m=@Q-KlT;yMpR6dGxJn*sBD)RJL(rEioIqh8GtN{V|qn)%A5Fy)L|*<`C@WOu9+1-F^)15!wn{Net?!QL3rqQP)KMn@u{`D$X?}=aKRh_oC}qc?JKa}2M+1{&#?$!1ZFdTJ#?A}IFW;`Vvi}+{AZ~HQ=~>JJ z0h}Fg<;O67W;`6f!SP9SYmDJbuUMfG4@%BKbH)qV)RHo#WK^kzxZ*ld=Be=c#!?seUma}j@&l!sB!t$6|Ron2cf$mbSvhBgib(cb3(;Q4i4n1 z9Y3LqKSp0uVSJP0ZR=`h*t$IDVucHOodZK4vKOnS z^rYY@DTXW-Q;>2XD2fQ46dVl_DTq(3;pFDv}dh&8^GoU|gcvL5|^OyixeVmG- z6Vc2mOJ~xYzWEGKNb~0G$qFotPdE2W?_=if0GeCm10s4StIoDz?FTY%ZBCoLnu&E1 zkjb}MY~Bw>CQ0VW@{=qk>nsps)SSF~lDt4!L2-O>Nn%N9anW?3GO^8R3#yseRGES1 zOq;xMgT&ABi*L)yT9w=K}G}&&W+2(_b=QFXHz*RF9&7It^Oq6ln z { + const config = await exportVps(vpsId) + await navigator.clipboard.writeText(JSON.stringify(config, null, 2)) + } + // Attente vérification auth if (!authChecked) return null @@ -305,6 +310,7 @@ export default function App() { onEdit={setEditVps} onStats={(vpsId, vpsName) => setStatsModal({ vpsId, vpsName })} onUpdateAgent={handleUpdateAgent} + onExport={handleExportVps} /> ))} diff --git a/vps-monitor/frontend/src/api/client.js b/vps-monitor/frontend/src/api/client.js index 310114b..f094eff 100644 --- a/vps-monitor/frontend/src/api/client.js +++ b/vps-monitor/frontend/src/api/client.js @@ -117,6 +117,11 @@ export async function updateVps(vpsId, data) { return handleResponse(res) } +export async function exportVps(vpsId) { + const res = await fetch(`${BASE}/vps/${vpsId}/export`, { headers: authHeaders() }) + return handleResponse(res) +} + export async function fetchVpsStats(vpsId, duration = 600) { const res = await fetch(`${BASE}/vps/${vpsId}/stats?duration=${duration}`, { headers: authHeaders() }) return handleResponse(res) diff --git a/vps-monitor/frontend/src/components/AddVpsModal.jsx b/vps-monitor/frontend/src/components/AddVpsModal.jsx index 63eea07..f348fc9 100644 --- a/vps-monitor/frontend/src/components/AddVpsModal.jsx +++ b/vps-monitor/frontend/src/components/AddVpsModal.jsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { X } from 'lucide-react' +import { X, Upload } from 'lucide-react' import TagInput from './TagInput' const DEFAULTS = { id: '', name: '', host: '', port: '8001', api_key: '', description: '', tags: [] } @@ -14,9 +14,12 @@ const FIELDS = [ ] export default function AddVpsModal({ onSave, onClose }) { + const [mode, setMode] = useState('manual') // 'manual' | 'import' const [form, setForm] = useState(DEFAULTS) const [saving, setSaving] = useState(false) const [error, setError] = useState('') + const [json, setJson] = useState('') + const [jsonError, setJsonError] = useState('') useEffect(() => { const handler = (e) => { if (e.key === 'Escape') onClose() } @@ -26,6 +29,31 @@ export default function AddVpsModal({ onSave, onClose }) { const set = (key) => (e) => setForm(f => ({ ...f, [key]: e.target.value })) + const handleImportJson = () => { + setJsonError('') + try { + const parsed = JSON.parse(json.trim()) + const required = ['id', 'name', 'host', 'port', 'api_key'] + const missing = required.filter(k => !parsed[k]) + if (missing.length > 0) { + setJsonError(`Champs manquants : ${missing.join(', ')}`) + return + } + setForm({ + id: String(parsed.id), + name: String(parsed.name), + host: String(parsed.host), + port: String(parsed.port), + api_key: String(parsed.api_key), + description: String(parsed.description ?? ''), + tags: Array.isArray(parsed.tags) ? parsed.tags : [], + }) + setMode('manual') + } catch { + setJsonError('JSON invalide. Assurez-vous d\'avoir collé la config exportée.') + } + } + const handleSubmit = async (e) => { e.preventDefault() setSaving(true) @@ -45,58 +73,115 @@ export default function AddVpsModal({ onSave, onClose }) { >
-

Ajouter un VPS

+
+

Ajouter un VPS

+
+ + +
+
-
- {FIELDS.map(({ key, label, placeholder, required, type }) => ( -
- - -
- ))} - - {error && ( -

- {error} + {mode === 'import' ? ( +

+

+ Collez le JSON copié via le bouton Exporter d'une autre instance.

- )} - -
- - setForm(f => ({ ...f, tags }))} /> -

Entrée ou virgule pour valider

+