git clean smaže uživatelské uploady (untracked soubory v repu)
import { Aside } from ‘@astrojs/starlight/components’;
Symptom
Sekce “Symptom”Klientské fotky a dokumenty nahrané uživateli (vč. fotek z mobilu) přestaly být dostupné — odkazy vracely 404 / soubory chyběly na disku. Databázové záznamy o souborech existovaly dál, ale fyzické soubory byly pryč. Odhaleno náhodně až po týdnech, protože nic neupozornilo.
Root cause
Sekce “Root cause”Uživatelské uploady se ukládaly do adresáře uvnitř git working tree aplikace (<APP_DIR>/uploads/) a tento adresář nebyl v .gitignore. Soubory tedy existovaly jako untracked.
Při ladění/údržbě někdo spustil git clean -fdx (běžný příkaz na „uveď repo do čistého stavu”) — ten smaže všechny untracked soubory a adresáře, tedy i uploady.
Stopa po smazání nebyla v shell historii: příkazy spouštěné neinteraktivně přes ssh server "<cmd>" (typicky deploy a automatizace) se nezapisují do .bash_history, takže destruktivní jednorázový příkaz nezanechá dohledatelnou stopu.
# Smrtící kombinace:# - uploads/ je untracked (není v .gitignore)# - uploads/ leží uvnitř git working treegit clean -fdx # smaže VŠE untracked → včetně uploads/Data zachránila jediná věc: offsite záloha běžela rsyncem BEZ --delete, takže soubory smazané lokálně na záloze zůstaly. S --delete by byla nenávratně pryč.
Fix
Sekce “Fix”- Okamžitá obnova ze zálohy (additivně, aby se nepřepsaly novější lokální verze):
Terminál rsync -az --ignore-existing -e "ssh ..." <REMOTE>:<PATH>/uploads/ <APP_DIR>/uploads/ - Gitignore uploadů — aby
git cleanani omylem nemohl uploady smazat a aby se nezacommitovaly:# Uživatelské nahrané soubory — NIKDY do gituuploads/ - Monitoring — hodinový cron porovná počet souborů lokálně vs. záloha a při propadu pošle e-mail (viz níže).
Jak se tomu vyvarovat v jiných systémech
Sekce “Jak se tomu vyvarovat v jiných systémech”- Detection:
git check-ignore <UPLOADS_DIR>musí vrátit cestu (= je ignorováno). Pokud nevrátí nic → díra.- Grep v repu: leží
uploads/(nebomedia/,storage/,attachments/) uvnitř working tree a chybí v.gitignore?
- Anti-pattern:
- Uživatelská data v adresáři, který je v dosahu
git clean/git reset --hard/rm -rfz deploy skriptu. - Záloha s
--delete(zrcadlí i mazání → není to záloha proti smazání). - Spoléhání na
.bash_historyjako audit trail (neinteraktivní SSH příkazy tam nejsou).
- Uživatelská data v adresáři, který je v dosahu
- Lepší přístup:
- Uploady ukládej do adresáře mimo git working tree (např.
/var/lib/<app>/uploads), cestu drž v ENV (UPLOADS_DIR). I tak je gitignoruj. - Offsite záloha vždy bez
--delete(additivní = high-water mark). - Obnovovací příkaz měj zdokumentovaný v runbooku/README.
- Guard cron s e-mail alertem na propad počtu souborů (alert přes lokální
sendmail, ne přes SMTP auth, která může selhat/rotovat):Terminál MISSING=$(rsync -rn --ignore-existing --out-format="%n" -e "ssh ..." \<REMOTE>:<PATH>/uploads/<cenny_adresar>/ <UPLOADS_DIR>/<cenny_adresar>/ \| grep -vE '/$' | wc -l)[ "$MISSING" -ge 10 ] && echo "ALERT: chybí $MISSING souborů" | /usr/sbin/sendmail -t ...# regenerovatelné adresáře (cache) z kontroly vynech - Deploy skript nikdy nesmí sahat na
UPLOADS_DIR(žádnégit clean,reset --hard,rm -rf).
- Uploady ukládej do adresáře mimo git working tree (např.
Sister bugs / související
Sekce “Sister bugs / související”- Prázdný
mimeTypeu uložených souborů → servírují se jakoapplication/octet-streama místo zobrazení se stáhnou. Vždy ukládejmimeType,originalName,storagePath,fileSize. - HEIC/HEIF z mobilů prohlížeč nezobrazí → konvertuj na JPEG už při uploadu (ImageMagick/sharp), HEIC detekuj i podle přípony (prohlížeč posílá špatný/prázdný MIME).
- Servírování souborů přes autentizovaný endpoint s RBAC, ne ze static složky bez kontroly oprávnění.