# frozen_string_literal: true module Api class RulesController < ApplicationController # NOTE: This controller is now SECONDARY/UNUSED for primary agent synchronization # Agents get rule updates via event responses (see Api::EventsController) # These endpoints are kept for administrative/debugging purposes only skip_before_action :verify_authenticity_token allow_unauthenticated_access # Skip normal session auth, use DSN auth instead before_action :authenticate_dsn! before_action :check_dsn_enabled # GET /api/rules/version # Quick version check - returns latest updated_at timestamp def version current_sampling = HubLoad.current_sampling response.headers['X-Sample-Rate'] = current_sampling[:allowed_requests].to_s render json: { version: Rule.latest_version, count: Rule.active.count, sampling: current_sampling } end # GET /api/rules?since=1730646186 # Incremental sync - returns rules updated since timestamp (Unix timestamp in seconds) # GET /api/rules # Full sync - returns all active rules def index rules = if params[:since].present? # Incremental sync since_time = parse_timestamp(params[:since]) Rule.since(since_time) else # Full sync - only return enabled rules Rule.active.sync_order end current_sampling = HubLoad.current_sampling response.headers['X-Sample-Rate'] = current_sampling[:allowed_requests].to_s render json: { version: Rule.latest_version, sampling: current_sampling, rules: rules.map(&:to_agent_format) } rescue ArgumentError => e render json: { error: "Invalid timestamp format: #{e.message}" }, status: :bad_request end private def authenticate_dsn! @dsn = DsnAuthenticationService.authenticate(request) unless @dsn render json: { error: "Invalid DSN key" }, status: :unauthorized return end rescue DsnAuthenticationService::AuthenticationError => e render json: { error: e.message }, status: :unauthorized end def check_dsn_enabled unless @dsn.enabled? render json: { error: "DSN is disabled" }, status: :forbidden end end def parse_timestamp(timestamp_str) # Parse Unix timestamp in seconds unless timestamp_str.match?(/^\d+$/) raise ArgumentError, "Invalid timestamp format. Expected Unix timestamp in seconds (e.g., 1730646186)" end Time.at(timestamp_str.to_i) rescue ArgumentError => e raise ArgumentError, "Invalid timestamp format: #{e.message}. Use Unix timestamp in seconds (e.g., 1730646186)" end end end