76 lines
2.1 KiB
Ruby
76 lines
2.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Api::EventsController < ApplicationController
|
|
skip_before_action :verify_authenticity_token
|
|
|
|
# POST /api/:project_id/events
|
|
def create
|
|
project = authenticate_project!
|
|
return head :not_found unless project
|
|
|
|
# Parse the incoming WAF event data
|
|
event_data = parse_event_data(request)
|
|
|
|
# Create event asynchronously
|
|
ProcessWafEventJob.perform_later(
|
|
project_id: project.id,
|
|
event_data: event_data,
|
|
headers: extract_serializable_headers(request)
|
|
)
|
|
|
|
# Always return 200 OK to avoid agent retries
|
|
head :ok
|
|
rescue DsnAuthenticationService::AuthenticationError => e
|
|
Rails.logger.warn "DSN authentication failed: #{e.message}"
|
|
head :unauthorized
|
|
rescue JSON::ParserError => e
|
|
Rails.logger.error "Invalid JSON in event data: #{e.message}"
|
|
head :bad_request
|
|
end
|
|
|
|
private
|
|
|
|
def authenticate_project!
|
|
DsnAuthenticationService.authenticate(request, params[:project_id])
|
|
end
|
|
|
|
def parse_event_data(request)
|
|
# Handle different content types
|
|
content_type = request.content_type || "application/json"
|
|
|
|
case content_type
|
|
when /application\/json/
|
|
JSON.parse(request.body.read)
|
|
when /application\/x-www-form-urlencoded/
|
|
# Convert form data to JSON-like hash
|
|
request.request_parameters
|
|
else
|
|
# Try to parse as JSON anyway
|
|
JSON.parse(request.body.read)
|
|
end
|
|
rescue => e
|
|
Rails.logger.error "Failed to parse event data: #{e.message}"
|
|
{}
|
|
ensure
|
|
request.body.rewind if request.body.respond_to?(:rewind)
|
|
end
|
|
|
|
def extract_serializable_headers(request)
|
|
# Only extract the headers we need for analytics, avoiding IO objects
|
|
important_headers = %w[
|
|
User-Agent Content-Type Content-Length Accept
|
|
X-Forwarded-For X-Real-IP X-Forwarded-Proto
|
|
Authorization X-Baffle-Auth X-Sentry-Auth
|
|
Referer Accept-Language Accept-Encoding
|
|
]
|
|
|
|
headers = {}
|
|
important_headers.each do |header|
|
|
value = request.headers[header]
|
|
# Standardize headers to lower case during import phase
|
|
headers[header.downcase] = value if value.present?
|
|
end
|
|
|
|
headers
|
|
end
|
|
end |