Saugyklos sluoksnis ir dokumentų lentelė
Septyni failai kortelėse, iš apačios į viršų: saugyklos sluoksnis, tada lentelė, tada sujungimas. storage.go → local.go → 00004_documents.sql → documents.sql → config.go → handlers.go → main.go.
1. internal/storage/storage.go (naujas) — Store sąsaja. Trys metodai, o paketo komentaras įvardija jungtį garsiai: tai ta pati forma kaip email.Sender, ir ji čia todėl, kad Railway konteinerio failų sistema laikina.
2. internal/storage/local.go (naujas) — LocalStore. Širdis yra Save: atverti failą, tada io.Copy(io.MultiWriter(f, h), r) — vienas perėjimas, maiša ir rašymas kartu. Įvykus bet kokiai kopijavimo ar užvėrimo klaidai, jis pašalina pusiau įrašytą failą, kad nepavykęs įkėlimas nepaliktų nieko. Open/Delete praleidžia raktą per safePath, kuri užtikrina keyPattern prieš jungdama jį prie bazinio aplanko. newKey — 16 atsitiktinių baitų kaip šešioliktainis.
3. db/migrations/00004_documents.sql (naujas) — documents lentelė:
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
file_hash TEXT NOT NULL, -- SHA-256, šešioliktainis
storage_key TEXT NOT NULL, -- neskaidrus raktas į saugyklą
status TEXT NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft', 'sent', 'completed')),
status yra TEXT + CHECK, ne Postgres ENUM — aibė maža ir fiksuota, o išplėsti tikrinimo apribojimą vėliau lengviau nei daryti ALTER TYPE šokį. Gyvavimo ciklas — draft → sent → completed; šiandien viskas yra juodraštis. ON DELETE CASCADE reiškia, kad ištrynus vartotoją, jo dokumentai ištrinami kartu.
4. internal/db/queries/documents.sql (naujas) — kol kas dvi užklausos: CreateDocument (įterpimas, RETURNING *) ir ListDocumentsByOwner (WHERE owner_id = $1 ORDER BY created_at DESC). Savininkui apribotas skaitymas ir juodraščiu ribotas trynimas atkeliaus kitą pamoką. Paleisk sqlc generate ir atsiras tipizuoti CreateDocument/ListDocumentsByOwner metodai.
5. internal/config/config.go (keitimas) — du papildymai: UploadDir (UPLOAD_DIR, numatyta uploads) ir MaxUploadBytes (25 << 20). Komentaras pažymi Railway įspėjimą, kuriuo pasirūpinsi diegimo pamokoje — konteinerio diskas laikinas, todėl produkcijoje UploadDir nukreipiamas į primontuotą tomą.
6. internal/handlers/handlers.go (keitimas) — Handlers struktūra įgyja Store storage.Store lauką, o New jį priima. Grynas papildymas šalia praėjusios pamokos Mailer lauko.
7. cmd/signflow/main.go (keitimas) — sukonstruok saugyklą ir paduok ją New:
store, err := storage.NewLocalStore(cfg.UploadDir)
if err != nil {
return err
}
log.Info("file storage: local disk", "dir", cfg.UploadDir)
h := handlers.New(cfg, queries, sessions, mailer, store, log)
NewLocalStore sukuria aplanką, jei jo nėra, tad pirmas paleidimas tiesiog veikia.