diff --git a/api.php b/api.php index c90934d..29f36c2 100644 --- a/api.php +++ b/api.php @@ -902,6 +902,145 @@ class ImapClient { } } +// ==================== IREDMAIL CLIENT ==================== + +class IRedMailClient { + private string $baseUrl; + private string $adminEmail; + private string $adminPassword; + private ?string $cookie = null; + + public function __construct(string $baseUrl, string $adminEmail, string $adminPassword) { + $this->baseUrl = rtrim($baseUrl, '/'); + if (!preg_match('#^https?://#i', $this->baseUrl)) { + $this->baseUrl = 'https://' . $this->baseUrl; + } + $this->adminEmail = $adminEmail; + $this->adminPassword = $adminPassword; + } + + public function login(): void { + $ch = curl_init($this->baseUrl . '/api/login'); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query([ + 'username' => $this->adminEmail, + 'password' => $this->adminPassword, + ]), + CURLOPT_TIMEOUT => 10, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_HEADER => true, + CURLOPT_SSL_VERIFYPEER => false, + ]); + $response = curl_exec($ch); + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $headers = substr($response, 0, $headerSize); + curl_close($ch); + + if ($httpCode >= 400) { + throw new \RuntimeException('iRedMail kirjautuminen epäonnistui (HTTP ' . $httpCode . ')'); + } + + // Parse Set-Cookie header + if (preg_match('/Set-Cookie:\s*([^;\r\n]+)/i', $headers, $m)) { + $this->cookie = $m[1]; + } else { + throw new \RuntimeException('iRedMail: session-cookie puuttuu vastauksesta'); + } + } + + public function request(string $method, string $endpoint, ?array $data = null, bool $retried = false): array { + if (!$this->cookie) $this->login(); + + $url = $this->baseUrl . '/api/' . ltrim($endpoint, '/'); + $ch = curl_init($url); + $opts = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 15, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_HTTPHEADER => ['Cookie: ' . $this->cookie], + CURLOPT_SSL_VERIFYPEER => false, + ]; + + if ($method === 'POST') { + $opts[CURLOPT_POST] = true; + if ($data) $opts[CURLOPT_POSTFIELDS] = http_build_query($data); + } elseif ($method === 'PUT') { + $opts[CURLOPT_CUSTOMREQUEST] = 'PUT'; + if ($data) $opts[CURLOPT_POSTFIELDS] = http_build_query($data); + } elseif ($method === 'DELETE') { + $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; + } + + curl_setopt_array($ch, $opts); + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + // Retry login on 401 + if ($httpCode === 401 && !$retried) { + $this->cookie = null; + return $this->request($method, $endpoint, $data, true); + } + + $result = json_decode($response, true) ?: []; + if ($httpCode >= 400 || (isset($result['_success']) && !$result['_success'])) { + throw new \RuntimeException('iRedMail API: ' . ($result['_msg'] ?? "HTTP $httpCode")); + } + return $result; + } + + public function getDomains(): array { return $this->request('GET', 'domains'); } + public function createDomain(string $domain, array $opts = []): array { + return $this->request('POST', 'domain/' . urlencode($domain), $opts); + } + public function deleteDomain(string $domain): array { + return $this->request('DELETE', 'domain/' . urlencode($domain)); + } + public function getUsers(string $domain): array { + return $this->request('GET', 'users/' . urlencode($domain)); + } + public function getUser(string $email): array { + return $this->request('GET', 'user/' . urlencode($email)); + } + public function createUser(string $email, string $password, array $opts = []): array { + $opts['password'] = $password; + return $this->request('POST', 'user/' . urlencode($email), $opts); + } + public function updateUser(string $email, array $opts): array { + return $this->request('PUT', 'user/' . urlencode($email), $opts); + } + public function deleteUser(string $email): array { + return $this->request('DELETE', 'user/' . urlencode($email)); + } + public function getAliases(string $domain): array { + return $this->request('GET', 'aliases/' . urlencode($domain)); + } + public function createAlias(string $alias, array $opts = []): array { + return $this->request('POST', 'alias/' . urlencode($alias), $opts); + } + public function deleteAlias(string $alias): array { + return $this->request('DELETE', 'alias/' . urlencode($alias)); + } + public function testConnection(): array { + $domains = $this->getDomains(); + return ['ok' => true, 'domains' => count($domains['_data'] ?? [])]; + } +} + +function getIRedMailClient(): IRedMailClient { + $config = dbLoadConfig(); + $url = $config['iredmail_api_url'] ?? ''; + $email = $config['iredmail_admin_email'] ?? ''; + $pw = $config['iredmail_admin_password'] ?? ''; + if (!$url || !$email || !$pw) { + throw new \RuntimeException('iRedMail-asetukset puuttuvat. Aseta ensin URL, admin-sähköposti ja salasana.'); + } + return new IRedMailClient($url, $email, $pw); +} + // ==================== TICKETS HELPER ==================== function sendTelegramAlert(string $companyId, array $ticket): void { @@ -5872,6 +6011,210 @@ switch ($action) { } break; + // ==================== IREDMAIL HALLINTA ==================== + + case 'iredmail_config': + requireSuperAdmin(); + $config = dbLoadConfig(); + echo json_encode([ + 'url' => $config['iredmail_api_url'] ?? '', + 'admin_email' => $config['iredmail_admin_email'] ?? '', + 'has_password' => !empty($config['iredmail_admin_password']), + ]); + break; + + case 'iredmail_config_save': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $config = dbLoadConfig(); + if (isset($input['url'])) $config['iredmail_api_url'] = trim($input['url']); + if (isset($input['admin_email'])) $config['iredmail_admin_email'] = trim($input['admin_email']); + if (!empty($input['password'])) $config['iredmail_admin_password'] = $input['password']; + dbSaveConfig($config); + echo json_encode(['ok' => true]); + break; + + case 'iredmail_test': + requireSuperAdmin(); + if ($method !== 'POST') break; + try { + $client = getIRedMailClient(); + $result = $client->testConnection(); + echo json_encode($result); + } catch (\Throwable $e) { + http_response_code(400); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_domains': + requireSuperAdmin(); + try { + $client = getIRedMailClient(); + $result = $client->getDomains(); + echo json_encode($result['_data'] ?? []); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_domain_create': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $domain = trim($input['domain'] ?? ''); + if (!$domain) { http_response_code(400); echo json_encode(['error' => 'Domain puuttuu']); break; } + try { + $client = getIRedMailClient(); + $opts = []; + if (!empty($input['cn'])) $opts['cn'] = $input['cn']; + if (isset($input['quota'])) $opts['quota'] = intval($input['quota']); + $result = $client->createDomain($domain, $opts); + echo json_encode(['ok' => true]); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_domain_delete': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $domain = trim($input['domain'] ?? ''); + if (!$domain) { http_response_code(400); echo json_encode(['error' => 'Domain puuttuu']); break; } + try { + $client = getIRedMailClient(); + $client->deleteDomain($domain); + echo json_encode(['ok' => true]); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_users': + requireSuperAdmin(); + $domain = trim($_GET['domain'] ?? ''); + if (!$domain) { http_response_code(400); echo json_encode(['error' => 'Domain puuttuu']); break; } + try { + $client = getIRedMailClient(); + $result = $client->getUsers($domain); + echo json_encode($result['_data'] ?? []); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_user_create': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $email = trim($input['email'] ?? ''); + $password = $input['password'] ?? ''; + if (!$email || !$password) { http_response_code(400); echo json_encode(['error' => 'Sähköposti ja salasana vaaditaan']); break; } + try { + $client = getIRedMailClient(); + $opts = []; + if (!empty($input['cn'])) $opts['cn'] = $input['cn']; + if (isset($input['mailQuota'])) $opts['mailQuota'] = intval($input['mailQuota']); + $client->createUser($email, $password, $opts); + echo json_encode(['ok' => true]); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_user_update': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $email = trim($input['email'] ?? ''); + if (!$email) { http_response_code(400); echo json_encode(['error' => 'Sähköposti puuttuu']); break; } + try { + $client = getIRedMailClient(); + $opts = []; + if (!empty($input['password'])) $opts['password'] = $input['password']; + if (!empty($input['cn'])) $opts['cn'] = $input['cn']; + if (isset($input['mailQuota'])) $opts['mailQuota'] = intval($input['mailQuota']); + if (isset($input['accountStatus'])) $opts['accountStatus'] = $input['accountStatus']; + $client->updateUser($email, $opts); + echo json_encode(['ok' => true]); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_user_delete': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $email = trim($input['email'] ?? ''); + if (!$email) { http_response_code(400); echo json_encode(['error' => 'Sähköposti puuttuu']); break; } + try { + $client = getIRedMailClient(); + $client->deleteUser($email); + echo json_encode(['ok' => true]); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_aliases': + requireSuperAdmin(); + $domain = trim($_GET['domain'] ?? ''); + if (!$domain) { http_response_code(400); echo json_encode(['error' => 'Domain puuttuu']); break; } + try { + $client = getIRedMailClient(); + $result = $client->getAliases($domain); + echo json_encode($result['_data'] ?? []); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_alias_create': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $alias = trim($input['alias'] ?? ''); + if (!$alias) { http_response_code(400); echo json_encode(['error' => 'Alias puuttuu']); break; } + try { + $client = getIRedMailClient(); + $opts = []; + if (!empty($input['cn'])) $opts['cn'] = $input['cn']; + if (!empty($input['members'])) $opts['accessPolicy'] = 'membersonly'; + $client->createAlias($alias, $opts); + echo json_encode(['ok' => true]); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + + case 'iredmail_alias_delete': + requireSuperAdmin(); + if ($method !== 'POST') break; + $input = json_decode(file_get_contents('php://input'), true); + $alias = trim($input['alias'] ?? ''); + if (!$alias) { http_response_code(400); echo json_encode(['error' => 'Alias puuttuu']); break; } + try { + $client = getIRedMailClient(); + $client->deleteAlias($alias); + echo json_encode(['ok' => true]); + } catch (\Throwable $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } + break; + default: http_response_code(404); echo json_encode(['error' => 'Tuntematon toiminto']); diff --git a/index.html b/index.html index 09ae15e..ffb704c 100644 --- a/index.html +++ b/index.html @@ -89,6 +89,7 @@ + @@ -1509,6 +1510,238 @@ + +
+
+ +
+ +
+
+
+

Sähköpostinhallinta (iRedMail)

+ +
+ +
+ Yhteyttä ei ole määritetty +
+ + +
+
+

Domainit

+ +
+
+ + + + + + + + + + + +
DomainTilejäAliaksiaKiintiö (MB)Toiminnot
+ +
+
+ + + +
+
+
+ + + + + + + + + + + +
diff --git a/script.js b/script.js index 6973bdf..b21b4f0 100644 --- a/script.js +++ b/script.js @@ -329,6 +329,10 @@ function switchToTab(target, subTab, extra) { if (target === 'users') loadUsers(); if (target === 'settings') loadSettings(); if (target === 'companies') loadCompaniesTab(); + if (target === 'hallinta') { + const hallintaSubMap = { email: 'hallinta-email' }; + switchHallintaSubTab(hallintaSubMap[subTab] || 'hallinta-email'); + } } document.querySelectorAll('.tab').forEach(tab => { @@ -6739,6 +6743,9 @@ function applyModules(modules, hasIntegrations) { } } }); + // Hallinta-tabi: aina näkyvä superadmineille, ei moduuliriippuvuutta + const hallintaTab = document.getElementById('tab-hallinta'); + if (hallintaTab) hallintaTab.style.display = isSuperAdmin ? '' : 'none'; // Jos aktiivinen tabi on piilotettu → vaihda ensimmäiseen näkyvään const activeTab = document.querySelector('.tab.active'); if (activeTab && activeTab.style.display === 'none') { @@ -6800,6 +6807,326 @@ async function loadBranding() { } } +// ==================== HALLINTA: IREDMAIL SÄHKÖPOSTI ==================== + +let iredmailCurrentDomain = ''; + +function switchHallintaSubTab(target) { + document.querySelectorAll('#hallinta-sub-tab-bar .sub-tab').forEach(t => t.classList.remove('active')); + document.querySelectorAll('#tab-content-hallinta > .sub-tab-content').forEach(c => c.classList.remove('active')); + const btn = document.querySelector(`[data-hallinta-subtab="${target}"]`); + if (btn) btn.classList.add('active'); + const content = document.getElementById('subtab-' + target); + if (content) content.classList.add('active'); + if (target === 'hallinta-email') loadIRedMailDomains(); + window.location.hash = 'hallinta/' + target.replace('hallinta-', ''); +} + +document.querySelectorAll('#hallinta-sub-tab-bar .sub-tab').forEach(btn => { + btn.addEventListener('click', () => switchHallintaSubTab(btn.dataset.hallintaSubtab)); +}); + +// --- Domainit --- + +async function loadIRedMailDomains() { + const tbody = document.getElementById('iredmail-domain-tbody'); + const noData = document.getElementById('no-iredmail-domains'); + const statusEl = document.getElementById('iredmail-status-text'); + try { + const domains = await apiCall('iredmail_domains'); + if (!domains || domains.length === 0) { + tbody.innerHTML = ''; + noData.style.display = ''; + statusEl.innerHTML = '⚠ Yhteys OK, mutta ei domaineja'; + statusEl.parentElement.style.background = '#fff3cd'; + return; + } + noData.style.display = 'none'; + statusEl.innerHTML = '✓ Yhteys OK — ' + domains.length + ' domainia'; + statusEl.parentElement.style.background = '#d4edda'; + renderIRedMailDomains(domains); + } catch (e) { + tbody.innerHTML = ''; + noData.style.display = ''; + statusEl.innerHTML = '✗ ' + (e.message || 'Yhteysvirhe'); + statusEl.parentElement.style.background = '#f8d7da'; + } +} + +function renderIRedMailDomains(domains) { + const tbody = document.getElementById('iredmail-domain-tbody'); + tbody.innerHTML = domains.map(d => { + const domain = d.domain || d.primaryDomain || d.domainName || (typeof d === 'string' ? d : JSON.stringify(d)); + const users = d.mailboxes || d.numberOfUsers || d.aliases_count || '–'; + const aliases = d.aliases || d.numberOfAliases || '–'; + const quota = d.maxQuotaSize || d.quota || '0'; + return ` + ${esc(domain)} + ${esc(String(users))} + ${esc(String(aliases))} + ${esc(String(quota))} + + + + `; + }).join(''); +} + +async function openIRedMailDomain(domain) { + iredmailCurrentDomain = domain; + document.getElementById('iredmail-domain-section').style.display = 'none'; + document.getElementById('iredmail-users-section').style.display = ''; + document.getElementById('iredmail-current-domain').textContent = domain; + document.getElementById('iredmail-user-domain-label').textContent = domain; + document.getElementById('iredmail-alias-domain-label').textContent = domain; + await Promise.all([loadIRedMailUsers(domain), loadIRedMailAliases(domain)]); +} + +document.getElementById('iredmail-back-to-domains').addEventListener('click', (e) => { + e.preventDefault(); + iredmailCurrentDomain = ''; + document.getElementById('iredmail-users-section').style.display = 'none'; + document.getElementById('iredmail-domain-section').style.display = ''; +}); + +async function deleteIRedMailDomain(domain) { + if (!confirm('Poistetaanko domain ' + domain + ' ja KAIKKI sen tilit? Tätä ei voi perua!')) return; + try { + await apiCall('iredmail_domain_delete', 'POST', { domain }); + await loadIRedMailDomains(); + } catch (e) { alert(e.message); } +} + +// Lisää domain +document.getElementById('btn-iredmail-add-domain').addEventListener('click', () => { + document.getElementById('iredmail-domain-name').value = ''; + document.getElementById('iredmail-domain-cn').value = ''; + document.getElementById('iredmail-domain-quota').value = '0'; + document.getElementById('iredmail-domain-modal').style.display = 'flex'; +}); + +document.getElementById('btn-iredmail-domain-save').addEventListener('click', async () => { + const domain = document.getElementById('iredmail-domain-name').value.trim(); + if (!domain) { alert('Domain puuttuu'); return; } + try { + await apiCall('iredmail_domain_create', 'POST', { + domain, + cn: document.getElementById('iredmail-domain-cn').value.trim(), + quota: parseInt(document.getElementById('iredmail-domain-quota').value) || 0, + }); + document.getElementById('iredmail-domain-modal').style.display = 'none'; + await loadIRedMailDomains(); + } catch (e) { alert(e.message); } +}); + +// --- Käyttäjät --- + +async function loadIRedMailUsers(domain) { + const tbody = document.getElementById('iredmail-user-tbody'); + try { + const users = await apiCall('iredmail_users&domain=' + encodeURIComponent(domain)); + renderIRedMailUsers(users || []); + } catch (e) { + tbody.innerHTML = `${esc(e.message)}`; + } +} + +function renderIRedMailUsers(users) { + const tbody = document.getElementById('iredmail-user-tbody'); + if (users.length === 0) { + tbody.innerHTML = 'Ei tilejä'; + return; + } + tbody.innerHTML = users.map(u => { + const email = u.mail || u.email || u.username || ''; + const name = u.cn || u.name || u.displayName || ''; + const quota = u.mailQuota || u.quota || '0'; + const status = u.accountStatus === 'disabled' ? 'Ei käytössä' : 'Aktiivinen'; + return ` + ${esc(email)} + ${esc(name)} + ${esc(String(quota))} + ${status} + + + + + `; + }).join(''); +} + +// Haku +document.getElementById('iredmail-user-search').addEventListener('input', function() { + const q = this.value.toLowerCase(); + document.querySelectorAll('#iredmail-user-tbody tr[data-email]').forEach(row => { + const email = row.dataset.email || ''; + const name = row.children[1]?.textContent?.toLowerCase() || ''; + row.style.display = (email.includes(q) || name.includes(q)) ? '' : 'none'; + }); +}); + +// Lisää tili +document.getElementById('btn-iredmail-add-user').addEventListener('click', () => { + document.getElementById('iredmail-user-modal-title').textContent = 'Lisää tili'; + document.getElementById('iredmail-user-local').value = ''; + document.getElementById('iredmail-user-cn').value = ''; + document.getElementById('iredmail-user-password').value = ''; + document.getElementById('iredmail-user-quota').value = '1024'; + document.getElementById('iredmail-user-modal').style.display = 'flex'; +}); + +document.getElementById('btn-iredmail-user-save').addEventListener('click', async () => { + const local = document.getElementById('iredmail-user-local').value.trim(); + const password = document.getElementById('iredmail-user-password').value; + if (!local) { alert('Käyttäjänimi puuttuu'); return; } + if (!password || password.length < 8) { alert('Salasana vähintään 8 merkkiä'); return; } + const email = local + '@' + iredmailCurrentDomain; + try { + await apiCall('iredmail_user_create', 'POST', { + email, + password, + cn: document.getElementById('iredmail-user-cn').value.trim(), + mailQuota: parseInt(document.getElementById('iredmail-user-quota').value) || 0, + }); + document.getElementById('iredmail-user-modal').style.display = 'none'; + await loadIRedMailUsers(iredmailCurrentDomain); + } catch (e) { alert(e.message); } +}); + +async function deleteIRedMailUser(email) { + if (!confirm('Poistetaanko tili ' + email + '? Kaikki viestit menetetään!')) return; + try { + await apiCall('iredmail_user_delete', 'POST', { email }); + await loadIRedMailUsers(iredmailCurrentDomain); + } catch (e) { alert(e.message); } +} + +// Salasanan vaihto +function showIRedMailPasswordModal(email) { + document.getElementById('iredmail-pw-email-label').textContent = email; + document.getElementById('iredmail-pw-new').value = ''; + document.getElementById('iredmail-pw-confirm').value = ''; + document.getElementById('iredmail-password-modal').style.display = 'flex'; + document.getElementById('iredmail-password-modal').dataset.email = email; +} + +document.getElementById('btn-iredmail-pw-save').addEventListener('click', async () => { + const pw1 = document.getElementById('iredmail-pw-new').value; + const pw2 = document.getElementById('iredmail-pw-confirm').value; + if (!pw1 || pw1.length < 8) { alert('Salasana vähintään 8 merkkiä'); return; } + if (pw1 !== pw2) { alert('Salasanat eivät täsmää'); return; } + const email = document.getElementById('iredmail-password-modal').dataset.email; + try { + await apiCall('iredmail_user_update', 'POST', { email, password: pw1 }); + document.getElementById('iredmail-password-modal').style.display = 'none'; + alert('Salasana vaihdettu!'); + } catch (e) { alert(e.message); } +}); + +// --- Aliakset --- + +async function loadIRedMailAliases(domain) { + const tbody = document.getElementById('iredmail-alias-tbody'); + try { + const aliases = await apiCall('iredmail_aliases&domain=' + encodeURIComponent(domain)); + renderIRedMailAliases(aliases || []); + } catch (e) { + tbody.innerHTML = `${esc(e.message)}`; + } +} + +function renderIRedMailAliases(aliases) { + const tbody = document.getElementById('iredmail-alias-tbody'); + if (aliases.length === 0) { + tbody.innerHTML = 'Ei aliaksia'; + return; + } + tbody.innerHTML = aliases.map(a => { + const alias = a.mail || a.address || a.alias || ''; + const members = a.members || a.goto || a.accessPolicy || ''; + const membersStr = Array.isArray(members) ? members.join(', ') : String(members); + return ` + ${esc(alias)} + ${esc(membersStr)} + + + + `; + }).join(''); +} + +document.getElementById('btn-iredmail-add-alias').addEventListener('click', () => { + document.getElementById('iredmail-alias-local').value = ''; + document.getElementById('iredmail-alias-members').value = ''; + document.getElementById('iredmail-alias-modal').style.display = 'flex'; +}); + +document.getElementById('btn-iredmail-alias-save').addEventListener('click', async () => { + const local = document.getElementById('iredmail-alias-local').value.trim(); + if (!local) { alert('Alias puuttuu'); return; } + const alias = local + '@' + iredmailCurrentDomain; + const members = document.getElementById('iredmail-alias-members').value.trim(); + try { + await apiCall('iredmail_alias_create', 'POST', { + alias, + cn: alias, + members: members, + }); + document.getElementById('iredmail-alias-modal').style.display = 'none'; + await loadIRedMailAliases(iredmailCurrentDomain); + } catch (e) { alert(e.message); } +}); + +async function deleteIRedMailAlias(alias) { + if (!confirm('Poistetaanko alias ' + alias + '?')) return; + try { + await apiCall('iredmail_alias_delete', 'POST', { alias }); + await loadIRedMailAliases(iredmailCurrentDomain); + } catch (e) { alert(e.message); } +} + +// --- iRedMail asetukset --- + +document.getElementById('btn-iredmail-settings').addEventListener('click', async () => { + try { + const cfg = await apiCall('iredmail_config'); + document.getElementById('iredmail-cfg-url').value = cfg.url || ''; + document.getElementById('iredmail-cfg-email').value = cfg.admin_email || ''; + document.getElementById('iredmail-cfg-password').value = ''; + document.getElementById('iredmail-cfg-password').placeholder = cfg.has_password ? 'Asetettu — jätä tyhjäksi jos ei muuteta' : 'Anna salasana'; + document.getElementById('iredmail-cfg-status').innerHTML = ''; + } catch (e) { + document.getElementById('iredmail-cfg-status').innerHTML = '' + esc(e.message) + ''; + } + document.getElementById('iredmail-config-modal').style.display = 'flex'; +}); + +document.getElementById('btn-iredmail-cfg-save').addEventListener('click', async () => { + const data = { + url: document.getElementById('iredmail-cfg-url').value.trim(), + admin_email: document.getElementById('iredmail-cfg-email').value.trim(), + }; + const pw = document.getElementById('iredmail-cfg-password').value; + if (pw) data.password = pw; + try { + await apiCall('iredmail_config_save', 'POST', data); + document.getElementById('iredmail-cfg-status').innerHTML = 'Tallennettu!'; + } catch (e) { + document.getElementById('iredmail-cfg-status').innerHTML = '' + esc(e.message) + ''; + } +}); + +document.getElementById('btn-iredmail-cfg-test').addEventListener('click', async () => { + const statusEl = document.getElementById('iredmail-cfg-status'); + statusEl.innerHTML = 'Testataan...'; + try { + const result = await apiCall('iredmail_test', 'POST', {}); + statusEl.innerHTML = '✓ Yhteys OK! ' + (result.domains || 0) + ' domainia.'; + } catch (e) { + statusEl.innerHTML = '✗ ' + esc(e.message) + ''; + } +}); + // Init — branding ensin, sitten auth (luo session-cookien), sitten captcha (käyttää samaa sessiota) loadBranding().then(async () => { await checkAuth(); diff --git a/style.css b/style.css index 0173145..048f8c4 100644 --- a/style.css +++ b/style.css @@ -2431,3 +2431,8 @@ span.empty { .integration-config-card { border-left: 3px solid var(--primary-color); } + +/* iRedMail */ +#iredmail-status { border-radius: 8px; transition: background 0.3s; } +.btn-danger { background: #e74c3c; color: #fff; border: none; border-radius: 4px; cursor: pointer; padding: 4px 10px; font-size: 0.82rem; } +.btn-danger:hover { background: #c0392b; }