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
|
||||
@@ -9,6 +9,7 @@ class User < ApplicationRecord
|
||||
has_many :activities, dependent: :nullify
|
||||
has_many :user_projects, dependent: :destroy
|
||||
has_many :projects, through: :user_projects
|
||||
has_many :api_tokens, dependent: :destroy
|
||||
|
||||
ROLES = %w[admin user].freeze
|
||||
|
||||
|
||||
Reference in New Issue
Block a user