Files
copymailrpi/web/admin/domain.php
2026-05-06 01:23:38 +02:00

287 lines
14 KiB
PHP

<?php
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/layout.php';
admin_check();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$fields = ['domain', 'hostname', 'smtp_port', 'submission_port', 'imap_port', 'imaps_port', 'smb_workgroup', 'vision_license_key'];
foreach ($fields as $f) {
$val = trim($_POST[$f] ?? '');
if ($val !== '') cfg_set($f, $val);
}
// Régénère les fichiers de config et recharge les services
$result = shell_exec('/opt/copymail/scripts/apply_config.sh 2>&1');
flash('success', 'Configuration sauvegardée et services rechargés.');
header('Location: /admin/domain.php');
exit;
}
$fields = ['domain', 'hostname', 'smtp_port', 'submission_port', 'imap_port', 'imaps_port', 'smb_workgroup'];
$vals = [];
foreach ($fields as $f) $vals[$f] = cfg($f);
html_head('Configuration');
html_navbar('domain');
?>
<div class="container" style="max-width:700px">
<?php flash_render(); ?>
<h5 class="mb-4"><i class="bi bi-gear-fill me-2"></i>Configuration du serveur</h5>
<form method="post">
<div class="card shadow-sm mb-4">
<div class="card-header fw-semibold"><i class="bi bi-globe me-2"></i>Réseau & Domaine</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Nom de domaine mail</label>
<input type="text" name="domain" class="form-control" value="<?= htmlspecialchars($vals['domain']) ?>" placeholder="mail.mondomaine.fr" required>
<div class="form-text">Ex: <code>mail.mondomaine.fr</code></div>
</div>
<div class="col-md-6">
<label class="form-label">Hostname RPi</label>
<input type="text" name="hostname" class="form-control" value="<?= htmlspecialchars($vals['hostname']) ?>" placeholder="raspberrypi">
</div>
</div>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-header fw-semibold"><i class="bi bi-envelope-at me-2"></i>Ports mail</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label">SMTP (réception)</label>
<input type="number" name="smtp_port" class="form-control" value="<?= htmlspecialchars($vals['smtp_port']) ?>" min="1" max="65535">
<div class="form-text">Défaut: 25</div>
</div>
<div class="col-md-3">
<label class="form-label">Submission</label>
<input type="number" name="submission_port" class="form-control" value="<?= htmlspecialchars($vals['submission_port']) ?>" min="1" max="65535">
<div class="form-text">Défaut: 587</div>
</div>
<div class="col-md-3">
<label class="form-label">IMAP</label>
<input type="number" name="imap_port" class="form-control" value="<?= htmlspecialchars($vals['imap_port']) ?>" min="1" max="65535">
<div class="form-text">Défaut: 143</div>
</div>
<div class="col-md-3">
<label class="form-label">IMAPS</label>
<input type="number" name="imaps_port" class="form-control" value="<?= htmlspecialchars($vals['imaps_port']) ?>" min="1" max="65535">
<div class="form-text">Défaut: 993</div>
</div>
</div>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-header fw-semibold"><i class="bi bi-folder-symlink me-2"></i>Samba (SMB)</div>
<div class="card-body">
<div class="col-md-4">
<label class="form-label">Workgroup</label>
<input type="text" name="smb_workgroup" class="form-control" value="<?= htmlspecialchars($vals['smb_workgroup']) ?>" placeholder="WORKGROUP">
</div>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-header fw-semibold"><i class="bi bi-shield-check me-2"></i>Licence Vision</div>
<div class="card-body">
<?php
require_once __DIR__ . '/../includes/vision.php';
$lic = vision_check();
$licClass = $lic['valid'] ? 'success' : 'danger';
$licIcon = $lic['valid'] ? 'check-circle-fill' : 'x-circle-fill';
$licMsg = $lic['valid']
? ('Valide — ' . htmlspecialchars($lic['client'] ?? '') . (!empty($lic['expires']) ? ' — expire le ' . date('d/m/Y', strtotime($lic['expires'])) : ''))
: htmlspecialchars($lic['msg'] ?? 'Non valide');
if (!empty($lic['degraded'])) $licClass = 'warning';
?>
<div class="alert alert-<?= $licClass ?> py-2 mb-3">
<i class="bi bi-<?= $licIcon ?> me-2"></i><?= $licMsg ?>
</div>
<div class="col-md-8">
<label class="form-label">Clé de licence</label>
<input type="text" name="vision_license_key" class="form-control font-monospace"
value="<?= htmlspecialchars(cfg('vision_license_key', '')) ?>"
placeholder="COPYDEV-XXXXXXXX-XXXXXXXX-XXXXXXXX">
<div class="form-text">Générée depuis <strong>Vision</strong> → onglet Licences → produit CopyMail.</div>
</div>
</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">
<i class="bi bi-floppy-fill me-1"></i>Sauvegarder & Appliquer
</button>
<a href="/admin/index.php" class="btn btn-outline-secondary">Annuler</a>
</div>
</form>
<!-- ── Réseau (hors formulaire principal) ── -->
<div class="card shadow-sm mt-4">
<div class="card-header fw-semibold d-flex justify-content-between align-items-center">
<span><i class="bi bi-hdd-network me-2"></i>Réseau</span>
<span id="net-current-ip" class="badge bg-secondary font-monospace">chargement…</span>
</div>
<div class="card-body">
<div id="net-loading" class="text-muted small mb-3">
<div class="spinner-border spinner-border-sm me-2"></div>Lecture de la configuration…
</div>
<div id="net-form" style="display:none">
<!-- Mode -->
<div class="mb-3">
<label class="form-label fw-semibold">Mode</label>
<div class="d-flex gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="net-mode" id="net-dhcp" value="dhcp">
<label class="form-check-label" for="net-dhcp">DHCP <span class="text-muted small">(automatique)</span></label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="net-mode" id="net-static" value="static">
<label class="form-check-label" for="net-static">IP Statique</label>
</div>
</div>
</div>
<!-- Champs IP statique -->
<div id="net-static-fields" style="display:none">
<div class="row g-3 mb-3">
<div class="col-md-5">
<label class="form-label">Adresse IP</label>
<input type="text" id="net-ip" class="form-control font-monospace" placeholder="192.168.1.100">
</div>
<div class="col-md-2">
<label class="form-label">Masque</label>
<select id="net-prefix" class="form-select font-monospace">
<option value="24">/24 — 255.255.255.0</option>
<option value="25">/25 — 255.255.255.128</option>
<option value="16">/16 — 255.255.0.0</option>
<option value="8">/8 — 255.0.0.0</option>
</select>
</div>
<div class="col-md-5">
<label class="form-label">Passerelle</label>
<input type="text" id="net-gw" class="form-control font-monospace" placeholder="192.168.1.1">
</div>
<div class="col-md-6">
<label class="form-label">Serveurs DNS <span class="text-muted small">(séparés par virgule)</span></label>
<input type="text" id="net-dns" class="form-control font-monospace" placeholder="8.8.8.8,1.1.1.1">
</div>
</div>
</div>
<div id="net-alert" class="alert py-2 mb-3" style="display:none"></div>
<div class="d-flex gap-2 align-items-center">
<button class="btn btn-warning" onclick="applyNetwork()">
<i class="bi bi-hdd-network me-1"></i>Appliquer la configuration réseau
</button>
<span id="net-spinner" class="spinner-border spinner-border-sm text-warning" style="display:none"></span>
<span class="text-muted small" id="net-warn" style="display:none">
<i class="bi bi-exclamation-triangle me-1 text-warning"></i>
Si vous changez l'IP, reconnectez-vous sur la nouvelle adresse.
</span>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
loadNetworkConfig();
document.querySelectorAll('input[name="net-mode"]').forEach(function(r) {
r.addEventListener('change', function() {
document.getElementById('net-static-fields').style.display = r.value === 'static' ? '' : 'none';
document.getElementById('net-warn').style.display = r.value === 'static' ? '' : 'none';
});
});
});
function loadNetworkConfig() {
fetch('/api/network.php')
.then(r => r.json())
.then(function(d) {
document.getElementById('net-loading').style.display = 'none';
document.getElementById('net-form').style.display = '';
// IP actuelle
const ip = d.current_ip || d.static_ip || '—';
document.getElementById('net-current-ip').textContent = ip;
// Mode
const mode = d.method || 'dhcp';
document.querySelector('input[name="net-mode"][value="' + mode + '"]').checked = true;
// Champs statique
if (mode === 'static') {
document.getElementById('net-static-fields').style.display = '';
document.getElementById('net-warn').style.display = '';
}
// Pré-remplissage IP statique
const sipRaw = d.static_ip || d.current_ip || '';
const sipParts = sipRaw.split('/');
document.getElementById('net-ip').value = sipParts[0] || '';
document.getElementById('net-prefix').value = sipParts[1] || '24';
document.getElementById('net-gw').value = d.static_gw || d.current_gw || '';
document.getElementById('net-dns').value = (d.static_dns || d.current_dns || '8.8.8.8,1.1.1.1').replace(/\s/g,'');
})
.catch(function() {
document.getElementById('net-loading').innerHTML = '<span class="text-danger"><i class="bi bi-exclamation-triangle me-1"></i>Impossible de lire la configuration réseau.</span>';
});
}
function applyNetwork() {
const mode = document.querySelector('input[name="net-mode"]:checked')?.value || 'dhcp';
const body = { mode };
if (mode === 'static') {
body.ip = document.getElementById('net-ip').value.trim();
body.prefix = document.getElementById('net-prefix').value;
body.gateway = document.getElementById('net-gw').value.trim();
body.dns = document.getElementById('net-dns').value.trim();
if (!body.ip || !body.gateway) {
showNetAlert('danger', 'Veuillez remplir l\'adresse IP et la passerelle.');
return;
}
}
document.getElementById('net-spinner').style.display = '';
document.getElementById('net-alert').style.display = 'none';
fetch('/api/network.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body)
})
.then(r => r.json())
.then(function(d) {
document.getElementById('net-spinner').style.display = 'none';
if (d.success) {
let msg = d.msg || 'Configuration appliquée.';
if (d.new_ip) msg += ' — Reconnectez-vous sur <strong>' + d.new_ip + '</strong>';
showNetAlert('success', msg);
setTimeout(loadNetworkConfig, 2000);
} else {
showNetAlert('danger', d.error || 'Erreur inconnue.');
}
})
.catch(function() {
document.getElementById('net-spinner').style.display = 'none';
showNetAlert('warning', 'Pas de réponse — l\'IP a peut-être changé. Reconnectez-vous.');
});
}
function showNetAlert(type, msg) {
const el = document.getElementById('net-alert');
el.className = 'alert alert-' + type + ' py-2 mb-3';
el.innerHTML = msg;
el.style.display = '';
}
</script>
<?php html_foot(); ?>