add connexions
This commit is contained in:
+2
-2
@@ -2,5 +2,5 @@ FossBilling :
|
|||||||
fpBTefVk568S4VP36feE6i7XKcqNmvi4
|
fpBTefVk568S4VP36feE6i7XKcqNmvi4
|
||||||
|
|
||||||
Hestiacp :
|
Hestiacp :
|
||||||
id : EpIQTIAJVlYGRerVfiNo
|
id : s8UXxCnsZTTCDJzCfY4I
|
||||||
secret : PMzs_nKYFelMfa3T9Vu9NgNZDtDFenxjeFCo-7Eq
|
secret : Qgax08BFStr2P3aEu-cRvJPbEUAh7CM5SVSZ5oQn
|
||||||
@@ -4,6 +4,7 @@ import PublicLayout from './layouts/PublicLayout';
|
|||||||
import Home from './pages/public/Home';
|
import Home from './pages/public/Home';
|
||||||
import Login from './pages/public/Login';
|
import Login from './pages/public/Login';
|
||||||
import Dashboard from './pages/app/Dashboard';
|
import Dashboard from './pages/app/Dashboard';
|
||||||
|
import Register from './pages/public/Register';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -13,6 +14,7 @@ function App() {
|
|||||||
<Route element={<PublicLayout />}>
|
<Route element={<PublicLayout />}>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
|
<Route path="/register" element={<Register />} />
|
||||||
{/* Tu pourras ajouter /offres, /register ici plus tard */}
|
{/* Tu pourras ajouter /offres, /register ici plus tard */}
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,27 @@ import { Outlet, Link } from 'react-router-dom';
|
|||||||
export default function PublicLayout() {
|
export default function PublicLayout() {
|
||||||
return (
|
return (
|
||||||
<div style={{ backgroundColor: '#121212', color: '#E0E0E0', minHeight: '100vh', fontFamily: 'monospace' }}>
|
<div style={{ backgroundColor: '#121212', color: '#E0E0E0', minHeight: '100vh', fontFamily: 'monospace' }}>
|
||||||
<nav style={{ padding: '20px', borderBottom: '1px solid #242424', display: 'flex', gap: '15px' }}>
|
<nav style={{ padding: '20px', borderBottom: '1px solid #242424', display: 'flex', gap: '15px', alignItems: 'center' }}>
|
||||||
<Link to="/" style={{ color: '#00E5FF', textDecoration: 'none' }}>[ GISE_BUNKER ]</Link>
|
<Link to="/" style={{ color: '#00E5FF', textDecoration: 'none', fontWeight: 'bold' }}>[ GISE_BUNKER ]</Link>
|
||||||
<Link to="/offres" style={{ color: '#E0E0E0', textDecoration: 'none' }}>Catalogue</Link>
|
<Link to="/offres" style={{ color: '#E0E0E0', textDecoration: 'none' }}>Catalogue</Link>
|
||||||
<Link to="/login" style={{ color: '#E0E0E0', textDecoration: 'none', marginLeft: 'auto' }}>Connexion</Link>
|
|
||||||
|
{/* AJOUT DU BOUTON D'INSCRIPTION */}
|
||||||
|
<Link to="/register" style={{ color: '#A0A0A0', textDecoration: 'none', marginLeft: 'auto', fontSize: '0.9rem' }}>
|
||||||
|
Créer un compte
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link to="/login" style={{
|
||||||
|
color: '#000',
|
||||||
|
backgroundColor: '#00E5FF',
|
||||||
|
padding: '6px 12px',
|
||||||
|
textDecoration: 'none',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: '0.9rem'
|
||||||
|
}}>
|
||||||
|
Connexion
|
||||||
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* C'est ici que les pages (Accueil, Login, etc.) vont s'afficher */}
|
{/* C'est ici que les pages (Accueil, Login, etc.) vont s'afficher */}
|
||||||
<main style={{ padding: '20px' }}>
|
<main style={{ padding: '20px' }}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default function Dashboard() {
|
|||||||
window.open(serviceData.server.login_url, '_blank');
|
window.open(serviceData.server.login_url, '_blank');
|
||||||
}
|
}
|
||||||
else if (serviceData && serviceData.server) {
|
else if (serviceData && serviceData.server) {
|
||||||
|
console.log(serviceData);
|
||||||
// L'URL d'action du formulaire de connexion HestiaCP
|
// L'URL d'action du formulaire de connexion HestiaCP
|
||||||
const hestiaLoginUrl = "https://panel.gise.be/login/";
|
const hestiaLoginUrl = "https://panel.gise.be/login/";
|
||||||
const username = serviceData.username;
|
const username = serviceData.username;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'center', marginTop: '10vh' }}>
|
<div style={{ textAlign: 'center', marginTop: '10vh' }}>
|
||||||
@@ -22,21 +24,24 @@ export default function Home() {
|
|||||||
<br />Propulsé par une architecture bare-metal locale.
|
<br />Propulsé par une architecture bare-metal locale.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Un petit bouton d'action pour la suite */}
|
{/* LE BOUTON D'ACTION PRINCIPAL REDIRIGE VERS /REGISTER */}
|
||||||
<div style={{ marginTop: '40px' }}>
|
<div style={{ marginTop: '40px' }}>
|
||||||
<button style={{
|
<Link to="/register" style={{
|
||||||
|
display: 'inline-block',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
color: '#00E5FF',
|
color: '#00E5FF',
|
||||||
border: '1px solid #00E5FF',
|
border: '1px solid #00E5FF',
|
||||||
padding: '12px 24px',
|
padding: '12px 24px',
|
||||||
fontSize: '1rem',
|
fontSize: '1rem',
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
cursor: 'pointer',
|
textDecoration: 'none',
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: '1px'
|
letterSpacing: '1px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
transition: 'all 0.3s ease'
|
||||||
}}>
|
}}>
|
||||||
Démarrer le déploiement
|
Démarrer le déploiement
|
||||||
</button>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
// src/pages/public/Register.jsx
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useNavigate, Link } from 'react-router-dom';
|
||||||
|
import { registerUnifiedClient } from '../../services/api';
|
||||||
|
|
||||||
|
export default function Register() {
|
||||||
|
const [firstName, setFirstName] = useState('');
|
||||||
|
const [lastName, setLastName] = useState('');
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [confirmPassword, setConfirmPassword] = useState('');
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleRegister = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
// Sécurité Frontend : Validation des mots de passe avant envoi au serveur
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
setError("Les clés d'accès (mots de passe) ne correspondent pas.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sécurité Frontend : Regex simple pour valider le format exigé par HestiaCP
|
||||||
|
const validUsername = /^[a-zA-Z0-9]{3,12}$/.test(username);
|
||||||
|
if (!validUsername) {
|
||||||
|
setError("Le nom d'utilisateur doit contenir uniquement des lettres ou chiffres (entre 3 et 12 caractères, sans espace).");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Envoi de la requête groupée à notre orchestrateur PHP backend
|
||||||
|
await registerUnifiedClient(email, username, password, firstName, lastName);
|
||||||
|
|
||||||
|
alert("[ PROVISIONNEMENT RÉUSSI ]\nVos comptes FOSSBilling, HestiaCP et Nextcloud ont été initialisés.\nVous pouvez maintenant vous connecter.");
|
||||||
|
|
||||||
|
// Redirection automatique vers la page de login après succès
|
||||||
|
navigate('/login');
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message || "Échec de l'initialisation de l'infrastructure.");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Réutilisation de ton Design System "Bunker"
|
||||||
|
const inputStyle = {
|
||||||
|
width: '100%', padding: '10px', marginBottom: '15px',
|
||||||
|
backgroundColor: '#1A1A1A', color: '#00E5FF',
|
||||||
|
border: '1px solid #333', fontFamily: 'monospace', outline: 'none',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonStyle = {
|
||||||
|
width: '100%', padding: '12px', backgroundColor: loading ? '#333' : '#00E5FF',
|
||||||
|
color: loading ? '#888' : '#000', border: 'none', cursor: loading ? 'not-allowed' : 'pointer',
|
||||||
|
fontFamily: 'monospace', fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: '1px',
|
||||||
|
marginTop: '10px'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '5vh', padding: '0 20px' }}>
|
||||||
|
<div style={{
|
||||||
|
width: '100%', maxWidth: '500px', backgroundColor: '#242424',
|
||||||
|
padding: '30px', borderTop: '4px solid #00E5FF', boxShadow: '0 10px 30px rgba(0,0,0,0.5)'
|
||||||
|
}}>
|
||||||
|
<h2 style={{ color: '#FFF', textTransform: 'uppercase', marginBottom: '5px', fontSize: '1.5rem' }}>
|
||||||
|
Créer un accès réseau
|
||||||
|
</h2>
|
||||||
|
<p style={{ color: '#888', fontFamily: 'monospace', fontSize: '0.85rem', marginBottom: '25px' }}>
|
||||||
|
[ INITIALISATION DU PROVISIONNEMENT TRIPLE EN CASCADE ]
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div style={{ backgroundColor: 'rgba(255, 68, 68, 0.1)', color: '#FF4444', padding: '10px', marginBottom: '20px', border: '1px solid #FF4444', fontSize: '0.9rem', fontFamily: 'monospace' }}>
|
||||||
|
[ ERREUR ] : {error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<form onSubmit={handleRegister}>
|
||||||
|
<div style={{ display: 'flex', gap: '15px' }}>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>PRÉNOM</label>
|
||||||
|
<input type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} style={inputStyle} required placeholder="John" />
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>NOM</label>
|
||||||
|
<input type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} style={inputStyle} required placeholder="Doe" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>ADRESSE E-MAIL (IDENTIFIANT FACTURATION)</label>
|
||||||
|
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} style={inputStyle} required placeholder="john.doe@gise.be" />
|
||||||
|
|
||||||
|
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>NOM D'UTILISATEUR SÉCURISÉ (ACCÈS HESTIA & CLOUD)</label>
|
||||||
|
<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} style={inputStyle} required placeholder="ex: jdoe42" />
|
||||||
|
|
||||||
|
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>CLÉ D'ACCÈS (MOT DE PASSE UNIQUE)</label>
|
||||||
|
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} style={inputStyle} required placeholder="••••••••" />
|
||||||
|
|
||||||
|
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>CONFIRMER LA CLÉ D'ACCÈS</label>
|
||||||
|
<input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} style={inputStyle} required placeholder="••••••••" />
|
||||||
|
|
||||||
|
<button type="submit" style={buttonStyle} disabled={loading}>
|
||||||
|
{loading ? 'DÉPLOIEMENT EN COURS...' : 'LANCER LE PROVISIONNEMENT'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div style={{ marginTop: '20px', textAlign: 'center', fontSize: '0.85rem' }}>
|
||||||
|
<Link to="/login" style={{ color: '#888', textDecoration: 'none' }}>
|
||||||
|
Déjà enregistré ? <span style={{ color: '#00E5FF' }}>Se connecter au terminal ></span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+28
-5
@@ -16,16 +16,39 @@ const apiCall = async (url, body = {}) => {
|
|||||||
return data.result;
|
return data.result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loginClient = (email, password) =>
|
export const loginClient = (email, password) =>
|
||||||
apiCall('/api/guest/client/login', { email, password });
|
apiCall('/api/guest/client/login', { email, password });
|
||||||
|
|
||||||
export const getClientProfile = () =>
|
export const getClientProfile = () =>
|
||||||
apiCall('/api/client/profile/get');
|
apiCall('/api/client/profile/get');
|
||||||
|
|
||||||
// Récupère la liste des services/commandes du client
|
// Récupère la liste des services/commandes du client
|
||||||
export const getClientOrders = () =>
|
export const getClientOrders = () =>
|
||||||
apiCall('/api/client/order/get_list');
|
apiCall('/api/client/order/get_list');
|
||||||
|
|
||||||
// Récupère les détails techniques du service rattaché à une commande
|
// Récupère les détails techniques du service rattaché à une commande
|
||||||
export const getOrderService = (order_id) =>
|
export const getOrderService = (order_id) =>
|
||||||
apiCall('/api/client/order/service', { id: order_id });
|
apiCall('/api/client/order/service', { id: order_id });
|
||||||
|
|
||||||
|
// Fonction d'inscription unifiee pour créer un compte client et commander un service en une seule étape
|
||||||
|
export const registerUnifiedClient = async (email, username, password, firstName, lastName) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/custom_api/signup.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
first_name: firstName, // L'étiquette est "first_name", le contenu est ta variable JS "firstName"
|
||||||
|
last_name: lastName // L'étiquette est "last_name", le contenu est ta variable JS "lastName"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.error) throw new Error(data.error.message);
|
||||||
|
return data.result;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Erreur lors de l'inscription unifiée :", err);
|
||||||
|
throw new Error("Échec de l'inscription. Veuillez réessayer plus tard.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,11 @@ export default defineConfig({
|
|||||||
target: 'https://web.gise.be',
|
target: 'https://web.gise.be',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false
|
secure: false
|
||||||
|
},
|
||||||
|
'/custom_api': {
|
||||||
|
target: 'https://web.gise.be',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user