package database import ( "database/sql" "errors" "github.com/owen/vibeproxy/internal/models" ) func ListAccessLists(db *sql.DB) ([]models.AccessList, error) { rows, err := db.Query(`SELECT id, name, default_allow, comments, created_at, updated_at FROM access_lists ORDER BY id`) if err != nil { return nil, err } defer rows.Close() var out []models.AccessList for rows.Next() { a, err := scanAccessList(rows) if err != nil { return nil, err } out = append(out, a) } if err := rows.Err(); err != nil { return nil, err } for i := range out { if err := hydrateAccessList(db, &out[i]); err != nil { return nil, err } } return out, nil } func scanAccessList(rs rowScanner) (models.AccessList, error) { var a models.AccessList var defaultAllow int var created, updated string err := rs.Scan(&a.ID, &a.Name, &defaultAllow, &a.Comments, &created, &updated) if err != nil { return a, err } a.DefaultAllow = defaultAllow == 1 a.CreatedAt = parseTime(created) a.UpdatedAt = parseTime(updated) return a, nil } func hydrateAccessList(db *sql.DB, a *models.AccessList) error { rrows, err := db.Query(`SELECT id, access_list_id, action, cidr, priority FROM access_rules WHERE access_list_id = ? ORDER BY priority, id`, a.ID) if err != nil { return err } for rrows.Next() { var r models.AccessRule if err := rrows.Scan(&r.ID, &r.AccessListID, &r.Action, &r.CIDR, &r.Priority); err != nil { rrows.Close() return err } a.Rules = append(a.Rules, r) } rrows.Close() if err := rrows.Err(); err != nil { return err } urows, err := db.Query(`SELECT id, access_list_id, username, password_hash FROM access_list_users WHERE access_list_id = ? ORDER BY id`, a.ID) if err != nil { return err } for urows.Next() { var u models.AccessListUser if err := urows.Scan(&u.ID, &u.AccessListID, &u.Username, &u.PasswordHash); err != nil { urows.Close() return err } a.BasicAuthUsers = append(a.BasicAuthUsers, u) } urows.Close() return urows.Err() } func GetAccessList(db *sql.DB, id int64) (*models.AccessList, error) { row := db.QueryRow(`SELECT id, name, default_allow, comments, created_at, updated_at FROM access_lists WHERE id = ?`, id) a, err := scanAccessList(row) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, err } if err := hydrateAccessList(db, &a); err != nil { return nil, err } return &a, nil } func SaveAccessList(db *sql.DB, a *models.AccessList) (int64, error) { tx, err := db.Begin() if err != nil { return 0, err } defer tx.Rollback() if a.ID == 0 { res, err := tx.Exec(`INSERT INTO access_lists (name, default_allow, comments) VALUES (?, ?, ?)`, a.Name, boolToInt(a.DefaultAllow), a.Comments) if err != nil { return 0, err } a.ID, err = res.LastInsertId() if err != nil { return 0, err } } else { if _, err := tx.Exec(`UPDATE access_lists SET name=?, default_allow=?, comments=?, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE id=?`, a.Name, boolToInt(a.DefaultAllow), a.Comments, a.ID); err != nil { return 0, err } if _, err := tx.Exec(`DELETE FROM access_rules WHERE access_list_id = ?`, a.ID); err != nil { return 0, err } if _, err := tx.Exec(`DELETE FROM access_list_users WHERE access_list_id = ?`, a.ID); err != nil { return 0, err } } for _, r := range a.Rules { if _, err := tx.Exec(`INSERT INTO access_rules (access_list_id, action, cidr, priority) VALUES (?,?,?,?)`, a.ID, r.Action, r.CIDR, r.Priority); err != nil { return 0, err } } for _, u := range a.BasicAuthUsers { if _, err := tx.Exec(`INSERT INTO access_list_users (access_list_id, username, password_hash) VALUES (?,?,?)`, a.ID, u.Username, u.PasswordHash); err != nil { return 0, err } } if err := tx.Commit(); err != nil { return 0, err } return a.ID, nil } func DeleteAccessList(db *sql.DB, id int64) error { _, err := db.Exec(`DELETE FROM access_lists WHERE id = ?`, id) return err }