Files
tyto/backend/internal/database/database.go
vikingowl c0e678931d feat: add database layer with SQLite and PostgreSQL support
Database Package (internal/database/):
- Database interface abstraction for multiple backends
- SQLite implementation with pure Go driver (no CGO)
- PostgreSQL implementation with connection pooling
- Factory pattern for creating database from config
- Tiered retention with automatic aggregation:
  - Raw metrics: 24h (5s resolution)
  - 1-minute aggregation: 7 days
  - 5-minute aggregation: 30 days
  - Hourly aggregation: 1 year

Schema includes:
- agents: registration, status, certificates
- users: local + LDAP authentication
- roles: RBAC with permissions JSON
- sessions: token-based authentication
- metrics_*: time-series with aggregation
- alerts: triggered alerts with acknowledgment

Configuration Updates:
- DatabaseConfig with SQLite path and PostgreSQL settings
- RetentionConfig for customizing data retention
- Environment variables: TYTO_DB_*, TYTO_DB_CONNECTION_STRING
- Default SQLite at /var/lib/tyto/tyto.db

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 08:18:48 +01:00

209 lines
6.8 KiB
Go

// Package database provides database abstraction for Tyto.
// Supports both SQLite (default) and PostgreSQL backends.
package database
import (
"context"
"time"
"tyto/internal/models"
)
// Database defines the interface for all database operations.
type Database interface {
// Lifecycle
Close() error
Migrate() error
// Metrics storage
StoreMetrics(ctx context.Context, agentID string, metrics *models.AllMetrics) error
QueryMetrics(ctx context.Context, agentID string, from, to time.Time, resolution string) ([]MetricPoint, error)
GetLatestMetrics(ctx context.Context, agentID string) (*models.AllMetrics, error)
// Agents (extends registry with persistence)
StoreAgent(ctx context.Context, agent *Agent) error
GetAgent(ctx context.Context, id string) (*Agent, error)
ListAgents(ctx context.Context) ([]*Agent, error)
UpdateAgentStatus(ctx context.Context, id string, status AgentStatus, lastSeen time.Time) error
DeleteAgent(ctx context.Context, id string) error
// Users
CreateUser(ctx context.Context, user *User) error
GetUser(ctx context.Context, id string) (*User, error)
GetUserByUsername(ctx context.Context, username string) (*User, error)
UpdateUser(ctx context.Context, user *User) error
DeleteUser(ctx context.Context, id string) error
ListUsers(ctx context.Context) ([]*User, error)
// Roles
CreateRole(ctx context.Context, role *Role) error
GetRole(ctx context.Context, id string) (*Role, error)
ListRoles(ctx context.Context) ([]*Role, error)
UpdateRole(ctx context.Context, role *Role) error
DeleteRole(ctx context.Context, id string) error
GetUserRoles(ctx context.Context, userID string) ([]*Role, error)
AssignRole(ctx context.Context, userID, roleID string) error
RemoveRole(ctx context.Context, userID, roleID string) error
// Sessions
CreateSession(ctx context.Context, session *Session) error
GetSession(ctx context.Context, token string) (*Session, error)
DeleteSession(ctx context.Context, token string) error
DeleteUserSessions(ctx context.Context, userID string) error
CleanupExpiredSessions(ctx context.Context) error
// Alerts
StoreAlert(ctx context.Context, alert *Alert) error
GetAlert(ctx context.Context, id string) (*Alert, error)
QueryAlerts(ctx context.Context, filter AlertFilter) ([]*Alert, error)
AcknowledgeAlert(ctx context.Context, id string) error
// Retention
RunRetention(ctx context.Context) error
}
// MetricPoint represents a single metric data point.
type MetricPoint struct {
Timestamp time.Time `json:"timestamp"`
AgentID string `json:"agentId"`
// Aggregated values
CPUAvg float64 `json:"cpuAvg"`
CPUMin float64 `json:"cpuMin"`
CPUMax float64 `json:"cpuMax"`
MemoryAvg float64 `json:"memoryAvg"`
MemoryMin float64 `json:"memoryMin"`
MemoryMax float64 `json:"memoryMax"`
DiskAvg float64 `json:"diskAvg,omitempty"`
GPUAvg float64 `json:"gpuAvg,omitempty"`
}
// AgentStatus represents agent connection state.
type AgentStatus string
const (
AgentStatusPending AgentStatus = "pending"
AgentStatusApproved AgentStatus = "approved"
AgentStatusConnected AgentStatus = "connected"
AgentStatusOffline AgentStatus = "offline"
AgentStatusRevoked AgentStatus = "revoked"
)
// Agent represents a registered monitoring agent.
type Agent struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Hostname string `json:"hostname"`
OS string `json:"os"`
Architecture string `json:"architecture"`
Version string `json:"version"`
Capabilities []string `json:"capabilities,omitempty"`
Status AgentStatus `json:"status"`
CertSerial string `json:"certSerial,omitempty"`
CertExpiry time.Time `json:"certExpiry,omitempty"`
LastSeen time.Time `json:"lastSeen,omitempty"`
RegisteredAt time.Time `json:"registeredAt"`
Tags []string `json:"tags,omitempty"`
}
// AuthProvider indicates how a user authenticates.
type AuthProvider string
const (
AuthProviderLocal AuthProvider = "local"
AuthProviderLDAP AuthProvider = "ldap"
)
// User represents a system user.
type User struct {
ID string `json:"id"`
Username string `json:"username"`
Email string `json:"email,omitempty"`
PasswordHash []byte `json:"-"`
AuthProvider AuthProvider `json:"authProvider"`
LDAPDN string `json:"ldapDn,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastLogin time.Time `json:"lastLogin,omitempty"`
Disabled bool `json:"disabled"`
}
// Role represents a set of permissions.
type Role struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Permissions []string `json:"permissions"`
IsSystem bool `json:"isSystem"`
CreatedAt time.Time `json:"createdAt"`
}
// Session represents an authenticated user session.
type Session struct {
Token string `json:"token"`
UserID string `json:"userId"`
CreatedAt time.Time `json:"createdAt"`
ExpiresAt time.Time `json:"expiresAt"`
IPAddress string `json:"ipAddress,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
}
// AlertSeverity indicates alert severity level.
type AlertSeverity string
const (
AlertSeverityWarning AlertSeverity = "warning"
AlertSeverityCritical AlertSeverity = "critical"
)
// Alert represents a triggered alert.
type Alert struct {
ID string `json:"id"`
AgentID string `json:"agentId"`
Type string `json:"type"`
Severity AlertSeverity `json:"severity"`
Message string `json:"message"`
Value float64 `json:"value"`
Threshold float64 `json:"threshold"`
TriggeredAt time.Time `json:"triggeredAt"`
ResolvedAt *time.Time `json:"resolvedAt,omitempty"`
Acknowledged bool `json:"acknowledged"`
}
// AlertFilter specifies criteria for querying alerts.
type AlertFilter struct {
AgentID string
Type string
Severity AlertSeverity
Acknowledged *bool
From time.Time
To time.Time
Limit int
Offset int
}
// RetentionConfig defines data retention policies.
type RetentionConfig struct {
// Raw metrics retention (default: 24 hours)
RawRetention time.Duration
// 1-minute aggregation retention (default: 7 days)
OneMinuteRetention time.Duration
// 5-minute aggregation retention (default: 30 days)
FiveMinuteRetention time.Duration
// Hourly aggregation retention (default: 1 year)
HourlyRetention time.Duration
}
// DefaultRetentionConfig returns default retention settings.
func DefaultRetentionConfig() RetentionConfig {
return RetentionConfig{
RawRetention: 24 * time.Hour,
OneMinuteRetention: 7 * 24 * time.Hour,
FiveMinuteRetention: 30 * 24 * time.Hour,
HourlyRetention: 365 * 24 * time.Hour,
}
}