Initial vibeproxy implementation
Self-contained HTTP/HTTPS/TCP/UDP reverse proxy manager written in Go, with SQLite storage, a Bootstrap 5 admin UI (Go templates, no JS framework), and a REST API under /api/v1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user