First commit!

This commit is contained in:
Dan Milne
2025-11-03 17:37:28 +11:00
commit 429d41eead
141 changed files with 5890 additions and 0 deletions

97
app/models/issue.rb Normal file
View File

@@ -0,0 +1,97 @@
# frozen_string_literal: true
class Issue < ApplicationRecord
belongs_to :project
has_many :events, dependent: :nullify
enum :status, { open: 0, resolved: 1, ignored: 2 }
validates :title, presence: true
scope :recent, -> { order(last_seen: :desc) }
scope :by_frequency, -> { order(count: :desc) }
# Callbacks for email notifications
after_create :notify_new_issue, if: :should_notify_new_issue?
after_update :notify_issue_reopened, if: :was_reopened?
# Real-time updates
after_create_commit do
broadcast_refresh_to(project)
end
after_update_commit do
broadcast_refresh # Refreshes the issue show page
broadcast_refresh_to(project, "issues") # Refreshes the project's issues index
end
def self.group_event(event_payload, project)
fingerprint = generate_fingerprint(event_payload)
find_or_create_by(project: project, fingerprint: fingerprint) do |issue|
issue.title = extract_title(event_payload)
issue.exception_type = extract_exception_type(event_payload)
issue.first_seen = Time.current
issue.last_seen = Time.current
issue.status = :open
end
end
def self.generate_fingerprint(payload)
# Use Sentry's fingerprint if provided
if payload["fingerprint"].present?
payload["fingerprint"].join("::")
else
# Generate from exception type + location
type = payload.dig("exception", "values", 0, "type")
file = payload.dig("exception", "values", 0, "stacktrace", "frames", -1, "filename")
line = payload.dig("exception", "values", 0, "stacktrace", "frames", -1, "lineno")
# Fallback to message if no exception
if type.blank?
message = payload["message"] || "Unknown Error"
Digest::MD5.hexdigest(message)
else
"#{type}::#{file}::#{line}"
end
end
end
def self.extract_title(payload)
payload.dig("exception", "values", 0, "value") ||
payload["message"] ||
payload.dig("exception", "values", 0, "type") ||
"Unknown Error"
end
def self.extract_exception_type(payload)
payload.dig("exception", "values", 0, "type")
end
def record_event!(timestamp: Time.current)
update!(
count: count + 1,
last_seen: timestamp
)
end
private
def notify_new_issue
IssueMailer.new_issue(self).deliver_later
end
def notify_issue_reopened
IssueMailer.issue_reopened(self).deliver_later
end
def should_notify_new_issue?
# Only notify for new issues in production environment or if explicitly enabled
Rails.env.production? || ENV['SPLAT_EMAIL_NOTIFICATIONS'] == 'true'
end
def was_reopened?
# Check if status changed from resolved to open
saved_change_to_status?(from: 1, to: 0) # resolved=1, open=0
end
end