feat(observability): phase 1 - schema, models & event log backend
Add database foundation for observability features: - event_log table with severity/source filtering and pagination - standalone_proxies table for user-created reverse proxies - stale_threshold_days setting (default 7 days) - Auto-persist warn/error events from event bus to database - SSE broadcast of persistent events for real-time UI updates - Frontend types and API functions for downstream UI phases
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// CreateStandaloneProxy inserts a new standalone proxy record.
|
||||
func (s *Store) CreateStandaloneProxy(p StandaloneProxy) (StandaloneProxy, error) {
|
||||
p.ID = uuid.New().String()
|
||||
p.CreatedAt = Now()
|
||||
p.UpdatedAt = p.CreatedAt
|
||||
|
||||
if p.HealthStatus == "" {
|
||||
p.HealthStatus = "unknown"
|
||||
}
|
||||
|
||||
_, err := s.db.Exec(
|
||||
`INSERT INTO standalone_proxies (id, domain, destination_url, destination_port, ssl_certificate_id, npm_proxy_id, health_status, health_checked_at, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
p.ID, p.Domain, p.DestinationURL, p.DestinationPort, p.SSLCertificateID,
|
||||
p.NpmProxyID, p.HealthStatus, p.HealthCheckedAt, p.CreatedAt, p.UpdatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return StandaloneProxy{}, fmt.Errorf("insert standalone proxy: %w", err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// GetStandaloneProxy returns a standalone proxy by ID.
|
||||
func (s *Store) GetStandaloneProxy(id string) (StandaloneProxy, error) {
|
||||
var p StandaloneProxy
|
||||
err := s.db.QueryRow(
|
||||
`SELECT id, domain, destination_url, destination_port, ssl_certificate_id, npm_proxy_id, health_status, health_checked_at, created_at, updated_at
|
||||
FROM standalone_proxies WHERE id = ?`, id,
|
||||
).Scan(&p.ID, &p.Domain, &p.DestinationURL, &p.DestinationPort, &p.SSLCertificateID,
|
||||
&p.NpmProxyID, &p.HealthStatus, &p.HealthCheckedAt, &p.CreatedAt, &p.UpdatedAt)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return StandaloneProxy{}, fmt.Errorf("standalone proxy %s: %w", id, ErrNotFound)
|
||||
}
|
||||
if err != nil {
|
||||
return StandaloneProxy{}, fmt.Errorf("query standalone proxy: %w", err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// ListStandaloneProxies returns all standalone proxy records ordered by creation time.
|
||||
func (s *Store) ListStandaloneProxies() ([]StandaloneProxy, error) {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id, domain, destination_url, destination_port, ssl_certificate_id, npm_proxy_id, health_status, health_checked_at, created_at, updated_at
|
||||
FROM standalone_proxies ORDER BY created_at DESC`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query standalone proxies: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
proxies := []StandaloneProxy{}
|
||||
for rows.Next() {
|
||||
var p StandaloneProxy
|
||||
if err := rows.Scan(&p.ID, &p.Domain, &p.DestinationURL, &p.DestinationPort, &p.SSLCertificateID,
|
||||
&p.NpmProxyID, &p.HealthStatus, &p.HealthCheckedAt, &p.CreatedAt, &p.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan standalone proxy: %w", err)
|
||||
}
|
||||
proxies = append(proxies, p)
|
||||
}
|
||||
return proxies, rows.Err()
|
||||
}
|
||||
|
||||
// UpdateStandaloneProxy updates an existing standalone proxy's mutable fields.
|
||||
func (s *Store) UpdateStandaloneProxy(p StandaloneProxy) error {
|
||||
p.UpdatedAt = Now()
|
||||
result, err := s.db.Exec(
|
||||
`UPDATE standalone_proxies SET domain=?, destination_url=?, destination_port=?, ssl_certificate_id=?, npm_proxy_id=?, health_status=?, health_checked_at=?, updated_at=?
|
||||
WHERE id=?`,
|
||||
p.Domain, p.DestinationURL, p.DestinationPort, p.SSLCertificateID,
|
||||
p.NpmProxyID, p.HealthStatus, p.HealthCheckedAt, p.UpdatedAt, p.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update standalone proxy: %w", err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
if n == 0 {
|
||||
return fmt.Errorf("standalone proxy %s: %w", p.ID, ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteStandaloneProxy removes a standalone proxy by ID.
|
||||
func (s *Store) DeleteStandaloneProxy(id string) error {
|
||||
result, err := s.db.Exec(`DELETE FROM standalone_proxies WHERE id = ?`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete standalone proxy: %w", err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
if n == 0 {
|
||||
return fmt.Errorf("standalone proxy %s: %w", id, ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateProxyHealth updates the health status and check timestamp for a standalone proxy.
|
||||
func (s *Store) UpdateProxyHealth(id string, status string) error {
|
||||
ts := Now()
|
||||
result, err := s.db.Exec(
|
||||
`UPDATE standalone_proxies SET health_status=?, health_checked_at=?, updated_at=? WHERE id=?`,
|
||||
status, ts, ts, id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update proxy health: %w", err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
if n == 0 {
|
||||
return fmt.Errorf("standalone proxy %s: %w", id, ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user