Introduce incidenti con dedup, job Sidekiq ogni 3 min, scan log cron, /up/deep, dashboard /admin/ops e push ntfy; include Sentry opzionale e fix test suite. Co-authored-by: Cursor <cursoragent@cursor.com>
44 lines
1.3 KiB
Ruby
44 lines
1.3 KiB
Ruby
module Ops
|
|
class Incident < ApplicationRecord
|
|
self.table_name = "ops_incidents"
|
|
|
|
KINDS = %w[
|
|
disk_space recordings_size service_down http_public sidekiq_stale sidekiq_dead
|
|
log_pattern garage_storage
|
|
].freeze
|
|
SEVERITIES = %w[critical warning info].freeze
|
|
STATUSES = %w[open acknowledged resolved].freeze
|
|
|
|
validates :kind, :severity, :status, :title, :fingerprint, presence: true
|
|
validates :severity, inclusion: { in: SEVERITIES }
|
|
validates :status, inclusion: { in: STATUSES }
|
|
validates :occurrence_count, numericality: { greater_than: 0 }
|
|
|
|
scope :open, -> { where(status: %w[open acknowledged]) }
|
|
scope :unresolved, -> { where.not(status: "resolved") }
|
|
scope :critical_open, -> { open.where(severity: "critical") }
|
|
scope :recent, -> { order(last_seen_at: :desc) }
|
|
scope :since, ->(time) { where("last_seen_at >= ?", time) }
|
|
|
|
def open?
|
|
status.in?(%w[open acknowledged])
|
|
end
|
|
|
|
def muted?
|
|
muted_until.present? && muted_until.future?
|
|
end
|
|
|
|
def acknowledge!
|
|
update!(status: "acknowledged", acknowledged_at: Time.current)
|
|
end
|
|
|
|
def resolve!
|
|
update!(status: "resolved")
|
|
end
|
|
|
|
def mute!(duration: 24.hours)
|
|
update!(muted_until: duration.from_now)
|
|
end
|
|
end
|
|
end
|