diff --git a/hub/internal/domain/stream.go b/hub/internal/domain/stream.go index a0d4499..7165398 100644 --- a/hub/internal/domain/stream.go +++ b/hub/internal/domain/stream.go @@ -1,5 +1,7 @@ package domain +import "time" + type AgentRequest struct { RequestID string Name string @@ -28,7 +30,15 @@ type SystemMetrics struct { DiskUsage float64 } -type Heartbeat struct { - Timestamp string +type HeartbeatModel struct { + ID int + AgentID string + Timestamp time.Time + Metrics SystemMetrics +} + +type CreateHeartbeatModel struct { + AgentID string + Timestamp time.Time Metrics SystemMetrics } diff --git a/hub/internal/migrations/20260506182346_create_heartbeat_table.up.sql b/hub/internal/migrations/20260506182346_create_heartbeat_table.up.sql index e848921..dd25c58 100644 --- a/hub/internal/migrations/20260506182346_create_heartbeat_table.up.sql +++ b/hub/internal/migrations/20260506182346_create_heartbeat_table.up.sql @@ -1,10 +1,10 @@ CREATE TABLE heartbeats ( id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id VARCHAR(32) UNIQUE NOT NULL, - cpu_usage FLOAT, - memory_usage FLOAT, - disk_usage FLOAT, - heartbeat_timestamp TIMESTAMP + cpu_usage FLOAT NOT NULL , + memory_usage FLOAT NOT NULL , + disk_usage FLOAT NOT NULL , + heartbeat_timestamp TIMESTAMP NOT NULL, FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE ); diff --git a/hub/internal/store/mapper.go b/hub/internal/store/mapper.go index 6fc40b7..e78c9e4 100644 --- a/hub/internal/store/mapper.go +++ b/hub/internal/store/mapper.go @@ -76,3 +76,27 @@ func toDomainCapabilities(caps []byte) []domain.Capability { } return capabilities } + +func toDBHeartbeat(heartbeat domainHub.CreateHeartbeatModel) gen2.InsertHeartbeatParams { + + return gen2.InsertHeartbeatParams{ + AgentID: heartbeat.AgentID, + HeartbeatTimestamp: heartbeat.Timestamp, + CpuUsage: heartbeat.Metrics.CpuUsage, + DiskUsage: heartbeat.Metrics.DiskUsage, + MemoryUsage: heartbeat.Metrics.MemoryUsage, + } +} + +func toHeartBeatModel(heartbeat gen2.Heartbeat) domainHub.HeartbeatModel { + return domainHub.HeartbeatModel{ + Timestamp: heartbeat.HeartbeatTimestamp, + AgentID: heartbeat.AgentID, + ID: int(heartbeat.ID), + Metrics: domainHub.SystemMetrics{ + CpuUsage: heartbeat.CpuUsage, + DiskUsage: heartbeat.DiskUsage, + MemoryUsage: heartbeat.MemoryUsage, + }, + } +} diff --git a/hub/internal/store/sqlc/gen/agent.sql.go b/hub/internal/store/sqlc/gen/agent.sql.go index 704434b..636304f 100644 --- a/hub/internal/store/sqlc/gen/agent.sql.go +++ b/hub/internal/store/sqlc/gen/agent.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.31.1 // source: agent.sql package gen diff --git a/hub/internal/store/sqlc/gen/db.go b/hub/internal/store/sqlc/gen/db.go index d577e39..b6fcf6b 100644 --- a/hub/internal/store/sqlc/gen/db.go +++ b/hub/internal/store/sqlc/gen/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.31.1 package gen diff --git a/hub/internal/store/sqlc/gen/heartbeat.sql.go b/hub/internal/store/sqlc/gen/heartbeat.sql.go new file mode 100644 index 0000000..b98736f --- /dev/null +++ b/hub/internal/store/sqlc/gen/heartbeat.sql.go @@ -0,0 +1,75 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: heartbeat.sql + +package gen + +import ( + "context" + "time" +) + +const insertHeartbeat = `-- name: InsertHeartbeat :exec +INSERT INTO heartbeats (agent_id, cpu_usage, memory_usage, disk_usage, heartbeat_timestamp) +VALUES (?1, ?2, ?3, ?4, ?5) +` + +type InsertHeartbeatParams struct { + AgentID string + CpuUsage float64 + MemoryUsage float64 + DiskUsage float64 + HeartbeatTimestamp time.Time +} + +func (q *Queries) InsertHeartbeat(ctx context.Context, arg InsertHeartbeatParams) error { + _, err := q.db.ExecContext(ctx, insertHeartbeat, + arg.AgentID, + arg.CpuUsage, + arg.MemoryUsage, + arg.DiskUsage, + arg.HeartbeatTimestamp, + ) + return err +} + +const selectHeartbeatsAfter = `-- name: SelectHeartbeatsAfter :many +SELECT id, agent_id, cpu_usage, memory_usage, disk_usage, heartbeat_timestamp FROM heartbeats +WHERE agent_id = ?1 AND heartbeat_timestamp > ?2 +` + +type SelectHeartbeatsAfterParams struct { + AgentID string + Timestamp time.Time +} + +func (q *Queries) SelectHeartbeatsAfter(ctx context.Context, arg SelectHeartbeatsAfterParams) ([]Heartbeat, error) { + rows, err := q.db.QueryContext(ctx, selectHeartbeatsAfter, arg.AgentID, arg.Timestamp) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Heartbeat + for rows.Next() { + var i Heartbeat + if err := rows.Scan( + &i.ID, + &i.AgentID, + &i.CpuUsage, + &i.MemoryUsage, + &i.DiskUsage, + &i.HeartbeatTimestamp, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/hub/internal/store/sqlc/gen/models.go b/hub/internal/store/sqlc/gen/models.go index b40fd8b..19c5aef 100644 --- a/hub/internal/store/sqlc/gen/models.go +++ b/hub/internal/store/sqlc/gen/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.31.1 package gen @@ -19,3 +19,12 @@ type Agent struct { Capabilities *string RegisteredAt time.Time } + +type Heartbeat struct { + ID int64 + AgentID string + CpuUsage float64 + MemoryUsage float64 + DiskUsage float64 + HeartbeatTimestamp time.Time +} diff --git a/hub/internal/store/sqlc/queries/heartbeat.sql b/hub/internal/store/sqlc/queries/heartbeat.sql new file mode 100644 index 0000000..936bae2 --- /dev/null +++ b/hub/internal/store/sqlc/queries/heartbeat.sql @@ -0,0 +1,7 @@ +-- name: InsertHeartbeat :exec +INSERT INTO heartbeats (agent_id, cpu_usage, memory_usage, disk_usage, heartbeat_timestamp) +VALUES (:agent_id, :cpu_usage, :memory_usage, :disk_usage, :heartbeat_timestamp); + +-- name: SelectHeartbeatsAfter :many +SELECT * FROM heartbeats +WHERE agent_id = :agent_id AND heartbeat_timestamp > :timestamp; \ No newline at end of file diff --git a/hub/internal/store/store.go b/hub/internal/store/store.go index 3cfa60b..9d9fd94 100644 --- a/hub/internal/store/store.go +++ b/hub/internal/store/store.go @@ -3,6 +3,7 @@ package store import ( "context" "database/sql" + "time" domainHub "github.com/lorsanstand/HomeOps-Hub/hub/internal/domain" "github.com/lorsanstand/HomeOps-Hub/hub/internal/store/sqlc/gen" @@ -21,8 +22,8 @@ func (h *HubStore) NewAgent(ctx context.Context, agent domainHub.CreateAgentMode return h.queries.CreateAgent(ctx, toDBAgent(agent)) } -func (h *HubStore) GetAgentByAgentID(ctx context.Context, AgentID string) (domainHub.AgentModel, error) { - data, err := h.queries.GetAgentByAgentID(ctx, AgentID) +func (h *HubStore) GetAgentByAgentID(ctx context.Context, agentID string) (domainHub.AgentModel, error) { + data, err := h.queries.GetAgentByAgentID(ctx, agentID) if err != nil { return domainHub.AgentModel{}, err } @@ -34,3 +35,24 @@ func (h *HubStore) UpdateAgentByID(ctx context.Context, ID int, updateAgent doma data.ID = int64(ID) return h.queries.UpdateAgentByID(ctx, data) } + +func (h *HubStore) CreateHeartbeat(ctx context.Context, heartbeat domainHub.CreateHeartbeatModel) error { + data := toDBHeartbeat(heartbeat) + return h.queries.InsertHeartbeat(ctx, data) +} + +func (h *HubStore) GetHeartbeatsByIDAfter(ctx context.Context, agentID string, timestamp time.Time) ([]domainHub.HeartbeatModel, error) { + data := gen.SelectHeartbeatsAfterParams{AgentID: agentID, Timestamp: timestamp} + heartbeats, err := h.queries.SelectHeartbeatsAfter(ctx, data) + if err != nil { + return []domainHub.HeartbeatModel{}, err + } + + heartbeatsModel := make([]domainHub.HeartbeatModel, len(heartbeats)) + + for i, heartbeat := range heartbeats { + heartbeatsModel[i] = toHeartBeatModel(heartbeat) + } + + return heartbeatsModel, nil +}