mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 15:51:23 +00:00
feat: telemetry alert rules with webhook delivery
core.alert_rules (migration 00012) — heartbeat_silence and error_spike rules evaluated by a 60s backend ticker. Transition-based firing: alert.fired on breach, alert.resolved on recovery, both first-class webhook event types riding the signed dispatcher + realtime stream. Dashboard manages rules on the Integrations page. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
345cf7918b
commit
2f4de543a9
28 files changed
+1693
-10
No files matched your search
@@ -0,0 +1,290 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// source: alert_rules.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createAlertRule = `-- name: CreateAlertRule :one
|
||||
INSERT INTO core.alert_rules (project_id, name, kind, config)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, project_id, name, kind, config, enabled, state, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateAlertRuleParams struct {
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
Config []byte `json:"config"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateAlertRule(ctx context.Context, arg CreateAlertRuleParams) (CoreAlertRule, error) {
|
||||
row := q.db.QueryRow(ctx, createAlertRule,
|
||||
arg.ProjectID,
|
||||
arg.Name,
|
||||
arg.Kind,
|
||||
arg.Config,
|
||||
)
|
||||
var i CoreAlertRule
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Name,
|
||||
&i.Kind,
|
||||
&i.Config,
|
||||
&i.Enabled,
|
||||
&i.State,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteAlertRule = `-- name: DeleteAlertRule :exec
|
||||
DELETE FROM core.alert_rules
|
||||
WHERE id = $1 AND project_id = $2
|
||||
`
|
||||
|
||||
type DeleteAlertRuleParams struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteAlertRule(ctx context.Context, arg DeleteAlertRuleParams) error {
|
||||
_, err := q.db.Exec(ctx, deleteAlertRule, arg.ID, arg.ProjectID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAlertRule = `-- name: GetAlertRule :one
|
||||
SELECT id, project_id, name, kind, config, enabled, state, created_at, updated_at FROM core.alert_rules
|
||||
WHERE id = $1 AND project_id = $2
|
||||
`
|
||||
|
||||
type GetAlertRuleParams struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAlertRule(ctx context.Context, arg GetAlertRuleParams) (CoreAlertRule, error) {
|
||||
row := q.db.QueryRow(ctx, getAlertRule, arg.ID, arg.ProjectID)
|
||||
var i CoreAlertRule
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Name,
|
||||
&i.Kind,
|
||||
&i.Config,
|
||||
&i.Enabled,
|
||||
&i.State,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const lastHeartbeats = `-- name: LastHeartbeats :many
|
||||
SELECT co.name AS component_name, max(e.ts)::timestamptz AS last_beat
|
||||
FROM core.components co
|
||||
JOIN core.events e ON e.component_id = co.id AND e.type = 'heartbeat'
|
||||
WHERE co.project_id = $1
|
||||
GROUP BY co.name
|
||||
`
|
||||
|
||||
type LastHeartbeatsRow struct {
|
||||
ComponentName string `json:"component_name"`
|
||||
LastBeat pgtype.Timestamptz `json:"last_beat"`
|
||||
}
|
||||
|
||||
// Per-component most recent heartbeat — heartbeat_silence detection.
|
||||
func (q *Queries) LastHeartbeats(ctx context.Context, projectID uuid.UUID) ([]LastHeartbeatsRow, error) {
|
||||
rows, err := q.db.Query(ctx, lastHeartbeats, projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []LastHeartbeatsRow
|
||||
for rows.Next() {
|
||||
var i LastHeartbeatsRow
|
||||
if err := rows.Scan(&i.ComponentName, &i.LastBeat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listAlertRules = `-- name: ListAlertRules :many
|
||||
SELECT id, project_id, name, kind, config, enabled, state, created_at, updated_at FROM core.alert_rules
|
||||
WHERE project_id = $1
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) ListAlertRules(ctx context.Context, projectID uuid.UUID) ([]CoreAlertRule, error) {
|
||||
rows, err := q.db.Query(ctx, listAlertRules, projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CoreAlertRule
|
||||
for rows.Next() {
|
||||
var i CoreAlertRule
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Name,
|
||||
&i.Kind,
|
||||
&i.Config,
|
||||
&i.Enabled,
|
||||
&i.State,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listEnabledAlertRules = `-- name: ListEnabledAlertRules :many
|
||||
SELECT id, project_id, name, kind, config, enabled, state, created_at, updated_at FROM core.alert_rules
|
||||
WHERE enabled
|
||||
`
|
||||
|
||||
func (q *Queries) ListEnabledAlertRules(ctx context.Context) ([]CoreAlertRule, error) {
|
||||
rows, err := q.db.Query(ctx, listEnabledAlertRules)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CoreAlertRule
|
||||
for rows.Next() {
|
||||
var i CoreAlertRule
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Name,
|
||||
&i.Kind,
|
||||
&i.Config,
|
||||
&i.Enabled,
|
||||
&i.State,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const recentErrorCounts = `-- name: RecentErrorCounts :many
|
||||
SELECT co.name AS component_name, count(*) AS errors
|
||||
FROM core.events e
|
||||
JOIN core.components co ON co.id = e.component_id
|
||||
WHERE e.project_id = $1 AND e.type = 'error'
|
||||
AND e.ts > now() - make_interval(secs => $2::bigint)
|
||||
GROUP BY co.name
|
||||
`
|
||||
|
||||
type RecentErrorCountsParams struct {
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
Column2 int64 `json:"column_2"`
|
||||
}
|
||||
|
||||
type RecentErrorCountsRow struct {
|
||||
ComponentName string `json:"component_name"`
|
||||
Errors int64 `json:"errors"`
|
||||
}
|
||||
|
||||
// Per-component error count inside a sliding window — error_spike detection.
|
||||
func (q *Queries) RecentErrorCounts(ctx context.Context, arg RecentErrorCountsParams) ([]RecentErrorCountsRow, error) {
|
||||
rows, err := q.db.Query(ctx, recentErrorCounts, arg.ProjectID, arg.Column2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []RecentErrorCountsRow
|
||||
for rows.Next() {
|
||||
var i RecentErrorCountsRow
|
||||
if err := rows.Scan(&i.ComponentName, &i.Errors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const setAlertRuleState = `-- name: SetAlertRuleState :exec
|
||||
UPDATE core.alert_rules
|
||||
SET state = $3, updated_at = NOW()
|
||||
WHERE id = $1 AND project_id = $2
|
||||
`
|
||||
|
||||
type SetAlertRuleStateParams struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
State []byte `json:"state"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetAlertRuleState(ctx context.Context, arg SetAlertRuleStateParams) error {
|
||||
_, err := q.db.Exec(ctx, setAlertRuleState, arg.ID, arg.ProjectID, arg.State)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateAlertRule = `-- name: UpdateAlertRule :one
|
||||
UPDATE core.alert_rules
|
||||
SET name = $3, config = $4, enabled = $5, updated_at = NOW()
|
||||
WHERE id = $1 AND project_id = $2
|
||||
RETURNING id, project_id, name, kind, config, enabled, state, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateAlertRuleParams struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
Name string `json:"name"`
|
||||
Config []byte `json:"config"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateAlertRule(ctx context.Context, arg UpdateAlertRuleParams) (CoreAlertRule, error) {
|
||||
row := q.db.QueryRow(ctx, updateAlertRule,
|
||||
arg.ID,
|
||||
arg.ProjectID,
|
||||
arg.Name,
|
||||
arg.Config,
|
||||
arg.Enabled,
|
||||
)
|
||||
var i CoreAlertRule
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Name,
|
||||
&i.Kind,
|
||||
&i.Config,
|
||||
&i.Enabled,
|
||||
&i.State,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
Reference in new issue
Block a user