From 8d5ef864f90e5dc3c0dacd449f7f3fb584e77dbd Mon Sep 17 00:00:00 2001 From: Jukka Lampikoski Date: Fri, 13 Mar 2026 02:40:32 +0200 Subject: [PATCH] =?UTF-8?q?Saatavuuskyselyt:=20IP-organisaatio,=20siirr?= =?UTF-8?q?=C3=A4=20API-tabiin=20+=20s=C3=A4hk=C3=B6postien=20formatointi?= =?UTF-8?q?=20+=20tikettiviestiv=C3=A4rit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Lisää IP-organisaatio/ISP-kenttä saatavuuskyselyihin (ip-api.com haku) - Siirrä saatavuuskyselyt-taulukko Asiakkaat-tabista API-asetussivulle - Korjaa rivinvaihdot ja välilyönnit Zammad-sähköpostivastauksissa (white-space:pre-wrap) - Korjaa quoted thread: plain-text viestit muunnetaan HTML:ksi oikein - Tikettiviestiketjun värit selkeämmiksi (sininen=saapuva, vihreä=lähtevä) Co-Authored-By: Claude Opus 4.6 --- api.php | 28 +++++++++++++++++++++------ db.php | 1 + index.html | 57 ++++++++++++++++++++++++------------------------------ script.js | 9 +++++---- style.css | 8 ++++---- 5 files changed, 57 insertions(+), 46 deletions(-) diff --git a/api.php b/api.php index 24459e7..d29e190 100644 --- a/api.php +++ b/api.php @@ -1227,10 +1227,21 @@ switch ($action) { if (!$exists) { $ip = getClientIp(); $hostname = @gethostbyaddr($ip) ?: ''; - if ($hostname === $ip) $hostname = ''; // gethostbyaddr palauttaa IP:n jos ei löydy + if ($hostname === $ip) $hostname = ''; + // Hae IP-osoitteen organisaatio/ISP ip-api.com:sta + $org = ''; + try { + $ipApiUrl = "http://ip-api.com/json/{$ip}?fields=org,isp,as"; + $ctx = stream_context_create(['http' => ['timeout' => 3]]); + $ipJson = @file_get_contents($ipApiUrl, false, $ctx); + if ($ipJson) { + $ipData = json_decode($ipJson, true); + $org = $ipData['org'] ?? $ipData['isp'] ?? ''; + } + } catch (\Throwable $e) { /* IP-haku ei saa kaataa API:a */ } _dbExecute( - "INSERT INTO availability_queries (company_id, osoite, postinumero, kaupunki, saatavilla, ip_address, hostname, user_agent, referer, created_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO availability_queries (company_id, osoite, postinumero, kaupunki, saatavilla, ip_address, hostname, org, user_agent, referer, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [ $matchedCompany['id'], $rawOsoite, @@ -1239,6 +1250,7 @@ switch ($action) { $found ? 1 : 0, $ip, $hostname, + $org, substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500), substr($_SERVER['HTTP_REFERER'] ?? '', 0, 500), date('Y-m-d H:i:s'), @@ -1267,7 +1279,7 @@ switch ($action) { $total = (int)_dbFetchScalar("SELECT COUNT(*) FROM availability_queries WHERE company_id IN ($placeholders)", $userCompanyIds); $params = array_merge($userCompanyIds, [$limit, $offset]); $rows = _dbFetchAll( - "SELECT aq.id, aq.company_id, c.nimi as company_nimi, aq.osoite, aq.postinumero, aq.kaupunki, aq.saatavilla, aq.ip_address, aq.hostname, aq.referer, aq.created_at + "SELECT aq.id, aq.company_id, c.nimi as company_nimi, aq.osoite, aq.postinumero, aq.kaupunki, aq.saatavilla, aq.ip_address, aq.hostname, aq.org, aq.referer, aq.created_at FROM availability_queries aq LEFT JOIN companies c ON c.id = aq.company_id WHERE aq.company_id IN ($placeholders) ORDER BY aq.created_at DESC LIMIT ? OFFSET ?", $params @@ -5398,8 +5410,8 @@ switch ($action) { $to = !empty($input['to']) ? trim($input['to']) : ($ticket['from_email'] ?? ''); $cc = !empty($input['cc']) ? trim($input['cc']) : ''; - // Muunna uusi viesti HTML:ksi - $newMsgHtml = nl2br(htmlspecialchars($body, ENT_QUOTES, 'UTF-8')); + // Muunna uusi viesti HTML:ksi (säilytä rivinvaihdot ja välilyönnit) + $newMsgHtml = '
' . htmlspecialchars($body, ENT_QUOTES, 'UTF-8') . '
'; // Rakenna viestiketju (quoted thread) vastaukseen $messages = _dbFetchAll( @@ -5411,6 +5423,10 @@ switch ($action) { $sender = $msg['from_name'] ?: $msg['from_email']; $date = date('d.m.Y H:i', strtotime($msg['timestamp'])); $msgBody = $msg['body'] ?: ''; + // Jos viesti on plain text (ei HTML-tageja), muunna HTML:ksi + if ($msgBody !== '' && strip_tags($msgBody) === $msgBody) { + $msgBody = '
' . htmlspecialchars($msgBody, ENT_QUOTES, 'UTF-8') . '
'; + } $quotedThread .= '
' . '' . htmlspecialchars($sender) . ' — ' . $date . '
' . '
' . $msgBody . '
' diff --git a/db.php b/db.php index 2764390..0fcb9ad 100644 --- a/db.php +++ b/db.php @@ -676,6 +676,7 @@ function initDatabase(): void { "ALTER TABLE tickets ADD COLUMN zammad_ticket_id INT DEFAULT NULL AFTER mailbox_id", "ALTER TABLE ticket_messages ADD COLUMN zammad_article_id INT DEFAULT NULL AFTER message_id", "ALTER TABLE availability_queries ADD COLUMN hostname VARCHAR(255) DEFAULT '' AFTER ip_address", + "ALTER TABLE availability_queries ADD COLUMN org VARCHAR(255) DEFAULT '' AFTER hostname", ]; foreach ($alters as $sql) { try { $db->query($sql); } catch (\Throwable $e) { /* sarake on jo olemassa / jo ajettu */ } diff --git a/index.html b/index.html index fd7d19d..1ccab0d 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Noxus HUB - + @@ -93,7 +93,6 @@
-
@@ -191,35 +190,6 @@
-
-
-
-
-
-

Saatavuuskyselyt

-

Nettisivujen kautta tehdyt saatavuustarkistukset

-
- -
- - - - - - - - - - - - - - -
AikaOsoitePostinumeroKaupunkiTulosIP / VerkkoLähdeYritys
-
-
-
-
@@ -1556,6 +1526,29 @@ + +

Saatavuuskyselyt

+

Nettisivujen kautta tehdyt saatavuustarkistukset

+ +
+ + + + + + + + + + + + + + + +
AikaOsoitePostinumeroKaupunkiTulosIP / VerkkoOrganisaatioLähdeYritys
+
+
@@ -2264,6 +2257,6 @@ - + diff --git a/script.js b/script.js index e983f0c..36a73d1 100644 --- a/script.js +++ b/script.js @@ -281,8 +281,6 @@ function switchToTab(target, subTab) { loadCustomers(); if (subTab === 'archive') { switchCustomerSubTab('customers-archive'); - } else if (subTab === 'availability') { - switchCustomerSubTab('customers-availability'); } else { switchCustomerSubTab('customers-list'); } @@ -2570,6 +2568,9 @@ async function loadSettings() { document.getElementById('settings-telegram-chat').value = config.telegram_chat_id || ''; } catch (e) { console.error(e); } + // Lataa saatavuuskyselyt + loadAvailabilityQueries(); + // Näytä API-sivun kortit integraatioiden perusteella try { const integs = await apiCall('integrations'); @@ -3497,7 +3498,6 @@ function switchCustomerSubTab(target) { const content = document.getElementById('subtab-' + target); if (content) content.classList.add('active'); if (target === 'customers-archive') { loadArchive(); window.location.hash = 'customers/archive'; } - else if (target === 'customers-availability') { loadAvailabilityQueries(); window.location.hash = 'customers/availability'; } else { window.location.hash = 'customers'; } } @@ -3520,7 +3520,7 @@ async function loadAvailabilityQueries(page = 0) { countEl.textContent = `Yhteensä ${data.total} kyselyä`; if (data.queries.length === 0) { - tbody.innerHTML = 'Ei vielä kyselyjä'; + tbody.innerHTML = 'Ei vielä kyselyjä'; } else { tbody.innerHTML = data.queries.map(q => { const date = q.created_at ? q.created_at.replace('T', ' ').substring(0, 16) : ''; @@ -3542,6 +3542,7 @@ async function loadAvailabilityQueries(page = 0) { ${esc(q.kaupunki)} ${badge} ${ipInfo} + ${esc(q.org || '')} ${esc(source)} ${esc(q.company_nimi || q.company_id || '')} `; diff --git a/style.css b/style.css index bcf7588..0642b4d 100644 --- a/style.css +++ b/style.css @@ -1432,13 +1432,13 @@ span.empty { } .ticket-msg-in { - background: #f0f7ff; - border-left: 3px solid #3498db; + background: #e3f0fc; + border-left: 4px solid #2980d9; } .ticket-msg-out { - background: #eafaf1; - border-left: 3px solid #2ecc71; + background: #e0f5e4; + border-left: 4px solid #27ae60; } .ticket-msg-note {