Vlna vylepšení — poistka pri nasadení, rýchlosť importu feedov, spevnenie fakturácie
· Infraštruktúra
Konsolidovaná vlna vylepšení za obdobie 15. 7. – 24. 7. 2026. (1) Kontrola prebiehajúcich operácií pri nasadení (ec90342e) — poistka pred buildom, ktorá hľadá bežiace hromadné akcie, automatizácie a ERP prenosy, nikdy nedobehla: každá vetva UNION-u joinuje tabuľku company, takže neokvalifikované id::text padalo na „column reference is ambiguous", a po zabalení do vonkajšieho ssh volania sa rozbilo aj úvodzovkovanie. Všetky stĺpce sú teraz kvalifikované aliasom a SQL ide dnu cez heredoc; overené na produkcii, kde opravený dotaz našiel bežiaci erp_transfer_log na 60/151, ktorý pôvodný nevedel ohlásiť. Predtým mohlo nasadenie prerušiť rozbehnutú úlohu a nechať jej záznam natrvalo v stave running. (2) Horúce cesty importu feedov (283954f2, c2119a89, e2b4e6da) — FlushProducts riešil značky necachovanou cestou raz na skupinu produktov (feed s 25 000 produktmi a ~30 značkami vygeneroval ~25 000 upsertov, teraz ~30) a ide už cez cache viazanú na jedno volanie; MapValue/MapParameterName prechádzali pri každom volaní celý zoznam mapovaní spoločnosti a teraz sa vyhodnocujú cez indexy (osobitne pre dodávateľa a globálne) prestavané raz pri obnove cache; hodnoty parametrov sa zapisujú jedným viacriadkovým ON CONFLICT upsertom namiesto 30+ sekvenčných insertov na entitu, pričom obsahová brána IS DISTINCT FROM ostáva zachovaná, takže nezmenená resynchronizácia stále nezapíše nič. (3) Ohraničené hľadanie variantných vzorkovníkov (add704b0) — resolveAutoVariantRule na detaile produktu v e-shope načítal ako plné entity každý produkt spoločnosti so zhodnou hodnotou parametra a až potom filtroval a orezával v pamäti. Podmienka published/is_listed aj LIMIT maxProducts sú teraz zatlačené do agregačného dotazu (filter pred limitom, takže nepublikované riadky s vysokou zhodou už nevytlačia platné kandidátky) a rehydratácia ostáva ohraničená na tenanta. (4) Úvodný bundle administrácie (5eeb1f9b) — recharts (~100 KB gzip) sa cez zdieľaný StatCard dostával do úvodného bundlu každej obrazovky dashboardu. Jediným importérom je teraz spoločný komponent Sparkline načítaný cez next/dynamic({ssr:false}), takže knižnica končí v asynchrónnych chunkoch; zároveň sa doň zliali štyri kópie rovnakého markupu. (5) Bezpečné vydanie fakturácie aj bez Stripe (ab214468, 461dfa68) — nasadzovacia infraštruktúra pre predplatné: samostatná neškrtená nginx lokácia pre webhook, ktorá posiela ďalej surové telo požiadavky aj hlavičku stripe-signature, a všetky premenné STRIPE_SAAS_* zdokumentované a štandardne prázdne. Regresné testy pripínajú sľúbenú degradáciu — SaasStripeService sa skonštruuje aj bez konfigurácie a createPortal vráti zrozumiteľnú 400 „nie je nakonfigurované" namiesto 500 — vďaka čomu je fázované spustenie (balíčky teraz, platby kartou neskôr) bezpečné. (6) Najnižšie potrebné oprávnenia pre fakturačné funkcie (d4570427, 8619edce, migrácia 461) — record_saas_billing_event a finalize_saas_billing_event mali v search_path len public a migrácia po REVOKE FROM PUBLIC znova udelila EXECUTE práve PUBLIC-u. Obe chyby sme opravili priamo v migrácii ešte pred jej aplikovaním: kanonické public, pg_temp a EXECUTE výhradne pre rolu monokaido_app, takže testy 9 a 10 z RLS-izolačnej brány ostávajú zelené. Zapisovač obchádzajúci RLS, ktorý môže zavolať ktorákoľvek rola clustra, je presne to, na čo tá brána existuje. (7) Zadné vrátka k AI bonusu pri kupónom udelenom balíčku (02f48686) — applyPlanToCompany({grantAiBonus:false}) top-up správne preskočil, ale AuthService.register následne volá autoActivateMonokaidoAi, ktorý odvodzuje limit z aktuálneho subscription_plan_id spoločnosti, teda z balíčka, ktorý kupón práve udelil. Partnerský kupón, ktorý mal dať iba prístup k balíčku, tak rozdával aj 500 000 tokenov úrovne Pro. createOrUpdate dostal interný prepínač bonusPlanId (zámerne nie pole DTO), pri registrácii pripnutý na free; grant je monotónny a nikdy sa neodoberá, takže registrácia je jediná príležitosť trafiť to správne. (8) Helpdesk konzola — agregáty, prihlásenie a poctivé MRR (a524b91c, 76ef6cf9, d98ff8cb, migrácia 468) — len pre interný tím. getStats/getManagementSummary volali plnú SECURITY DEFINER prehľadovú funkciu nad každým tenantom len kvôli počtu (nová odľahčená sesterská funkcia, migrácia 468), ~7 nezávislých čítaní v getCompanyDetail beží paralelne, zápisy z operácií invalidujú aj kľúče štatistík a manažérskeho reportu a spoločnosť na zrušenom balíčku už nenafukuje súčty bez toho, aby sa objavila v niektorom riadku. Prihlásenie: správne heslo už neresetuje zámok z nesprávnych kódov, platná 8-hodinová refresh cookie už nevynúti opätovné heslo + MFA a zamknuté konto mimo interného tímu už nevracia odlíšiteľnú odpoveď. PAYING_PREDICATE navyše vyžaduje stripe_subscription_id IS NOT NULL, takže darované firmy sa vykazujú vo vlastnej kategórii a nenafukujú tržby. (9) CI a vlastné runnery (7fc490ee, 853fd220, d4306223, 4e90cfdf) — pool vlastných runnerov spadol na nulu po tom, čo GitHub vynútil aktualizáciu agenta v kontajneri (premenná EPHEMERAL sa zapína pri akejkoľvek neprázdnej hodnote, takže „false" znamenalo stále ephemeral); runnery sú teraz trvalé, s vypnutou automatickou aktualizáciou a obrazom pripnutým na 2.336.0. Job RLS-izolácie publikoval svoj jednorazový Postgres na hostiteľskom porte 5432 na runneri v host-network režime, kde koliduje s produkčnou databázou — a keby sa naviazal, mieril by priamo na ňu; teraz používa 55432 plus predbežnú kontrolu, ktorá odmietne bežať nad neprázdnou schémou. Jest má pripnuté 2 workery a 30 s timeout a pre-push porovnáva voči integračnej vetve.