Záchrana · 10 min čítania
Aplikácia z Lovable alebo Bolt funguje v náhľade, ale v produkcii padá: príčiny a riešenia
Väčšina aplikácií postavených s AI, ktoré fungujú v náhľade a v produkcii zlyhajú, sa kazí z tej istej hŕstky dôvodov: premenné prostredia zapečené pri builde, presmerovania prihlásenia stále mieriace na náhľad, chýbajúca autorizácia v databáze, webhooky, ktoré nie sú idempotentné, CORS a žiadny monitoring chýb. Každý z nich má konkrétnu a overiteľnú opravu.
Autor BacklooPublikované
Hlavné body
- Náhľadové prostredie problémy skrýva: jeden používateľ, testovacie kľúče, požiadavky z rovnakého originu, zhovievavé nastavenia prihlásenia a žiadna reálna prevádzka.
- Klientske premenné prostredia (`VITE_*`, `NEXT_PUBLIC_*`) sa zapečú do bundlu pri builde — ich zmena na hostingu znamená nový build a všetko, čo obsahujú, je verejné.
- Autorizácia patrí do databázy alebo na server. Skrytie tlačidla nie je kontrola prístupu; Row Level Security alebo kontroly na serveri áno.
- Platobné webhooky musia overovať podpis a byť idempotentné, pretože poskytovatelia opakujú doručenie a posielajú udalosti viackrát a v inom poradí.
- Pred spustením potrebujete monitoring chýb, otestovanú obnovu zo zálohy, limity požiadaviek na drahých endpointoch a migrácie vo verziovacom systéme.
Prečo aplikácia postavená s AI funguje v náhľade, ale v produkcii padá?
Pretože náhľad je oveľa prívetivejšie prostredie ako produkcia. V náhľade ste zvyčajne jediný používateľ, bežíte na testovacích kľúčoch, požiadavky prichádzajú z rovnakého originu, presmerovania prihlásenia mieria na URL náhľadu a nič sa zatiaľ neopakovalo, nesúperilo o zdroje ani nebolo zneužité. Produkcia zmení všetko naraz — a kód nikdy nebol písaný s ohľadom na ňu.
AI nástroje na programovanie optimalizujú to, čo vidíte: obrazovka funguje, keď sa ňou preklikáte. Oveľa slabšie sú v neviditeľných častiach — čo sa stane pri druhom doručení webhooku, čo vráti vaše API neprihlásenej požiadavke, čo obsahuje bundle po builde. To nie je dôvod prestať ich používať. Je to dôvod tieto časti zámerne skontrolovať skôr, než prídu skutoční používatelia a skutočné peniaze.
45 %
Nižšie sú typy zlyhaní, ktoré kontrolujeme ako prvé, keď nám niekto odovzdá projekt z Lovable, Bolt, v0 alebo Cursoru, ktorý „včera ešte fungoval“. Sú zoradené zhruba podľa toho, ako často spôsobia výpadok alebo únik dát, nie podľa toho, aké sú zaujímavé.
Premenné prostredia a tajné kľúče: čas buildu vs. čas behu
Najčastejšia produkčná chyba je premenná prostredia, ktorá chýba, je nesprávna alebo sa číta v nesprávnom čase. Frontendové frameworky vkladajú klientske premenné do JavaScriptového bundlu pri builde aplikácie, nie pri jej behu. Ak na hostingu nastavíte alebo zmeníte VITE_SUPABASE_URL či NEXT_PUBLIC_API_URL až po builde, nasadený kód obsahuje starú hodnotu, kým aplikáciu znova nenasadíte.
- Premenné s prefixom sú verejné. Všetko vo
VITE_*,NEXT_PUBLIC_*aleboEXPO_PUBLIC_*skončí v bundli v prehliadači, kde si to môže prečítať ktokoľvek. Pri anon kľúči Supabase alebo publishable kľúči Stripe je to v poriadku. Priservice_rolekľúči Supabase, tajnom kľúči Stripe alebo kľúči OpenAI je to únik. - Premenné bez prefixu existujú len na serveri. Ak klientsky kód číta
process.env.STRIPE_SECRET_KEY, v produkcii dostaneundefined— a „oprava“ od AI často spočíva v pridaní verejného prefixu, čím tajný kľúč unikne. - Náhľad a produkcia potrebujú oddelené hodnoty. Testovacie kľúče v produkcii znamenajú, že platby potichu nerobia nič; ostré kľúče v náhľade znamenajú, že vaše testy strhávajú peniaze zo skutočných kariet.
- Tajné kľúče commitnuté do gitu zostávajú v histórii gitu. Zmazať riadok nestačí. Kľúč najprv vymeňte (rotujte) a až potom ho odstráňte.
Rýchla kontrola: zbuildujte aplikáciu lokálne a vo výstupe vyhľadajte všetko, čo vyzerá ako tajný kľúč.
npm run build
# Vite zapisuje výstup do dist/, Next.js do .next/static
grep -rEo "(sk_live|sk_test|service_role|sb_secret)[A-Za-z0-9_.-]{0,12}" dist .next/static 2>/dev/null
grep -rEo "eyJ[A-Za-z0-9_-]{20,}" dist .next/static 2>/dev/null | head
# Druhý riadok hľadá JWT. Každý nález dekódujte a skontrolujte claim "role":
# "anon" je v bundli prehliadača očakávaný; "service_role" je únik.
# Tieto slová môže obsahovať aj kód knižníc, takže každý nález preverte a nepanikárte.Okrajové prípady prihlasovania, ktoré sa objavia až na reálnej doméne
Prihlasovanie sa v produkcii zvyčajne pokazí preto, lebo poskytovateľ autentifikácie si stále myslí, že vaša aplikácia beží na localhost alebo na URL náhľadu. Odkazy na potvrdenie e-mailu, odkazy na obnovu hesla aj OAuth callbacky sa skladajú z nakonfigurovanej URL stránky a zoznamu povolených presmerovaní. Ak na ňom vaša produkčná doména nie je, používatelia skončia v náhľade, na localhoste alebo na chybovej stránke.
- Site URL u poskytovateľa autentifikácie (napr. Supabase Auth → URL Configuration) nastavená na produkčnú doménu.
- Zoznam povolených presmerovaní obsahuje produkčnú doménu a každú variantu s
wwwaj bez neho, ktorú naozaj používate — a žiadny wildcard širší, než potrebujete. - OAuth aplikácie (Google, GitHub, Apple) majú zaregistrovanú produkčnú callback URL a ich súhlasové obrazovky sú zverejnené, nie v testovacom režime.
- E-mailové šablóny používajú produkčnú doménu a skutočnú odosielaciu doménu s nastaveným SPF a DKIM, inak e-maily na obnovu hesla skončia v spame.
- Otestované obnovenie relácie: nechajte kartu otvorenú dlhšie, než je platnosť prístupového tokenu, a overte, že ďalšia akcia stále funguje.
- Odhlásenie otestované na druhom zariadení a chránené stránky overujú reláciu na serveri, nie iba v React efekte.
Posledná položka je dôležitejšia, než sa zdá. Veľa vygenerovaných aplikácií „chráni“ stránku tak, že v prehliadači presmeruje, keď nie je prihlásený používateľ. Požiadavka na dáta stránky však aj tak odíde a ktokoľvek, kto zavolá API priamo, dáta dostane. Čím sa dostávame k väčšiemu problému.
Autorizácia: kto smie čítať a zapisovať ktoré riadky
Autorizácia zlyháva potichu, a preto je najnebezpečnejšou položkou tohto zoznamu. Pri Supabase alebo Firebase komunikuje prehliadač s databázou priamo pomocou verejného kľúča. Jediné, čo bráni používateľovi A čítať dáta používateľa B, sú pravidlá samotnej databázy — Row Level Security v Supabase, Security Rules vo Firebase. Ak chýbajú alebo sú napísané ako „povoliť všetko“, každý riadok si prečíta ktokoľvek, kto otvorí kartu Network.
Nie je to teória. Chýbajúce RLS odhalilo dáta vo viac ako 170 aplikáciách postavených v Lovable a dostalo označenie CVE-2025-48757 (analýza). Napísali sme samostatný návod na úrovni SQL: checklist Supabase Row Level Security pre aplikácie postavené s AI. V skratke:
- Zapnite RLS na každej tabuľke vo vystavenej schéme a potom napíšte jednu politiku na každú operáciu (select, insert, update, delete).
- Nikdy nepošlite kľúč
service_role/ secret do klienta; RLS úplne obchádza. - Roly ukladajte do claimov, ktoré kontroluje server (
app_metadata), nie do metadát, ktoré môže upravovať používateľ. - Testujte ako anonymný používateľ a ako druhý používateľ, nielen sami za seba.
Platby: webhooky musia byť overené a idempotentné
Platby sa v produkcii kazia preto, lebo ideálny scenár z náhľadu — kliknúť na zaplatiť, presmerovať na stránku úspechu, označiť objednávku ako zaplatenú — nie je spôsob, akým platobné brány fungujú. Presmerovanie na úspech sa dá preskočiť, zavrieť alebo sfalšovať. Zdrojom pravdy je webhook a webhooky sa doručujú aspoň raz, môžu prísť v inom poradí a opakujú sa, keď je váš endpoint pomalý alebo vráti chybu.
Z toho vyplývajú tri pravidlá. Overte podpis pomocou surového tela požiadavky. Spracovanie urobte idempotentné, aby opakované doručenie nič nezmenilo. Rýchlo vráťte 2xx a pomalú prácu presuňte do fronty. Tu je minimálny route handler v Next.js pre Stripe, ktorý robí všetky tri veci:
// app/api/stripe/webhook/route.ts (Node.js runtime)
import Stripe from "stripe";
import { createClient } from "@supabase/supabase-js";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// klient iba pre server: tajný kľúč nikdy neopustí server
const db = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SECRET_KEY!);
export async function POST(req: Request) {
const body = await req.text(); // surové telo, nie req.json(): potrebné pre podpis
const signature = req.headers.get("stripe-signature") ?? "";
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
} catch {
return new Response("invalid signature", { status: 400 });
}
// stripe_events.id je primárny kľúč, takže druhé doručenie sa nepodarí vložiť
const { error } = await db.from("stripe_events").insert({ id: event.id, type: event.type });
if (error?.code === "23505") return new Response("already processed", { status: 200 });
if (error) return new Response("storage error", { status: 500 });
try {
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
await db.from("orders").update({ status: "paid" }).eq("checkout_session_id", session.id);
}
} catch (err) {
// uvoľnite záznam, aby ho opakovaný pokus zo Stripe mohol spracovať znova
await db.from("stripe_events").delete().eq("id", event.id);
throw err;
}
return new Response("ok", { status: 200 });
}Ľudia sa často pomýlia ešte v dvoch veciach. Podpisový kľúč webhooku je iný pre každý endpoint a každý režim, takže produkčný endpoint potrebuje vlastný STRIPE_WEBHOOK_SECRET. A keďže udalosti môžu prísť v inom poradí, stav aktualizujte podľa objektu, ktorý ste dostali (alebo si ho znova načítajte cez API), nie na základe predpokladu, že created vždy príde pred updated.
Migrácie databázy a zálohy
Ak vaša schéma existuje len ako klikanie v administrácii a SQL od AI vložené do editora, nedokážete ju spoľahlivo znovu vytvoriť, skontrolovať ani vrátiť späť. Dajte schému do migračných súborov vo verziovacom systéme a aplikujte ich rovnakým spôsobom na každé prostredie.
- Aktuálnu produkčnú schému raz stiahnite do migrácií (pri Supabase:
supabase db pull), commitnite ju a každú ďalšiu zmenu urobte ako nový migračný súbor. - Migrácie aplikujte najprv na staging projekt a až potom na produkciu. Nikdy nedovoľte AI nástroju spúšťať DDL priamo nad produkčnou databázou.
- Uprednostnite aditívne zmeny: pridajte stĺpec, doplňte dáta, prepnite kód a starý stĺpec odstráňte až v ďalšom vydaní.
- Poznajte svoju politiku záloh: ako často sa robia, ako dlho sa uchovávajú a či máte v pláne zapnutú obnovu k ľubovoľnému bodu v čase (point-in-time recovery).
- Urobte jednu obnovu do testovacieho projektu. Záloha, ktorú ste nikdy neobnovili, je nádej, nie záloha.
Monitoring chýb: zistite to skôr, než vám to povedia používatelia
Bez monitoringu chýb sa o produkčných chybách dozviete od zákazníkov, zvyčajne s niekoľkodňovým oneskorením a bez stack trace. Pridajte nástroj na sledovanie chýb (Sentry alebo podobný) do frontendu aj do všetkých serverových či edge funkcií, nahrajte source mapy, aby stack trace ukazoval na skutočné riadky, a posielajte upozornenia tam, kde ich číta človek.
Okolo rizikových ciest — registrácia, platobné webhooky, nahrávanie súborov, volania AI — pridajte štruktúrované logy s ID požiadavky, podľa ktorého sa dá vyhľadávať. A nastavte jednoduchú kontrolu dostupnosti na domovskú stránku a na jednu API route vyžadujúcu prihlásenie, aby ste si všimli výpadok databázy, aj keď statická stránka beží.
Limity požiadaviek a CORS
Limity požiadaviek
Každý endpoint, ktorý vás stojí peniaze alebo sa dotýka prihlasovania, potrebuje pred spustením limit požiadaviek (rate limit). Najzrejmejšie sú AI endpointy: nechránená route, ktorá preposiela požiadavky na API modelu, vám cez noc dokáže narobiť vysoký účet. Obmedzte počet požiadaviek na používateľa aj na IP adresu, obmedzte veľkosť požiadavky a ako poslednú poistku nastavte v administrácii poskytovateľa modelu pevný mesačný limit výdavkov. To isté urobte pre registráciu, prihlásenie, obnovu hesla a všetko, čo posiela e-maily alebo SMS.
CORS
Chyby CORS sa často objavia až v produkcii, pretože náhľad servíroval frontend aj API z rovnakého originu. Riešením je odpovedať na preflight požiadavku prehliadača OPTIONS a vrátiť explicitný zoznam povolených originov — nie všade nastaviť Access-Control-Allow-Origin: *. Pamätajte, že CORS je pravidlo prehliadača, nie zabezpečenie: nezabráni curl ani skriptu volať vaše API, takže autorizácia sa aj tak musí diať na serveri.
// Supabase Edge Function (Deno): explicitný zoznam povolených originov + preflight
const ALLOWED = new Set(["https://app.example.com", "https://www.example.com"]);
Deno.serve(async (req) => {
const origin = req.headers.get("origin") ?? "";
const cors: Record<string, string> = {
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
"Access-Control-Allow-Methods": "POST, OPTIONS",
Vary: "Origin",
};
// neznáme originy nedostanú hlavičku Allow-Origin, takže prehliadač odpoveď zablokuje
if (ALLOWED.has(origin)) cors["Access-Control-Allow-Origin"] = origin;
if (req.method === "OPTIONS") return new Response(null, { status: 204, headers: cors });
// ...overte JWT používateľa a vykonajte prácu...
return new Response(JSON.stringify({ ok: true }), {
headers: { ...cors, "Content-Type": "application/json" },
});
});Checklist pred spustením aplikácie z vibecodingu
Prejdite si tento zoznam skôr, než na aplikáciu postavenú s AI pustíte produkčnú prevádzku. Každú položku overíte za pár minút až hodinu; žiadna z nich nevyžaduje prepis.
- Produkčný build neobsahuje tajné kľúče (prehľadajte bundle; dekódujte všetky JWT).
- Oddelené hodnoty premenných pre náhľad a produkciu; klientske premenné po zmene nanovo zbuildované.
- Všetky commitnuté tajné kľúče vymenené, nielen zmazané.
- Site URL autentifikácie, zoznam povolených presmerovaní a OAuth callbacky mieria na produkčnú doménu.
- Obnova hesla, potvrdenie e-mailu a odhlásenie otestované od začiatku do konca v produkcii.
- RLS zapnuté na každej vystavenej tabuľke s politikami pre jednotlivé operácie; otestované ako anon aj ako druhý používateľ.
- Chránené dáta sa overujú na serveri alebo v databáze, nie sú iba skryté v UI.
- Platobný webhook overuje podpisy, je idempotentný a používa produkčný podpisový kľúč.
- Schéma v migračných súboroch; zmeny sa aplikujú najprv na staging.
- Zálohy zapnuté a aspoň jedna obnova reálne vykonaná.
- Sledovanie chýb na frontende aj backende, so source mapami a upozorneniami.
- Limity požiadaviek na prihlasovanie, e-maily/SMS a AI endpointy; u poskytovateľa modelu nastavený limit výdavkov.
- Zoznam povolených originov v CORS namiesto
*; preflight ošetrený. - Kontrola dostupnosti na stránku aj na API route vyžadujúcu prihlásenie.
Ak chcete rýchly prvý pohľad na to, kde vaša aplikácia stojí, 60-sekundový audit pripravenosti ohodnotí tie isté oblasti a povie vám, na ktorú sa pozrieť ako prvú.
Ako vám môže pomôcť Backloo
Ak checklist prechádzate sami, nechajte si túto stránku otvorenú a postupujte zhora nadol — poradie je zámerné. Ak by ste to radšej nechali na inžinierovi, naša záchrana AI aplikácií začína auditom presne týchto oblastí za pevnú cenu, po ktorom nasledujú šprinty s pevným rozsahom, v ktorých spevníme, čo je krehké. Po spustení sa o monitoring, aktualizácie a reakčné časy postará údržba a SLA. V oboch prípadoch vám zostáva kód, repozitár aj cloudové účty. Nie ste si istí, či je oprava vôbec správny krok? Prečítajte si záchrana, refaktoring, alebo prepis.
Otázky
Prečo moja aplikácia z Lovable funguje v náhľade, ale po nasadení nie?
Zvyčajne pre rozdiely v prostredí: klientske premenné prostredia zapečené pri builde, presmerovania prihlásenia stále mieriace na náhľad, CORS na novom origine alebo testovacie kľúče v produkcii. Skontrolujte tieto štyri veci skôr, než začnete ladiť kód aplikácie.
Je bezpečné, že môj anon kľúč Supabase je viditeľný v prehliadači?
Áno, anon (publishable) kľúč je navrhnutý ako verejný — ale len ak je na každej vystavenej tabuľke zapnuté Row Level Security so správnymi politikami. Bez RLS dáva anon kľúč prístup na čítanie a často aj na zápis do vašich dát. Kľúč service_role (secret) sa nikdy nesmie dostať do prehliadača.
Musím aplikáciu vygenerovanú AI prepísať, aby bola pripravená na produkciu?
Zriedka. Väčšina produkčných problémov v aplikáciách postavených s AI sa týka konfigurácie, autorizácie a chýbajúcich poistiek, ktoré sa dajú opraviť na mieste. Prepis má zmysel iba vtedy, keď dátový model alebo architektúra bojuje proti každej zmene; pozrite si náš návod záchrana, refaktoring, alebo prepis.
Čo je idempotencia webhooku a prečo na nej záleží?
Idempotencia znamená, že dvojité spracovanie tej istej udalosti má rovnaký účinok ako jednorazové. Platobné brány doručujú webhooky aspoň raz a pri chybách ich opakujú, takže bez idempotencie môžete udeliť prístup dvakrát, poslať duplicitné e-maily alebo započítať tržby dvakrát.