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,109 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/owen/vibeproxy/internal/models"
|
||||
)
|
||||
|
||||
func scanCertificate(rs rowScanner) (models.Certificate, error) {
|
||||
var c models.Certificate
|
||||
var domainsJSON string
|
||||
var challengeType, dnsProvider, certPath, keyPath, notBefore, notAfter, lastRenewed, lastErr sql.NullString
|
||||
var autoRenew int
|
||||
var created, updated string
|
||||
err := rs.Scan(&c.ID, &c.Name, &c.Type, &domainsJSON, &challengeType, &dnsProvider, &certPath, &keyPath,
|
||||
¬Before, ¬After, &lastRenewed, &lastErr, &autoRenew, &created, &updated)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
json.Unmarshal([]byte(domainsJSON), &c.Domains)
|
||||
c.ChallengeType = models.ChallengeType(challengeType.String)
|
||||
c.DNSProvider = dnsProvider.String
|
||||
c.CertPath = certPath.String
|
||||
c.KeyPath = keyPath.String
|
||||
if notBefore.Valid {
|
||||
t := parseTime(notBefore.String)
|
||||
c.NotBefore = &t
|
||||
}
|
||||
if notAfter.Valid {
|
||||
t := parseTime(notAfter.String)
|
||||
c.NotAfter = &t
|
||||
}
|
||||
if lastRenewed.Valid {
|
||||
t := parseTime(lastRenewed.String)
|
||||
c.LastRenewedAt = &t
|
||||
}
|
||||
c.LastError = lastErr.String
|
||||
c.AutoRenew = autoRenew == 1
|
||||
c.CreatedAt = parseTime(created)
|
||||
c.UpdatedAt = parseTime(updated)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
const certCols = `id, name, type, domains, challenge_type, dns_provider, cert_path, key_path, not_before, not_after, last_renewed_at, last_error, auto_renew, created_at, updated_at`
|
||||
|
||||
func ListCertificates(db *sql.DB) ([]models.Certificate, error) {
|
||||
rows, err := db.Query(`SELECT ` + certCols + ` FROM certificates ORDER BY id`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []models.Certificate
|
||||
for rows.Next() {
|
||||
c, err := scanCertificate(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, c)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func GetCertificate(db *sql.DB, id int64) (*models.Certificate, error) {
|
||||
row := db.QueryRow(`SELECT `+certCols+` FROM certificates WHERE id = ?`, id)
|
||||
c, err := scanCertificate(row)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func SaveCertificate(db *sql.DB, c *models.Certificate) (int64, error) {
|
||||
domainsJSON, _ := json.Marshal(c.Domains)
|
||||
if c.ID == 0 {
|
||||
res, err := db.Exec(`INSERT INTO certificates (name, type, domains, challenge_type, dns_provider, cert_path, key_path,
|
||||
not_before, not_after, last_renewed_at, last_error, auto_renew) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
c.Name, c.Type, string(domainsJSON), string(c.ChallengeType), c.DNSProvider, c.CertPath, c.KeyPath,
|
||||
timeOrNil(c.NotBefore), timeOrNil(c.NotAfter), timeOrNil(c.LastRenewedAt), c.LastError, boolToInt(c.AutoRenew))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
c.ID = id
|
||||
return id, err
|
||||
}
|
||||
_, err := db.Exec(`UPDATE certificates SET name=?, type=?, domains=?, challenge_type=?, dns_provider=?, cert_path=?,
|
||||
key_path=?, not_before=?, not_after=?, last_renewed_at=?, last_error=?, auto_renew=?, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now')
|
||||
WHERE id=?`, c.Name, c.Type, string(domainsJSON), string(c.ChallengeType), c.DNSProvider, c.CertPath, c.KeyPath,
|
||||
timeOrNil(c.NotBefore), timeOrNil(c.NotAfter), timeOrNil(c.LastRenewedAt), c.LastError, boolToInt(c.AutoRenew), c.ID)
|
||||
return c.ID, err
|
||||
}
|
||||
|
||||
func DeleteCertificate(db *sql.DB, id int64) error {
|
||||
_, err := db.Exec(`DELETE FROM certificates WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func timeOrNil(t *time.Time) any {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
return t.UTC().Format(timeFmt)
|
||||
}
|
||||
Reference in New Issue
Block a user