Přeskočit na obsah
Jan HřebíčekEnaro
← Studie

Modernizace + automatizace

Chata Roklinka: Modernizace a automatizace rezervačního systému

Projekt jsem převzal ve chvíli, kdy se mu původní vývojář přestal věnovat. Web chaty měl povedený design, ale rezervace byla jen formulář, který odeslal e-mail. Termíny, potvrzení i platby pak majitelé řešili ručně a pod povrchem běžel Next.js 12 z roku 2022. Postupně z něj vznikl rezervační systém, ve kterém si host termín vybere, zaplatí a v případě potřeby ho i sám zruší, aniž by provozovatel musel cokoli dělat.

Ručně → auto
rezervace, platba i storno
170
automatických testů
RolePřevzetí, modernizace, vývoj a správaStackNext.js · MongoDB · Stripe · Resend

Výzva

Rezervační formulář odeslal majitelům e-mail a tím jeho práce skončila. Každá rezervace pak znamenala práci navíc. Majitelé museli:

  • ručně ověřit, že je termín volný
  • ručně potvrdit rezervaci a domluvit platbu
  • ručně zablokovat termín v kalendáři
  • ručně termín uvolnit, když host zrušil
  • ručně spočítat, kolik mu při stornu vrátit

K tomu zastaralý stack, nasazování, které padalo, a galerie, která s každým nasazením nahrávala stovky megabajtů fotek, které web nikdy nezobrazil.

Stabilizace a rychlejší galerie

Než šlo cokoli přidávat, musel web spolehlivě jít nasadit. Optimalizace obrázků přes next/image na hostingu selhávala, nahradil jsem ji proto vlastní přípravou náhledů při buildu.

  • každá fotka ve třech velikostech: dvě pro mřížku, jedna pro prohlížeč
  • WebP se použije jen tam, kde je opravdu menší než JPEG. U 17 z 30 fotek nebyl, a prohlížeče přitom stahovaly ten větší soubor
  • otevření fotky stojí ~421 kB místo ~675 kB a rozmazaný náhled nic, protože je to už načtená miniatura z mřížky
  • originály se přesunuly mimo veřejnou složku, takže se s každým nasazením nenahrávají na CDN
  • náhledy mají roční cache, protože se mění jen s novým nasazením

Rezervace s online platbou

Host vybere v kalendáři příjezd a odjezd po půldnech, systém spočítá cenu včetně poplatku za psa a pošle ho na platební bránu Stripe. Po zaplacení dostane potvrzení on i provozovatel a termín je zablokovaný. Bez telefonu a bez přepisování do tabulky.

  • cena se počítá jen na serveru a nabídka na webu i částka k úhradě vycházejí z jediné definice, takže se nemůžou rozejít
  • termín se zablokuje už při přechodu k platbě, aby ho mezitím nekoupil někdo jiný
  • nezaplacené rezervace se po vypršení platby uvolní samy (plánovaná úloha každých 5 minut) a při zrušení platby okamžitě
  • když dvě platby za stejný termín projdou současně, druhá se automaticky vrátí
  • webhook je idempotentní: opakované doručení od Stripe nepošle dvě potvrzení ani nevytvoří dvě refundace

Bezpečnostní audit

Jakmile web začal přijímat peníze, prošel jsem celý kód jako při code review. Nejvážnější nálezy byly v administraci a v rušení rezervací a žádný z nich se nedal odhalit zvenku, dokud ho někdo nezkusil zneužít.

  • přihlášení do administrace přestavěné od základu: hashovaná hesla, podepsaná session v httpOnly cookie a postupně se prodlužující blokace při hádání hesla
  • endpoint kalendáře přijímal víc, než měl. Vstupy jsou teď striktně omezené na to, co kalendář opravdu posílá
  • odkaz na storno z e-mailu je chráněný proti podvrženým dotazům do databáze
  • kontrola konfigurace, která zastaví build dřív, než se chybné nastavení dostane do produkce

Modernizace stacku

Next.js 12 → 16, React 18 → 19, Mongoose 6 → 9 a styled-components 5 → 6. Čtyři major verze najednou na kódu, který přijímá platby. Bránou byly testy a build, ne čtení changelogů. Díky upgradu zmizela i vlastní Babel konfigurace, která celý projekt odstavovala od rychlého kompilátoru SWC.

Dva jazyky, e-maily a právní náležitosti

  • čeština a angličtina včetně platební stránky a potvrzovacích e-mailů v jazyce hosta
  • hreflang, kanonické odkazy, sitemap a náhledy pro sociální sítě. Návštěvníka nikam automaticky nepřesměrovávám, jazyk si přepne odkazem
  • automatický test hlídá, aby obě jazykové verze měly shodné texty a stejné procento refundace
  • e-maily přešly na Resend a dostaly nový vzhled, postavený kolem toho, proč je host otevírá
  • mimo produkci se veškerá pošta přesměruje na testovací adresu, takže zkušební rezervace nikdy neodejde skutečnému hostovi
  • obchodní podmínky, zásady ochrany osobních údajů, informace o cookies a souhlas při rezervaci

Rušení rezervací a refundace

Host mohl rezervaci zrušit odkazem z e-mailu, ale kolik peněz se mu vrátí, zjistil až po potvrzení. Nejnovější fáze to obrátila: host vidí konkrétní částku dřív, než se rozhodne, a provozovatel nic nepřepočítává.

  • storno podle podmínek: 100 / 90 / 70 / 50 / 0 % podle počtu dní do příjezdu
  • náhled refundace a potvrzovací krok s přesnou částkou
  • výpočet refundace na jednom místě pro celou aplikaci, dřív existoval ve čtyřech kopiích
  • ochrana proti dvojí refundaci, když host otevře odkaz dvakrát nebo storno současně spustí host i provozovatel
  • u nezaplacené rezervace se vrácení peněz vůbec neslibuje
  • test ověřuje, že storno podmínky na webu odpovídají tomu, co systém skutečně vyplatí

Administrace pro provozovatele

  • v kalendáři je vidět, které půldny jsou prodané
  • mazání celého rozsahu na dvě kliknutí
  • kalendář nikdy nepřepíše ani nesmaže už zaplacený termín
  • zobrazený režim úprav, odhlášení a přihlášení, které vydrží obnovení stránky
  • úprava kalendáře posílá jen změněné dny, ne celý kalendář

Kvalita a provoz

Web nemá testovací prostředí, každá změna jde rovnou do produkce. Proto před každým nasazením spouštím kontrolu konfigurace, lint a 170 automatických testů, které pokrývají výpočet ceny a refundací, práci s daty, přihlášení i doručování pošty. Všechny kroky a proměnné prostředí jsou zdokumentované, aby se provoz dal kdykoli předat.

Navazující spolupráce

Spolupráce pokračuje správou a dalším rozvojem. Když majitel rozšířil nabídku o svatby v lese, oslovil mě znovu a vznikl samostatný svatební web Lesní svatba Roklinka, který na chatu odkazuje kvůli ubytování. Oba weby dnes spravuji, takže změnu v provozu, třeba kontakt nebo sezónu, upravím na obou místech najednou.

Projekt zároveň ukazuje, že vývoj na míru nemusí znamenat stavět nový systém od nuly. Často přináší největší hodnotu převzetí existující aplikace, její modernizace a odstranění ruční práce, která provoz zbytečně zdržuje.

Použité technologie

  • Next.js 16
  • React 19
  • JavaScript
  • MongoDB · Mongoose
  • Stripe Checkout
  • Resend
  • styled-components
  • Framer Motion
  • sharp
  • Vitest
  • Serverless funkce

Výsledek

  • převzetí a stabilizace projektu, který původní vývojář opustil
  • rezervace, platba, potvrzení i blokování termínů bez ruční práce
  • online platby s ochranou proti dvojímu prodeji stejného termínu
  • storno s náhledem refundace a ochranou proti dvojímu vrácení peněz
  • opravené bezpečnostní slabiny v administraci a rušení rezervací
  • upgrade z Next.js 12 na 16 a z Reactu 18 na 19
  • otevření fotky v galerii o třetinu lehčí a žádné zbytečné soubory na CDN
  • čeština i angličtina, včetně plateb a e-mailů
  • 170 automatických testů jako brána před každým nasazením
  • klient se vrátil s dalším projektem: Lesní svatba Roklinka