<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Zápisky – Jakub Kubík</title><description>Technické texty Jakuba Kubíka k projektom: weby pre malé organizácie.</description><link>https://jakubkubik.com/zapisky/</link><language>sk</language><lastBuildDate>Sat, 03 Oct 2026 00:00:00 GMT</lastBuildDate><atom:link href="https://jakubkubik.com/rss.xml" rel="self" type="application/rss+xml"/><item><title>Ako vznikol web OZ Juliána: redizajn bez WordPressu a s vlastnou správou aktualít</title><link>https://jakubkubik.com/zapisky/ozjuliana-bez-wordpressu/</link><guid isPermaLink="true">https://jakubkubik.com/zapisky/ozjuliana-bez-wordpressu/</guid><description>Prečo web malého združenia beží bez WordPressu a databázy, ako funguje správa aktualít s bezpečným prihlásením z telefónu a ako sa overoval redizajn.</description><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;V administrácii hostingu je tlačidlo „nastaviť DNS“. Kto naň klikne, ticho prepíše záznamy, podľa ktorých
prehliadač hľadá server domény: zrazu mieria priamo na server hostingu, mimo CDN (siete serverov, ktorá
web doručuje a chráni). Web beží ďalej, takže si zmenu nikto nevšimne. Na ozjuliana.sk sa presne toto
stalo a zmenu neodhalil človek, ale skript, ktorý po každom nasadení živý web iba číta. Takých poistiek má web
malého združenia viac, než by sa čakalo, a tento zápisok je o tom, prečo tam sú.&lt;/p&gt;
&lt;p&gt;Občianske združenie Juliána celoročne zbiera oblečenie, obuv, hračky a potreby do domácnosti
a rozváža ich ľuďom v núdzi. Web &lt;a href=&quot;https://ozjuliana.sk/&quot;&gt;ozjuliana.sk&lt;/a&gt; preň dobrovoľnícky robím a spravujem
od apríla 2026. Zadanie znie jednoducho: povedať, čo združenie robí a ako sa dá pomôcť, a umožniť človeku
bez technického zázemia pridať z telefónu správu o zbierke aj s fotkami. Čísla v skratke sú
v &lt;a href=&quot;https://jakubkubik.com/projekty/ozjuliana/&quot;&gt;prípadovej štúdii&lt;/a&gt;; tu je dlhší príbeh.&lt;/p&gt;
&lt;h2 id=&quot;preco-nie-wordpress-a-preco-subory-json-bez-databazy&quot;&gt;Prečo nie WordPress a prečo súbory JSON bez databázy&lt;/h2&gt;
&lt;p&gt;Web beží na lacnom zdieľanom webhostingu za CDN. Na serveri je iba PHP: žiadne SSH (vzdialený príkazový
riadok), žiadny Git, nasadzuje sa cez FTPS (prenos súborov šifrovaným spojením). WordPress by tam bežal,
ale priniesol by so sebou pravidelné aktualizácie jadra a pluginov, databázu a nekonečné pokusy
o prihlásenie na všeobecne známej adrese. Združenie pritom pridá článok s fotkami raz za pár týždňov.
Pre takú potrebu sa neoplatí udržiavať celý redakčný systém.&lt;/p&gt;
&lt;p&gt;Výsledkom je vlastné PHP bez frameworku a bez Composera. Verejné stránky sú šablóny, ktoré fungujú
aj bez JavaScriptu. Tri malé skripty len pridávajú animácie, lištu so súhlasom, úvodnú clonu s logom
a prehliadač fotiek.&lt;/p&gt;
&lt;p&gt;Databáza nie je ani pre články. Každý článok je jeden súbor JSON (textový formát pre štruktúrované údaje),
fotky ležia vedľa v priečinku a pri nahratí ich zmenší knižnica GD. Aj SQLite by sa dala použiť, lenže
pri desiatkach článkov nič nezrýchli a súbory majú dve praktické výhody: celý obsah webu sa zálohuje
skopírovaním dvoch priečinkov a v núdzi sa dá článok opraviť cez FTP v obyčajnom textovom editore.&lt;/p&gt;
&lt;p&gt;Zápis ide vždy do dočasného súboru, ktorý sa až potom premenuje na cieľový, takže prerušené uloženie
nenechá napoly zapísaný článok.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &quot;slug&quot;: &quot;zbierka-satstva-2026&quot;,
    &quot;title&quot;: &quot;Zbierka šatstva 2026&quot;,
    &quot;status&quot;: &quot;published&quot;,
    &quot;published_at&quot;: &quot;2026-09-18T12:00:00+02:00&quot;,
    &quot;text&quot;: &quot;Ďakujeme všetkým, ktorí prispeli.\n\nZbierka pokračuje každý piatok.&quot;,
    &quot;cover&quot;: { &quot;big&quot;: &quot;uploads/…-big.jpg&quot;, &quot;thumb&quot;: &quot;uploads/…-thumb.jpg&quot;, &quot;alt&quot;: &quot;Triedenie darovaného oblečenia&quot; },
    &quot;gallery&quot;: []
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Článok je vymyslený, skutočné súbory majú navyše čas vytvorenia a poslednej úpravy. Obsah zo servera
každý večer stiahne záloha, ktorá cez FTPS iba číta a drží 30 denných snímok. Pri chybe príde e‑mail.&lt;/p&gt;
&lt;h2 id=&quot;dvojfaktorove-prihlasenie-v-cistom-php&quot;&gt;Dvojfaktorové prihlásenie v čistom PHP&lt;/h2&gt;
&lt;p&gt;Administrácia článkov je jediné miesto, kde sa dá obsah webu zmeniť, preto má povinné dvojfaktorové
prihlásenie: heslo a šesťmiestny kód z aplikácie Google Authenticator. Bez druhého faktora účet ani nevznikne.&lt;/p&gt;
&lt;p&gt;TOTP, teda jednorazový kód odvodený od času (norma RFC 6238), je prekvapivo malý algoritmus: HMAC
(kryptografický odtlačok) z tajného kľúča a poradového čísla 30-sekundového okna, z výsledku sa vyberie
šesť číslic. Na hostingu bez Composera je jednoduchšie napísať ho v čistom PHP zo štandardných funkcií,
než ručne nahrávať knižnicu so závislosťami. Implementácia sa overuje proti testovacím hodnotám priamo
z normy a testovacia sada bez závislostí beží pred každým nasadením.&lt;/p&gt;
&lt;p&gt;Okolo toho je bežná výbava: heslá uložené ako odtlačok bcrypt, ochranný token proti podvrhnutému
odoslaniu (CSRF) v každom formulári, session s obmedzenou platnosťou, počítadlo neúspešných
pokusov a prvý účet, ktorý sa nedá založiť bez prístupu k serveru. Konkrétne nastavenia tu zámerne
nie sú; útočníkovi by pomohli viac než čitateľovi.&lt;/p&gt;
&lt;h2 id=&quot;administracia-pre-telefon-a-cloveka-bez-technickeho-zazemia&quot;&gt;Administrácia pre telefón a človeka bez technického zázemia&lt;/h2&gt;
&lt;p&gt;Obsah sa spravuje takmer výlučne z telefónu. Administrácia je preto navrhnutá od šírky 320 px: väčšie
tlačidlá a zaškrtávacie polia, dotykové ciele aspoň 44 px a vstupné polia s písmom aspoň 16 px, aby iPhone
pri ťuknutí stránku nepribližoval. Funguje bez JavaScriptu, takže ju nerozbije ani starší prehliadač.&lt;/p&gt;
&lt;p&gt;Formulár má len to, čo treba: nadpis, dátum, text, úvodnú fotku, galériu, popisy fotiek a prepínač medzi
konceptom a zverejnením. Text je obyčajný text, prázdny riadok znamená nový odsek. Žiadny editor
s tlačidlami, ktorý by do článku vkladal skryté HTML. Fotky z telefónu sa otočia podľa údajov EXIF,
ktoré do nich zapíše fotoaparát, zmenšia a uložia bez metadát. Pri audite sa overilo všetkých 84 fotiek
na živom webe a ani jedna nemá GPS súradnice.&lt;/p&gt;
&lt;p&gt;Jedno rozhodnutie vzišlo priamo z toho, kto web obsluhuje. Prehliadač, v ktorom sa už niekto úspešne
prihlásil, má vlastné počítadlo pokusov. Cudzie pokusy o prihlásenie tak môžu zablokovať neznáme
zariadenia, nie však telefón, z ktorého sa obsah naozaj pridáva.&lt;/p&gt;
&lt;p&gt;K administrácii patrí aj krátky návod v slovenčine. Najužitočnejšia rada v ňom nie je technická: prvá veta
článku má povedať kedy, kde, čo a koľko, lebo z nej vzniká úryvok na kartách aj popis pre vyhľadávače.&lt;/p&gt;
&lt;h2 id=&quot;lista-so-suhlasom-podla-zakona-452-2021&quot;&gt;Lišta so súhlasom podľa zákona 452/2021&lt;/h2&gt;
&lt;p&gt;Zákon o elektronických komunikáciách v § 109 ods. 8 vyžaduje súhlas s ukladaním údajov v zariadení
návštevníka, pokiaľ nie sú nevyhnutné na fungovanie služby. Web je postavený tak, aby bez súhlasu
nepotreboval nič také: verejné stránky nenastavujú žiadnu cookie ani nespúšťajú session a písma sa
načítavajú z vlastného servera, nie z Google Fonts.&lt;/p&gt;
&lt;p&gt;Google Analytics sa načíta až po kliknutí na „Prijať“. „Prijať“ a „Odmietnuť“ sú na prvej vrstve rovnocenné
tlačidlá, voľba sa dá kedykoľvek zmeniť odkazom v pätičke a odvolanie zmaže analytické cookies. Každá voľba
sa zapíše na server ako doklad o súhlase: náhodný identifikátor, čas, verzia a voľba, bez IP adresy.
Výnimkou je vložená mapa Google v kontakte, ktorá sa načíta hneď; stránka o ochrane osobných údajov
to otvorene uvádza.&lt;/p&gt;
&lt;h2 id=&quot;redizajn-ktory-sa-da-zmerat&quot;&gt;Redizajn, ktorý sa dá zmerať&lt;/h2&gt;
&lt;p&gt;V septembri prišiel redizajn s pracovným názvom „Pokojná elegancia“. Predchádzal mu audit starej verzie
a výsledky boli triezve: 29 rôznych veľkostí písma, hranice vstupných polí s kontrastom 1,24 : 1, štyri
nálezy nástroja axe na automatickú kontrolu prístupnosti a navigácia, ktorá na niektorých šírkach
telefónu o pár pixelov pretekala.&lt;/p&gt;
&lt;p&gt;Práca mala dve fázy. Prvá bola refaktoring s jediným pravidlom: žiadna viditeľná zmena. Spoločná šablóna,
jeden modul kontaktného formulára, farby a veľkosti ako premenné.&lt;/p&gt;
&lt;p&gt;Po každom commite sa nová verzia porovnala so starou: HTML výstupy 41 scenárov bajt po bajte na PHP 8.4 aj 8.5 a screenshoty pixel po pixeli. Na konci fázy boli výstupy zhodné, okrem jednej vopred schválenej
opravy, a 450 screenshotov (15 stránok × 10 šírok × 3 režimy: bežný, bez JavaScriptu a so zníženým
pohybom) malo rozdiel 0 pixelov. Celá sada bežala bez administrátorských práv: PHP vo WebAssembly
a Chrome bez okna, ovládaný cez Playwright.&lt;/p&gt;
&lt;p&gt;Až potom nasledoval samotný redizajn. Každá zamýšľaná odchýlka sa zapísala vopred a porovnanie ju
normalizovalo; všetko ostatné muselo ostať zhodné. Takto sa nedá omylom zmeniť stránka, ktorá sa meniť
nemala.&lt;/p&gt;
&lt;p&gt;Výsledok: typografická škála s 12 krokmi (audit pred redizajnom napočítal 29 hodnôt), hranice polí s kontrastom nad 3 : 1, nula porušení
podľa axe (pravidlá prístupnosti WCAG 2, úrovne A a AA) v 30 behoch na 10 stránkach a žiadne vodorovné
pretečenie v rozsahu 320–1440 px, v najrizikovejších pásmach kontrolované po jednom pixeli. Sekcia
o poslaní združenia sa na mobile skrátila z približne 1 000 na 450 px a logo v hlavičke telefónu narástlo
z 20 × 20 na 36 × 32 px, takže je konečne čitateľné.&lt;/p&gt;
&lt;h2 id=&quot;co-sa-pokazilo-a-ako-sa-to-opravilo&quot;&gt;Čo sa pokazilo a ako sa to opravilo&lt;/h2&gt;
&lt;p&gt;Audit pred redizajnom našiel v pôvodnej administrácii chyby, ktoré bežné klikanie neodhalí. Apostrof
v nadpise rozbil potvrdzovacie okno pri mazaní, takže článok sa zmazal bez opýtania. Neplatný znak UTF-8 v mene účtu viedol k zápisu prázdneho súboru s účtami. Na PHP 8.5 navyše zastaraná funkcia na prácu
s obrázkami vypísala po uložení článku varovanie a presmerovanie sa nevykonalo. Všetky tri chyby boli opravené
a overené ešte pred spustením.&lt;/p&gt;
&lt;p&gt;Hneď po spustení prišiel bezpečnostný audit živého webu v dvoch kolách, druhé z nich s dvoma nezávislými
útočnými revíziami kódu. Priniesol 38 zistení, ale cestu do administrácie bez prihlásenia nenašlo ani jedno
kolo. Najpoučnejšie zistenie: zo 16 súbežných pokusov so zlým heslom sa nezablokoval ani jeden, lebo pokus sa
započítal až po kontrole hesla. Dnes sa pokus započíta pod zámkom ešte pred ňou. Opravy kódu išli
v piatich pull requestoch v ten istý deň.&lt;/p&gt;
&lt;p&gt;Posledná chyba, tá s tlačidlom z úvodu, nebola v kóde. Doména po jednom kliknutí mierila priamo na server
hostingu a web fungoval ďalej, len bez CDN a jej ochrany. Prezradil to skript, ktorý po každom nasadení
kontroluje živý web iba čítaním: neprešla mu kontrola certifikátu. Záznamy sa vrátili hneď nasledujúce
ráno. Bez toho skriptu mohol web takto bežať celé mesiace a nikto by si to nevšimol.&lt;/p&gt;
&lt;h2 id=&quot;co-si-z-toho-beriem&quot;&gt;Čo si z toho beriem&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Malé združenie nepotrebuje redakčný systém, potrebuje jeden dobrý formulár. Pri desiatkach článkov mu
stačia aj súbory bez databázy, ak je zápis atomický a záloha automatická.&lt;/li&gt;
&lt;li&gt;Návrh administrácie začína otázkou, kto ju obsluhuje a na akom zariadení. Od toho sa odvíja aj
bezpečnosť, napríklad dôveryhodný prehliadač s vlastným počítadlom.&lt;/li&gt;
&lt;li&gt;Refaktoring bez merania stojí na dojme. Bajtová a pixelová parita mení vetu „myslím, že sa nič nezmenilo“
na dôkaz.&lt;/li&gt;
&lt;li&gt;Audit sa oplatí dvakrát: na starom kóde pred zmenami a na živom webe po spustení. Každý z nich
našiel niečo iné.&lt;/li&gt;
&lt;li&gt;Kontrola nasadenia, ktorá živý web iba číta, chytí aj chyby mimo kódu.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tlačidlo „nastaviť DNS“ je v administrácii hostingu dodnes. Skript, ktorý na to pri najbližšom
nasadení príde, tiež.&lt;/p&gt;
</content:encoded><category>Weby pre malé organizácie</category></item></channel></rss>