287 lines
14 KiB
PHP
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(); ?>
|