Espone API JSON e MCP HTTP per far lavorare gli agenti sul CRM.
Gli agenti autenticati con token Bearer possono leggere today/pipeline e annotare attività, con host MCP allineati a quelli di produzione. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
class ApiToken < ApplicationRecord
|
||||
PREFIX = "crm_"
|
||||
|
||||
belongs_to :user
|
||||
|
||||
attr_accessor :plaintext
|
||||
|
||||
validates :name, presence: true
|
||||
validates :token_digest, presence: true, uniqueness: true
|
||||
validates :token_prefix, presence: true
|
||||
|
||||
scope :active, -> { where(revoked_at: nil) }
|
||||
|
||||
def self.digest(token)
|
||||
Digest::SHA256.hexdigest(token.to_s)
|
||||
end
|
||||
|
||||
def self.authenticate(plaintext)
|
||||
return if plaintext.blank?
|
||||
|
||||
token = active.find_by(token_digest: digest(plaintext))
|
||||
return unless token&.user&.active?
|
||||
|
||||
token
|
||||
end
|
||||
|
||||
def self.issue!(user:, name:)
|
||||
raw = "#{PREFIX}#{SecureRandom.urlsafe_base64(32)}"
|
||||
record = create!(
|
||||
user: user,
|
||||
name: name.to_s.strip.presence || "Agente",
|
||||
token_digest: digest(raw),
|
||||
token_prefix: raw[0, 8]
|
||||
)
|
||||
record.plaintext = raw
|
||||
record
|
||||
end
|
||||
|
||||
def revoked?
|
||||
revoked_at.present?
|
||||
end
|
||||
|
||||
def revoke!
|
||||
update!(revoked_at: Time.current)
|
||||
end
|
||||
|
||||
def touch_last_used!
|
||||
update_column(:last_used_at, Time.current)
|
||||
end
|
||||
|
||||
def masked
|
||||
"#{token_prefix}…"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user