diff --git a/src/pages/app/Services.jsx b/src/pages/app/Services.jsx index beb095a..28dfef1 100644 --- a/src/pages/app/Services.jsx +++ b/src/pages/app/Services.jsx @@ -1,45 +1,347 @@ import { useState, useEffect } from 'react'; -import { getMyServices, getServiceDetails, getHostingServiceDetails, resetHostingPassword, launchSSOGateway } from '../../services/api'; +import { getMyServices, getServiceDetails, getHostingServiceDetails, resetHostingPassword } from '../../services/api'; import { useVPC } from '../../services/useVPC'; -import { Server, Database, Cloud, Globe, Folder, Trash2, ExternalLink, Loader, Plus } from 'lucide-react'; +import { Server, Database, Cloud, Globe, Folder, Trash2, ExternalLink, Loader, Plus, Play } from 'lucide-react'; +// ============================================================================ +// COMPOSANT ISOLÉ : INSTANCE CARD +// ============================================================================ +const InstanceCard = ({ service, vpcs, onAssignVpc, onRemoveVpc, onOpenConsole, isConnecting }) => { + const titleLower = (service.title || '').toLowerCase(); + const isVPS = titleLower.includes('vps') || titleLower.includes('compute'); + const isCloud = titleLower.includes('cloud') || titleLower.includes('nextcloud'); + const isDB = titleLower.includes('db') || titleLower.includes('base') || titleLower.includes('sql'); + + // Vérification de l'état d'initialisation pour le VPS + const hasIP = service.hostingDetails?.ip && service.hostingDetails.ip !== '127.0.0.1' && service.hostingDetails.ip !== ''; + + // ------------------------------------------------------------------------ + // DESIGN DES BOUTONS PAR TYPE DE SERVICE + // ------------------------------------------------------------------------ + let buttonText = "CONSOLE D'ADMINISTRATION"; + let ButtonIcon = ExternalLink; + let buttonStyle = "bg-cyan-400/10 hover:bg-cyan-400 text-cyan-400 hover:text-gray-900 border-cyan-400"; + + if (isVPS) { + if (hasIP) { + buttonText = "GÉRER L'INSTANCE"; + ButtonIcon = ExternalLink; + } else { + buttonText = "INITIALISATION"; + ButtonIcon = Play; + buttonStyle = "bg-yellow-500/10 text-yellow-500 hover:bg-yellow-500 hover:text-gray-900 border-yellow-500/50"; + } + } else if (isCloud) { + buttonText = "ACCÉDER AU CLOUD"; + ButtonIcon = ExternalLink; + buttonStyle = "bg-blue-400/10 hover:bg-blue-400 text-blue-400 hover:text-gray-900 border-blue-400"; + } else if (isDB) { + buttonText = "PHPMYADMIN / CLUSTER"; + ButtonIcon = Database; + buttonStyle = "bg-purple-400/10 hover:bg-purple-400 text-purple-400 hover:text-gray-900 border-purple-400"; + } + + const getServiceIcon = () => { + if (isVPS) return ; + if (isCloud) return ; + if (isDB) return ; + return ; + }; + + // ------------------------------------------------------------------------ + // NETTOYAGE DU TEXTE (Titre à 2 mots & Extraction de domaine) + // ------------------------------------------------------------------------ + + // 1. Coupe le "pour X" ou "for X" du titre, puis garde les 2 premiers mots + const baseTitle = (service.title || '').split(/(?: for | pour )/i)[0].trim(); + const shortTitle = baseTitle.split(' ').slice(0, 2).join(' '); + + // 2. Extraction du domaine qui se trouve après "pour" ou "for" dans le titre FOSSBilling + const titleMatch = (service.title || '').match(/(?: for | pour )(.+)$/i); + const domainFromTitle = titleMatch ? titleMatch[1].trim() : null; + + // 3. Choix du domaine à afficher (Priorité à la BDD, sinon on prend celui du titre) + let displayDomain = service.hostingDetails?.domain && service.hostingDetails.domain !== '127.0.0.1' + ? service.hostingDetails.domain + : domainFromTitle || service.domain; + + if (!displayDomain || displayDomain === '127.0.0.1') { + displayDomain = null; + } + + return ( +
+
+
+
+ {getServiceIcon()} +
+ {service.status === 'active' ? ( + + {isVPS && !hasIP ? 'AWAITING INIT' : 'ONLINE'} + + ) : ( + DEPLOYING + )} +
+ +

+ {shortTitle} +

+ + {/* Espace fixe pour le domaine (remplace l'ancien CMD: #id) */} +
+ {displayDomain ? ( +

+ {displayDomain} +

+ ) : ( +

+ Non configuré +

+ )} +
+
+ +
+
+ Projet VPC: + +
+ + +
+
+ ); +}; + +// ============================================================================ +// MODAL : VPS DEPLOYER +// ============================================================================ +const VpsDeployer = ({ serviceId, onDeploySuccess }) => { + const [domain, setDomain] = useState(''); + const [password, setPassword] = useState(''); + const [isDeploying, setIsDeploying] = useState(false); + + const handleDeploy = async () => { + if (password.length < 8) { alert("Le mot de passe doit faire au moins 8 caractères."); return; } + + setIsDeploying(true); + try { + const response = await fetch('https://web.gise.be/custom_api/proxmox_create_vps.php', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ service_id: serviceId, domain: domain, password: password }) + }); + const data = await response.json(); + + if (data.status === 'success') { + onDeploySuccess({ ip: data.ip, domain: data.domain, password: password }); + } else { + alert("Erreur de provisionnement : " + (data.message || 'Inconnue')); + } + } catch (err) { + alert("Erreur de communication avec l'API Proxmox Gateway."); + } finally { + setIsDeploying(false); + } + }; + + return ( +
+
+ PHASE D'INITIALISATION REQUISE
+ La facturation est activée. Veuillez définir un mot de passe Root pour lancer la création de l'instance sur l'hyperviseur. +
+ +
+
+ + setDomain(e.target.value.toLowerCase())} placeholder="Laissez vide pour utiliser l'IP" className="w-full bg-gray-950 border border-gray-800 text-white p-3 rounded font-mono outline-none focus:border-cyan-500 transition-colors" /> +
+
+ + setPassword(e.target.value)} placeholder="••••••••" className="w-full bg-gray-950 border border-gray-800 text-white p-3 rounded font-mono outline-none focus:border-cyan-500 transition-colors" /> +
+
+ +
+ ); +}; + +// ============================================================================ +// MODAL : VPS MANAGER (Modèle PaaS Complet) +// ============================================================================ +const VpsManager = ({ details }) => { + const [showTerminal, setShowTerminal] = useState(false); + + // Les URLs de tes outils internes + const fileManagerUrl = `https://file.${details.domain}`; + const terminalUrl = `https://terminal.${details.domain}`; + + return ( +
+ {!showTerminal ? ( + <> +
+ {/* BLOC RÉSEAU */} +
+
+ +
+ + {details.domain} +
+
+
+ + {/* BLOC OUTILS MANAGÉS */} +
+
+ + +

• Utilisateur: root

+

• Pass: NexusGise2026! (à modifier)

+
+
+ + +

• Utilisateur: root

+

• Pass: mot de passe d'initialisation

+
+
+
+ +
+ + +
+ + ) : ( + /* VUE CONSOLE INTÉGRÉE */ +
+
+ + + NEXUS TERMINAL ({details.domain}) + +
+ + +
+
+ + {/* Le conteneur iFrame qui charge TTYD (xterm.js) */} +
+ {/* Message d'aide qui s'affiche brièvement pendant le chargement de l'iframe */} +
+ +

Chargement du moteur xterm.js...

+

Identifiant : root

+

Mot de passe : Celui d'initialisation

+
+ +