Files
archived-hc-harbor/app/models/wakatime_mirror.rb
2025-05-13 11:23:10 -04:00

62 lines
1.9 KiB
Ruby

class WakatimeMirror < ApplicationRecord
belongs_to :user
has_many :heartbeats, through: :user
encrypts :encrypted_api_key, deterministic: false
validates :endpoint_url, presence: true
validates :encrypted_api_key, presence: true
validates :endpoint_url, uniqueness: { scope: :user_id }
validate :endpoint_url_not_hackatime
after_create :schedule_initial_sync
def unsynced_heartbeats
# Get heartbeats since last sync, or all heartbeats if never synced
user.heartbeats.where("created_at > ?", last_synced_at || Time.at(0))
end
def sync_heartbeats
return unless encrypted_api_key.present?
# Get the next batch of heartbeats to sync (max 25 per WakaTime API limit)
batch = unsynced_heartbeats.limit(25).to_a
return if batch.empty?
# Send them all in a single request using the bulk endpoint
begin
body = batch.map { |h| h.attributes.slice(*Heartbeat.indexed_attributes) }
response = HTTP.headers(
"Authorization" => "Basic #{Base64.strict_encode64(encrypted_api_key + ':')}",
"Content-Type" => "application/json"
).post(
"#{endpoint_url}/users/current/heartbeats.bulk",
json: body
)
if response.status.success?
update_column(:last_synced_at, Time.current)
puts "Successfully synced #{batch.size} heartbeats: #{response.body}"
# queue another sync job
WakatimeMirrorSyncJob.perform_later(self)
else
Rails.logger.error("Failed to sync heartbeats to #{endpoint_url}: #{response.body}")
end
rescue => e
Rails.logger.error("Error syncing heartbeats to #{endpoint_url}: #{e.message}")
end
end
private
def endpoint_url_not_hackatime
if endpoint_url.present? && endpoint_url.include?("hackatime.hackclub.com")
errors.add(:endpoint_url, "cannot be hackatime.hackclub.com")
end
end
def schedule_initial_sync
WakatimeMirrorSyncJob.perform_later(self)
end
end