{"id":241,"date":"2026-08-12T23:18:41","date_gmt":"2026-08-12T21:18:41","guid":{"rendered":"https:\/\/kapelski.eu\/?page_id=241"},"modified":"2026-08-13T00:55:29","modified_gmt":"2026-08-12T22:55:29","slug":"kapelheim","status":"publish","type":"page","link":"https:\/\/kapelski.eu\/","title":{"rendered":"Kapelheim"},"content":{"rendered":"\n<div class=\"wp-block-cover alignfull is-light has-custom-content-position is-position-top-center\" style=\"border-style:none;border-width:0px;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px;margin-top:0;margin-bottom:0;padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30);min-height:100vh;aspect-ratio:unset;\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" class=\"wp-block-cover__image-background wp-image-259 size-large\" alt=\"\" src=\"https:\/\/kapelski.eu\/wp-content\/uploads\/2026\/08\/m_bcg-1024x576.jpg\" style=\"object-position:87% 50%\" data-object-fit=\"cover\" data-object-position=\"87% 50%\" srcset=\"https:\/\/kapelski.eu\/wp-content\/uploads\/2026\/08\/m_bcg-1024x576.jpg 1024w, https:\/\/kapelski.eu\/wp-content\/uploads\/2026\/08\/m_bcg-300x169.jpg 300w, https:\/\/kapelski.eu\/wp-content\/uploads\/2026\/08\/m_bcg-768x432.jpg 768w, https:\/\/kapelski.eu\/wp-content\/uploads\/2026\/08\/m_bcg-1536x864.jpg 1536w, https:\/\/kapelski.eu\/wp-content\/uploads\/2026\/08\/m_bcg.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><span aria-hidden=\"true\" class=\"wp-block-cover__background has-background-dim-30 has-background-dim\" style=\"background-color:#a29f86\"><\/span><div class=\"wp-block-cover__inner-container is-layout-constrained wp-container-core-cover-is-layout-bf999c55 wp-block-cover-is-layout-constrained\">\n<div class=\"wp-block-group alignwide is-content-justification-right is-nowrap is-layout-flex wp-container-core-group-is-layout-b48010d7 wp-block-group-is-layout-flex\">    <div id=\"spc-widget\" class=\"spc-widget\">\n        <style>\n            .spc-widget { max-width: 440px; font-family: -apple-system, \"Segoe UI\", sans-serif;\n                border-radius: 14px; overflow: hidden; box-shadow: 0 4px 18px rgba(0,0,0,.12); border: 1px solid #e2e2e2; }\n            .spc-header { background: linear-gradient(135deg, #23272e, #3a4048); color: #fff; padding: 16px 20px; }\n            .spc-header h3 { margin: 0; font-size: 18px; display:flex; align-items:center; gap:8px; }\n            .spc-header h3::before { content: \"\ud83d\udda5\"; }\n            .spc-body { padding: 18px 20px; background: #fafafa; }\n            .spc-login-box input { width: 100%; padding: 9px; margin-bottom: 12px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 6px; font-size: 14px; }\n            .spc-login-box button, .spc-toggle-btn { width: 100%; padding: 10px; background: #2271b1; color: #fff; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; }\n            .spc-login-box button:hover, .spc-toggle-btn:hover { background: #135e96; }\n            .spc-error { color: #b00; font-size: 13px; margin-bottom: 10px; }\n            .spc-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; font-size: 14px; }\n            .spc-badge { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 700; }\n            .spc-badge.on { background: #dbf3d8; color: #216c1e; }\n            .spc-badge.off { background: #fbdada; color: #a11c1c; }\n            .spc-section-title { font-size: 12px; color: #888; text-transform: uppercase; letter-spacing: .04em; margin: 16px 0 8px; }\n            .spc-metric { display: flex; justify-content: space-between; font-size: 13px; padding: 4px 0; border-bottom: 1px dashed #e5e5e5; }\n            .spc-btns { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }\n            .spc-btns button { flex: 1; min-width: 110px; padding: 9px; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600; }\n            .spc-btn-on { background: #2e7d32; color: #fff; }\n            .spc-btn-off { background: #c62828; color: #fff; }\n            .spc-fan-group { display: flex; justify-content: space-between; font-size: 13px; padding: 4px 0; border-bottom: 1px dashed #e5e5e5; }\n            .spc-fan-group .spc-fan-rpms { color: #444; }\n            .spc-updated { font-size: 11px; color: #999; margin-top: 14px; text-align: right; }\n            .spc-errors { font-size: 12px; color: #a11c1c; margin-top: 10px; }\n            .spc-hidden { display: none !important; }\n            .spc-logout { font-size: 11px; color: #888; text-decoration: underline; cursor: pointer; margin-top: 10px; display: inline-block; }\n            .spc-console {\n                background: #14161a; color: #9be89b; font-family: \"SFMono-Regular\", Consolas, monospace;\n                font-size: 11px; border-radius: 8px; padding: 10px 12px; max-height: 130px; overflow-y: auto;\n                line-height: 1.6;\n            }\n            .spc-console-empty { color: #666; }\n            .spc-console-line { white-space: pre-wrap; word-break: break-word; }\n            .spc-console-line.ok { color: #9be89b; }\n            .spc-console-line.err { color: #ff8a8a; }\n        <\/style>\n\n        <div class=\"spc-header\"><h3>Panel serwera<\/h3><\/div>\n        <div class=\"spc-body\">\n\n            <div id=\"spc-gate\" class=\"\">\n                <button class=\"spc-toggle-btn\" id=\"spc-show-login\">Zaloguj si\u0119<\/button>\n                <div id=\"spc-login-box\" class=\"spc-login-box spc-hidden\" style=\"margin-top:14px;\">\n                    <div class=\"spc-error spc-hidden\" id=\"spc-login-error\"><\/div>\n                    <input type=\"text\" id=\"spc-user\" placeholder=\"Login\" autocomplete=\"username\">\n                    <input type=\"password\" id=\"spc-pass\" placeholder=\"Has\u0142o\" autocomplete=\"current-password\">\n                    <button id=\"spc-do-login\">Zaloguj<\/button>\n                <\/div>\n            <\/div>\n\n            <div id=\"spc-panel\" class=\"spc-hidden\">\n                <div class=\"spc-row\">\n                    <span>Zasilanie:<\/span>\n                    <span class=\"spc-badge\" id=\"spc-power-badge\">\u2014<\/span>\n                <\/div>\n\n                <div class=\"spc-section-title\">Metryki<\/div>\n                <div id=\"spc-metrics\"><\/div>\n\n                <div class=\"spc-errors\" id=\"spc-errors\"><\/div>\n\n                <div class=\"spc-btns\">\n                    <button class=\"spc-btn-on\" data-type=\"power\" data-value=\"on\">W\u0142\u0105cz<\/button>\n                    <button class=\"spc-btn-off\" data-type=\"power\" data-value=\"off\" data-confirm=\"Na pewno wy\u0142\u0105czy\u0107 serwer?\">Wy\u0142\u0105cz<\/button>\n                <\/div>\n\n                <div class=\"spc-updated\" id=\"spc-updated\"><\/div>\n\n                <div class=\"spc-section-title\">Konsola<\/div>\n                <div class=\"spc-console\" id=\"spc-console\">\n                    <div class=\"spc-console-empty\">\u2014 brak zdarze\u0144 \u2014<\/div>\n                <\/div>\n\n                <span class=\"spc-logout\" id=\"spc-logout\">wyloguj<\/span>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <script>\n    (function () {\n        var ajaxUrl = \"https:\\\/\\\/kapelski.eu\\\/wp-admin\\\/admin-ajax.php\";\n        var nonce = \"a11ce7bd34\";\n        var pollTimer = null;\n\n        function post(action, extra) {\n            var body = new URLSearchParams();\n            body.append('action', action);\n            body.append('nonce', nonce);\n            for (var k in (extra || {})) body.append(k, extra[k]);\n            return fetch(ajaxUrl, { method: 'POST', body: body, credentials: 'same-origin' }).then(function (r) { return r.json(); });\n        }\n\n        document.getElementById('spc-show-login').addEventListener('click', function () {\n            document.getElementById('spc-login-box').classList.toggle('spc-hidden');\n        });\n\n        document.getElementById('spc-do-login').addEventListener('click', function () {\n            var user = document.getElementById('spc-user').value;\n            var pass = document.getElementById('spc-pass').value;\n            post('spc_login', { user: user, pass: pass }).then(function (json) {\n                var errEl = document.getElementById('spc-login-error');\n                if (json.success) {\n                    document.getElementById('spc-gate').classList.add('spc-hidden');\n                    document.getElementById('spc-panel').classList.remove('spc-hidden');\n                    manualRefresh();\n                    pollTimer = setInterval(timerRefresh, 30000);\n                } else {\n                    errEl.textContent = json.message || 'B\u0142\u0105d logowania.';\n                    errEl.classList.remove('spc-hidden');\n                }\n            });\n        });\n\n        document.getElementById('spc-logout').addEventListener('click', function () {\n            document.cookie = 'spc_auth=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=\/;';\n            document.getElementById('spc-panel').classList.add('spc-hidden');\n            document.getElementById('spc-gate').classList.remove('spc-hidden');\n            if (pollTimer) clearInterval(pollTimer);\n        });\n\n        document.querySelectorAll('.spc-btns button').forEach(function (btn) {\n            btn.addEventListener('click', function () {\n                var confirmMsg = btn.getAttribute('data-confirm');\n                if (confirmMsg && !confirm(confirmMsg)) return;\n                var label = btn.textContent.trim();\n                btn.disabled = true;\n                logConsole('Wys\u0142ano: ' + label + ' \u2026', null);\n                post('spc_action', { type: btn.getAttribute('data-type'), value: btn.getAttribute('data-value') })\n                    .then(function (json) {\n                        var msg = 'Wynik: ' + label + ' \u2192 ';\n                        if (json.success) {\n                            msg += 'OK';\n                        } else {\n                            var errText = (json.result && json.result.error) ? json.result.error : (json.message || 'nieznany b\u0142\u0105d');\n                            msg += 'B\u0141\u0104D (' + errText + ')';\n                        }\n                        logConsole(msg, json.success);\n                        setTimeout(function () { manualRefresh(); btn.disabled = false; }, 2000);\n                    })\n                    .catch(function () {\n                        logConsole('Wynik: ' + label + ' \u2192 B\u0141\u0104D (brak odpowiedzi serwera)', false);\n                        btn.disabled = false;\n                    });\n            });\n        });\n\n        function logConsole(text, ok) {\n            var box = document.getElementById('spc-console');\n            var empty = box.querySelector('.spc-console-empty');\n            if (empty) empty.remove();\n            var line = document.createElement('div');\n            var cls = 'spc-console-line' + (ok === true ? ' ok' : (ok === false ? ' err' : ''));\n            line.className = cls;\n            var ts = new Date().toLocaleTimeString('pl-PL');\n            line.textContent = '[' + ts + '] ' + text;\n            box.appendChild(line);\n            box.scrollTop = box.scrollHeight;\n        }\n\n        function clearConsole() {\n            var box = document.getElementById('spc-console');\n            box.innerHTML = '<div class=\"spc-console-empty\">\u2014 brak zdarze\u0144 \u2014<\/div>';\n        }\n\n        \/\/ Grupuje wentylatory po numerze (np. \"Fan1A\"\/\"Fan1B\" -> jedna grupa \"Fan1\"\n        \/\/ z podpisanymi obrotami A\/B) \u2014 odzwierciedla to, \u017ce w R330 poszczeg\u00f3lne\n        \/\/ \"sloty\" wentylator\u00f3w mog\u0105 mie\u0107 po dwa wirniki (przedni\/tylny) raportowane osobno.\n        function groupFans(fans) {\n            var groups = {};\n            var order = [];\n            (fans || []).forEach(function (f) {\n                var m = f.name.match(\/^(.*?)(\\d+)\\s*([A-Za-z]?)$\/);\n                var key = m ? (m[1] + m[2]) : f.name;\n                var sub = m && m[3] ? m[3] : null;\n                if (!groups[key]) { groups[key] = []; order.push(key); }\n                groups[key].push({ sub: sub, rpm: f.rpm });\n            });\n            return order.map(function (key) {\n                return { key: key, items: groups[key] };\n            });\n        }\n\n        function fanGroupLabel(key, index) {\n            \/\/ Og\u00f3lny, bezpieczny opis pozycji \u2014 iDRAC zwykle numeruje wentylatory\n            \/\/ od przodu obudowy (Fan1) w stron\u0119 ty\u0142u (Fan4), ale to mo\u017ce si\u0119 r\u00f3\u017cni\u0107\n            \/\/ zale\u017cnie od dok\u0142adnej konfiguracji Twojego egzemplarza R330.\n            return 'Wentylator ' + (index + 1) + ' (' + key + ')';\n        }\n\n        function render(d) {\n            var badge = document.getElementById('spc-power-badge');\n            var isOn = d.power_state === 'On';\n            badge.textContent = isOn ? 'W\u0141\u0104CZONY' : 'WY\u0141\u0104CZONY';\n            badge.className = 'spc-badge ' + (isOn ? 'on' : 'off');\n\n            var metrics = document.getElementById('spc-metrics');\n            metrics.innerHTML = '';\n\n            function addMetric(label, value) {\n                var row = document.createElement('div');\n                row.className = 'spc-metric';\n                row.innerHTML = '<span>' + label + '<\/span><span>' + value + '<\/span>';\n                metrics.appendChild(row);\n            }\n\n            if (d.watts !== null && d.watts !== undefined) addMetric('Pob\u00f3r mocy', d.watts + ' W');\n            (d.temps || []).forEach(function (t) { addMetric(t.name, t.celsius + ' \u00b0C'); });\n\n            var fanGroups = groupFans(d.fans);\n            fanGroups.forEach(function (g, idx) {\n                var row = document.createElement('div');\n                row.className = 'spc-fan-group';\n                var rpmText = g.items.map(function (it) {\n                    return (it.sub ? it.sub + ': ' : '') + it.rpm + ' RPM';\n                }).join('  \/  ');\n                row.innerHTML = '<span>' + fanGroupLabel(g.key, idx) + '<\/span><span class=\"spc-fan-rpms\">' + rpmText + '<\/span>';\n                metrics.appendChild(row);\n            });\n\n            var errEl = document.getElementById('spc-errors');\n            errEl.innerHTML = (d.errors && d.errors.length) ? d.errors.join('<br>') : '';\n\n            document.getElementById('spc-updated').textContent = 'od\u015bwie\u017cono: ' + new Date().toLocaleTimeString('pl-PL');\n        }\n\n        function refresh() {\n            post('spc_status').then(function (json) {\n                if (json.success) render(json.data);\n            });\n        }\n\n        \/\/ Od\u015bwie\u017cenie z timera (co 30s) \u2014 czy\u015bci konsol\u0119, bo to nie jest wynik\n        \/\/ Twojej akcji, tylko rutynowe sprawdzenie statusu.\n        function timerRefresh() {\n            clearConsole();\n            refresh();\n        }\n\n        \/\/ Od\u015bwie\u017cenie po Twojej akcji (klikni\u0119cie przycisku) \u2014 konsola zostaje,\n        \/\/ \u017ceby\u015b zd\u0105\u017cy\u0142 przeczyta\u0107 wynik zanim zniknie przy kolejnym tykni\u0119ciu timera.\n        function manualRefresh() {\n            refresh();\n        }\n\n            })();\n    <\/script>\n    \n<\/div>\n\n\n\n<div class=\"wp-block-group alignwide is-layout-constrained wp-container-core-group-is-layout-52ba6d82 wp-block-group-is-layout-constrained\" style=\"border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px;min-height:171px;margin-top:0;margin-bottom:0;padding-top:0;padding-right:0;padding-bottom:0;padding-left:0\">    <div id=\"msw-widget\" class=\"msw-widget\">\n        <style>\n            .msw-widget {\n                max-width: 420px; font-family: -apple-system, \"Segoe UI\", sans-serif;\n                border-radius: 14px; overflow: hidden; box-shadow: 0 4px 18px rgba(0,0,0,.12);\n                border: 1px solid #e2e2e2;\n            }\n            .msw-header {\n                background: linear-gradient(135deg, #4c8c2b, #6bab3f);\n                color: #fff; padding: 16px 20px;\n            }\n            .msw-header h3 { margin: 0 0 4px; font-size: 18px; display:flex; align-items:center; gap:8px; }\n            .msw-header h3::before { content: \"\u26cf\"; }\n            .msw-motd { font-size: 12px; opacity: .9; margin: 0; }\n            .msw-body { padding: 18px 20px; background: #fafafa; }\n            .msw-address-row {\n                display: flex; align-items: center; gap: 8px; background: #fff;\n                border: 1px solid #ddd; border-radius: 8px; padding: 8px 10px; margin-bottom: 16px;\n            }\n            .msw-address {\n                font-family: \"SFMono-Regular\", Consolas, monospace; font-size: 14px;\n                flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color:#222;\n            }\n            .msw-copy-btn {\n                background: #4c8c2b; color: #fff; border: none; border-radius: 6px;\n                padding: 6px 12px; font-size: 12px; cursor: pointer; white-space: nowrap;\n                transition: background .15s;\n            }\n            .msw-copy-btn:hover { background: #3d7222; }\n            .msw-copy-btn.copied { background: #2271b1; }\n            .msw-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; font-size: 14px; }\n            .msw-badge {\n                display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 700;\n            }\n            .msw-badge.online { background: #dbf3d8; color: #216c1e; }\n            .msw-badge.offline { background: #fbdada; color: #a11c1c; }\n            .msw-players { margin-top: 4px; }\n            .msw-players-title { font-size: 13px; color: #666; margin: 10px 0 6px; }\n            .msw-player-chip {\n                display: inline-block; background: #eef3ea; color: #35551f; border-radius: 14px;\n                padding: 3px 10px; font-size: 12px; margin: 0 4px 4px 0;\n            }\n            .msw-updated { font-size: 11px; color: #999; margin-top: 12px; text-align: right; }\n            .msw-empty { font-size: 12px; color: #999; }\n        <\/style>\n\n        <div class=\"msw-header\">\n            <h3>Serwer Minecraft<\/h3>\n            <p class=\"msw-motd\" id=\"msw-motd\"><\/p>\n        <\/div>\n        <div class=\"msw-body\">\n            <div class=\"msw-address-row\">\n                <span class=\"msw-address\" id=\"msw-address\">kapelheim.duckdns.org<\/span>\n                <button class=\"msw-copy-btn\" id=\"msw-copy-btn\" type=\"button\">Kopiuj IP<\/button>\n            <\/div>\n\n            <div class=\"msw-row\">\n                <span>Status:<\/span>\n                <span class=\"msw-badge offline\" id=\"msw-status-badge\">\n                    OFFLINE                <\/span>\n            <\/div>\n\n            <div class=\"msw-row\">\n                <span>Gracze:<\/span>\n                <span id=\"msw-count\">0 \/ 0<\/span>\n            <\/div>\n\n            <div class=\"msw-players-title\">Lista graczy:<\/div>\n            <div class=\"msw-players\" id=\"msw-players\">\n                                    <span class=\"msw-empty\">brak graczy online<\/span>\n                            <\/div>\n\n            <div class=\"msw-updated\" id=\"msw-updated\">od\u015bwie\u017cono: teraz<\/div>\n        <\/div>\n    <\/div>\n\n    <script>\n    (function () {\n        var ajaxUrl = \"https:\\\/\\\/kapelski.eu\\\/wp-admin\\\/admin-ajax.php\";\n        var nonce = \"a0ea968775\";\n\n        var copyBtn = document.getElementById('msw-copy-btn');\n        copyBtn.addEventListener('click', function () {\n            var addr = document.getElementById('msw-address').textContent;\n            navigator.clipboard.writeText(addr).then(function () {\n                copyBtn.textContent = 'Skopiowano!';\n                copyBtn.classList.add('copied');\n                setTimeout(function () {\n                    copyBtn.textContent = 'Kopiuj IP';\n                    copyBtn.classList.remove('copied');\n                }, 1500);\n            });\n        });\n\n        function render(data) {\n            document.getElementById('msw-address').textContent = data.address || '';\n            document.getElementById('msw-motd').textContent = data.motd || '';\n\n            var badge = document.getElementById('msw-status-badge');\n            badge.textContent = data.online ? 'ONLINE' : 'OFFLINE';\n            badge.className = 'msw-badge ' + (data.online ? 'online' : 'offline');\n\n            document.getElementById('msw-count').textContent = (data.count || 0) + ' \/ ' + (data.max || 0);\n\n            var playersEl = document.getElementById('msw-players');\n            playersEl.innerHTML = '';\n            if (data.players && data.players.length) {\n                data.players.forEach(function (p) {\n                    var chip = document.createElement('span');\n                    chip.className = 'msw-player-chip';\n                    chip.textContent = p;\n                    playersEl.appendChild(chip);\n                });\n            } else {\n                var empty = document.createElement('span');\n                empty.className = 'msw-empty';\n                empty.textContent = 'brak graczy online';\n                playersEl.appendChild(empty);\n            }\n\n            document.getElementById('msw-updated').textContent = 'od\u015bwie\u017cono: ' + new Date().toLocaleTimeString('pl-PL');\n        }\n\n        function refresh() {\n            var body = new URLSearchParams();\n            body.append('action', 'msw_status');\n            body.append('nonce', nonce);\n\n            fetch(ajaxUrl, { method: 'POST', body: body, credentials: 'same-origin' })\n                .then(function (r) { return r.json(); })\n                .then(function (json) {\n                    if (json && json.success !== false) { render(json.data || json); }\n                })\n                .catch(function () { \/* cichy fail \u2014 zostaw ostatnie znane dane *\/ });\n        }\n\n        setInterval(refresh, 30000); \/\/ od\u015bwie\u017canie co 30 sekund\n    })();\n    <\/script>\n    \n<\/div>\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"blank","meta":{"footnotes":""},"class_list":["post-241","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/kapelski.eu\/index.php?rest_route=\/wp\/v2\/pages\/241","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kapelski.eu\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/kapelski.eu\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/kapelski.eu\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kapelski.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=241"}],"version-history":[{"count":12,"href":"https:\/\/kapelski.eu\/index.php?rest_route=\/wp\/v2\/pages\/241\/revisions"}],"predecessor-version":[{"id":267,"href":"https:\/\/kapelski.eu\/index.php?rest_route=\/wp\/v2\/pages\/241\/revisions\/267"}],"wp:attachment":[{"href":"https:\/\/kapelski.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=241"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}