# Portal Cleanup Update - January 19, 2026 ## Summary Completed consolidation of portal's database access through lib functions. Removed all direct SQL calls and the duplicate database connection. ## Changes Made ### 1. New File: `portal/upload.go` (359 lines) Extracted all upload-related code from main.go into a dedicated file: - `Upload` struct - display model for uploads - `UploadData` struct - JSON structure for Entry.Data - `formatBytes()` - human-readable file sizes - `getUploads()` - list uploads using `lib.EntryList()` - `getUploadEntry()` - get single upload using `lib.EntryGet()` - `findUploadByFilename()` - find existing uploads using `lib.EntryList()` - `handleUploadPage()` - upload page handler - `handleUploadPost()` - upload POST handler - `handleDeleteFile()` - file deletion handler - `handleUpdateFile()` - file update handler **Key improvements:** - All SQL replaced with `lib.Entry*` functions - `uuid.New().String()` replaced with `lib.NewID()` (Hex16 format) - Proper JSON marshaling for upload metadata ### 2. Modified: `portal/main.go` **Removed:** - `db *sql.DB` variable declaration (line 29) - `db, err = sql.Open(...)` from `initDB()` (line 125) - `import "github.com/google/uuid"` - `import "encoding/json"` (unused after extraction) - `import "io"` (unused after extraction) - `func generateUUID()` (dead code) - `type Upload struct` (moved to upload.go) - `func formatBytes()` (moved to upload.go) - `func getUploads()` (moved to upload.go) - `func getUploadEntry()` (moved to upload.go) - `func handleUploadPage()` (moved to upload.go) - `func handleUploadPost()` (moved to upload.go) - `func handleDeleteFile()` (moved to upload.go) - `func handleUpdateFile()` (moved to upload.go) **Changed:** - `hasGenome` check: Direct SQL → `lib.EntryList()` with filter - Language update: `lib.DB().Exec()` → `lib.DossierWrite()` **Result:** main.go reduced from ~1540 lines to ~1290 lines ### 3. Modified: `portal/genome.go` **Removed:** - Buggy cleanup code that queried integer `category` column as string - Direct `db.Query()` and `db.Exec()` calls **Changed:** - `db.Begin()` → `lib.DB().Begin()` (transactions for batch inserts) **Note:** Genome import still uses transactions for bulk inserts (performance). This is acceptable as it goes through `lib.DB()`. ## Database Connection Architecture **Before:** ``` portal/main.go: db *sql.DB ──┐ ├──► same database file lib/db.go: db *sql.DB ──┘ ``` **After:** ``` lib/db.go: db *sql.DB ──► single connection portal/*: lib.DB() ──► uses lib's connection portal/main.go: rateDB ──► separate rate-limit database (unchanged) ``` ## Remaining lib.DB() Usage Only 2 locations in portal still use `lib.DB()` directly: | File | Line | Purpose | |------|------|---------| | genome.go | 263 | `tx.Begin()` for batch insert | | genome.go | 292 | `tx.Begin()` for batch insert (continuation) | These are performance-critical bulk operations using transactions. Acceptable. ## File Naming Change **Before:** Uploaded files stored with UUID names like: ``` uploads/a1b2c3d4e5f67890/550e8400-e29b-41d4-a716-446655440000 ``` **After:** Uploaded files stored with Hex16 names like: ``` uploads/a1b2c3d4e5f67890/f1e2d3c4b5a69078 ``` Existing files with UUID names will continue to work (paths stored in entry data). ## Verification - ✅ `go build ./portal` - passes - ✅ `go build ./api` - passes - ✅ No direct `db.Query/Exec/QueryRow` in portal/*.go - ✅ All entry operations use `lib.Entry*` functions - ✅ Single database connection via `lib.DB()` ## Next Steps Per the store consolidation plan (`docs/store-consolidation-plan.md`): 1. **Phase 2:** Consolidate remaining direct SQL in `api/` (21 operations in 4 files) 2. **Phase 4:** Add RBAC layer to lib/store.go The portal is now clean and ready for RBAC enforcement.