Add Hallinta module with iRedMail email management

New "Hallinta" main tab (superadmin only) with "Sähköposti" sub-tab for
managing email via iRedAdmin-Pro REST API. Features:
- IRedMailClient PHP class with cookie-based session auth + auto-retry
- Domain CRUD (list, create, delete)
- Mailbox CRUD (list, create, delete, password change)
- Alias CRUD (list, create, delete)
- Configuration modal (API URL, admin credentials, connection test)
- Search/filter for mailboxes
- 13 new API endpoints, all requireSuperAdmin()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 19:58:10 +02:00
parent b3d8b7e067
commit 6ea62b075f
4 changed files with 908 additions and 0 deletions

View File

@@ -89,6 +89,7 @@
<button class="tab" data-tab="documents">Dokumentit</button>
<button class="tab" data-tab="netadmin">NetAdmin</button>
<button class="tab" data-tab="changelog">Muutosloki</button>
<button class="tab" data-tab="hallinta" id="tab-hallinta" style="display:none">Hallinta</button>
<button class="tab" data-tab="settings" id="tab-settings" style="display:none">API</button>
</div>
@@ -1509,6 +1510,238 @@
</div>
</div>
<!-- Tab: Hallinta (vain superadmin) -->
<div class="tab-content" id="tab-content-hallinta">
<div class="sub-tab-bar" id="hallinta-sub-tab-bar">
<button class="sub-tab active" data-hallinta-subtab="hallinta-email">Sähköposti</button>
</div>
<div id="subtab-hallinta-email" class="sub-tab-content active">
<div class="main-container">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
<h3 style="color:var(--primary-dark);margin:0;">Sähköpostinhallinta (iRedMail)</h3>
<button class="btn-secondary" id="btn-iredmail-settings">⚙ Asetukset</button>
</div>
<div id="iredmail-status" class="stat-card" style="margin-bottom:1rem;padding:0.75rem 1rem;font-size:0.9rem;">
<span id="iredmail-status-text">Yhteyttä ei ole määritetty</span>
</div>
<!-- Domain-listaus -->
<div id="iredmail-domain-section">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem;">
<h4 style="margin:0;color:var(--primary-dark);">Domainit</h4>
<button class="btn-primary" id="btn-iredmail-add-domain">+ Lisää domain</button>
</div>
<div class="table-card">
<table>
<thead>
<tr>
<th>Domain</th>
<th>Tilejä</th>
<th>Aliaksia</th>
<th>Kiintiö (MB)</th>
<th style="width:100px;">Toiminnot</th>
</tr>
</thead>
<tbody id="iredmail-domain-tbody"></tbody>
</table>
<div id="no-iredmail-domains" style="text-align:center;padding:2rem;color:#aaa;display:none;">
Ei domaineja. Määritä ensin iRedMail-yhteys asetuksista.
</div>
</div>
</div>
<!-- Käyttäjät (näkyy kun domain valittu) -->
<div id="iredmail-users-section" style="display:none;margin-top:1.5rem;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem;flex-wrap:wrap;gap:0.5rem;">
<h4 style="margin:0;color:var(--primary-dark);">
<a href="#" id="iredmail-back-to-domains" style="text-decoration:none;color:var(--primary-color);">← Domainit</a>
/ <span id="iredmail-current-domain"></span> — Tilit
</h4>
<div style="display:flex;gap:0.5rem;">
<input type="text" id="iredmail-user-search" placeholder="Hae tilejä..." style="width:200px;">
<button class="btn-primary" id="btn-iredmail-add-user">+ Lisää tili</button>
</div>
</div>
<div class="table-card">
<table>
<thead>
<tr>
<th>Sähköposti</th>
<th>Nimi</th>
<th>Kiintiö (MB)</th>
<th>Tila</th>
<th style="width:160px;">Toiminnot</th>
</tr>
</thead>
<tbody id="iredmail-user-tbody"></tbody>
</table>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin:1.5rem 0 0.5rem;">
<h4 style="margin:0;color:var(--primary-dark);">Aliakset</h4>
<button class="btn-primary" id="btn-iredmail-add-alias">+ Lisää alias</button>
</div>
<div class="table-card">
<table>
<thead>
<tr>
<th>Alias</th>
<th>Kohde(t)</th>
<th style="width:80px;">Toiminnot</th>
</tr>
</thead>
<tbody id="iredmail-alias-tbody"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Modaalit: iRedMail -->
<div class="modal" id="iredmail-config-modal" style="display:none;">
<div class="modal-content" style="max-width:500px;">
<div class="modal-header">
<h3>iRedMail-asetukset</h3>
<button class="modal-close" onclick="document.getElementById('iredmail-config-modal').style.display='none'">&times;</button>
</div>
<div class="form-grid" style="max-width:100%;">
<div class="form-group full-width">
<label>API URL</label>
<input type="text" id="iredmail-cfg-url" placeholder="https://mail.example.com/iredadmin">
</div>
<div class="form-group full-width">
<label>Admin-sähköposti</label>
<input type="text" id="iredmail-cfg-email" placeholder="postmaster@example.com">
</div>
<div class="form-group full-width">
<label>Salasana</label>
<input type="password" id="iredmail-cfg-password" placeholder="Jätä tyhjäksi jos ei muuteta">
</div>
</div>
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
<button class="btn-primary" id="btn-iredmail-cfg-save">Tallenna</button>
<button class="btn-secondary" id="btn-iredmail-cfg-test">Testaa yhteyttä</button>
</div>
<div id="iredmail-cfg-status" style="margin-top:0.5rem;font-size:0.85rem;"></div>
</div>
</div>
<div class="modal" id="iredmail-domain-modal" style="display:none;">
<div class="modal-content" style="max-width:450px;">
<div class="modal-header">
<h3>Lisää domain</h3>
<button class="modal-close" onclick="document.getElementById('iredmail-domain-modal').style.display='none'">&times;</button>
</div>
<div class="form-grid" style="max-width:100%;">
<div class="form-group full-width">
<label>Domain *</label>
<input type="text" id="iredmail-domain-name" placeholder="esim. yritys.fi">
</div>
<div class="form-group full-width">
<label>Kuvaus</label>
<input type="text" id="iredmail-domain-cn" placeholder="Yrityksen nimi">
</div>
<div class="form-group full-width">
<label>Kiintiö (MB, 0 = rajaton)</label>
<input type="number" id="iredmail-domain-quota" value="0" min="0">
</div>
</div>
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
<button class="btn-primary" id="btn-iredmail-domain-save">Lisää</button>
<button class="btn-secondary" onclick="document.getElementById('iredmail-domain-modal').style.display='none'">Peruuta</button>
</div>
</div>
</div>
<div class="modal" id="iredmail-user-modal" style="display:none;">
<div class="modal-content" style="max-width:450px;">
<div class="modal-header">
<h3 id="iredmail-user-modal-title">Lisää tili</h3>
<button class="modal-close" onclick="document.getElementById('iredmail-user-modal').style.display='none'">&times;</button>
</div>
<div class="form-grid" style="max-width:100%;">
<div class="form-group full-width" id="iredmail-user-email-group">
<label>Sähköpostiosoite *</label>
<div style="display:flex;gap:0.3rem;align-items:center;">
<input type="text" id="iredmail-user-local" placeholder="kayttaja" style="flex:1;">
<span>@</span>
<span id="iredmail-user-domain-label" style="font-weight:600;"></span>
</div>
</div>
<div class="form-group full-width">
<label>Nimi</label>
<input type="text" id="iredmail-user-cn" placeholder="Etunimi Sukunimi">
</div>
<div class="form-group full-width">
<label>Salasana *</label>
<input type="password" id="iredmail-user-password" placeholder="Vähintään 8 merkkiä">
</div>
<div class="form-group full-width">
<label>Kiintiö (MB, 0 = rajaton)</label>
<input type="number" id="iredmail-user-quota" value="1024" min="0">
</div>
</div>
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
<button class="btn-primary" id="btn-iredmail-user-save">Tallenna</button>
<button class="btn-secondary" onclick="document.getElementById('iredmail-user-modal').style.display='none'">Peruuta</button>
</div>
</div>
</div>
<div class="modal" id="iredmail-password-modal" style="display:none;">
<div class="modal-content" style="max-width:400px;">
<div class="modal-header">
<h3>Vaihda salasana</h3>
<button class="modal-close" onclick="document.getElementById('iredmail-password-modal').style.display='none'">&times;</button>
</div>
<p style="color:#666;font-size:0.9rem;margin-bottom:1rem;" id="iredmail-pw-email-label"></p>
<div class="form-grid" style="max-width:100%;">
<div class="form-group full-width">
<label>Uusi salasana *</label>
<input type="password" id="iredmail-pw-new" placeholder="Vähintään 8 merkkiä">
</div>
<div class="form-group full-width">
<label>Vahvista salasana *</label>
<input type="password" id="iredmail-pw-confirm" placeholder="Sama salasana uudelleen">
</div>
</div>
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
<button class="btn-primary" id="btn-iredmail-pw-save">Vaihda</button>
<button class="btn-secondary" onclick="document.getElementById('iredmail-password-modal').style.display='none'">Peruuta</button>
</div>
</div>
</div>
<div class="modal" id="iredmail-alias-modal" style="display:none;">
<div class="modal-content" style="max-width:450px;">
<div class="modal-header">
<h3>Lisää alias</h3>
<button class="modal-close" onclick="document.getElementById('iredmail-alias-modal').style.display='none'">&times;</button>
</div>
<div class="form-grid" style="max-width:100%;">
<div class="form-group full-width">
<label>Alias-osoite *</label>
<div style="display:flex;gap:0.3rem;align-items:center;">
<input type="text" id="iredmail-alias-local" placeholder="info" style="flex:1;">
<span>@</span>
<span id="iredmail-alias-domain-label" style="font-weight:600;"></span>
</div>
</div>
<div class="form-group full-width">
<label>Kohdeosoitteet (yksi per rivi)</label>
<textarea id="iredmail-alias-members" rows="4" placeholder="user1@example.com&#10;user2@example.com"></textarea>
</div>
</div>
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
<button class="btn-primary" id="btn-iredmail-alias-save">Tallenna</button>
<button class="btn-secondary" onclick="document.getElementById('iredmail-alias-modal').style.display='none'">Peruuta</button>
</div>
</div>
</div>
<!-- Tab: Asetukset (vain admin) -->
<div class="tab-content" id="tab-content-settings">
<div class="main-container">