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:
@@ -7,5 +7,6 @@ class AdminController < ApplicationController
|
||||
@active_users_count = User.active.count
|
||||
@projects_count = Project.count
|
||||
@mail_identities_count = MailIdentity.count
|
||||
@api_tokens_count = current_user.api_tokens.active.count
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
module Api
|
||||
class BaseController < ActionController::API
|
||||
wrap_parameters false
|
||||
before_action :authenticate_api_token!
|
||||
|
||||
attr_reader :current_user, :current_api_token
|
||||
|
||||
private
|
||||
|
||||
def authenticate_api_token!
|
||||
token = ApiToken.authenticate(bearer_token)
|
||||
unless token
|
||||
render json: { error: "Non autenticato" }, status: :unauthorized
|
||||
return
|
||||
end
|
||||
|
||||
@current_api_token = token
|
||||
@current_user = token.user
|
||||
Current.user = @current_user
|
||||
token.touch_last_used!
|
||||
end
|
||||
|
||||
def bearer_token
|
||||
header = request.authorization.to_s.presence || request.headers["Authorization"].to_s
|
||||
header[/Bearer\s+(.+)/i, 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
module Api
|
||||
module V1
|
||||
class ActivitiesController < BaseController
|
||||
def create
|
||||
render_agent Crm::AgentSession.new(current_user).create_activity(params[:project_code], activity_params)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def activity_params
|
||||
source = params[:activity].presence || params
|
||||
source.permit(:organization_id, :activity_type, :subject, :description, :happened_at, :contact_id, :opportunity_id, :lost_reason)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
module Api
|
||||
module V1
|
||||
class BaseController < Api::BaseController
|
||||
private
|
||||
|
||||
def render_agent(result)
|
||||
render json: result.body, status: result.status
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
module Api
|
||||
module V1
|
||||
class OpportunitiesController < BaseController
|
||||
def update_stage
|
||||
source = params[:opportunity].presence || params
|
||||
render_agent Crm::AgentSession.new(current_user).update_opportunity_stage(
|
||||
params[:project_code],
|
||||
params[:id],
|
||||
pipeline_stage: source[:pipeline_stage],
|
||||
lost_reason: source[:lost_reason],
|
||||
notes: source[:notes]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Api
|
||||
module V1
|
||||
class OrganizationsController < BaseController
|
||||
def show
|
||||
render_agent Crm::AgentSession.new(current_user).organization(params[:project_code], params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Api
|
||||
module V1
|
||||
class ProjectsController < BaseController
|
||||
def index
|
||||
render_agent Crm::AgentSession.new(current_user).projects
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Api
|
||||
module V1
|
||||
class SearchController < BaseController
|
||||
def show
|
||||
render_agent Crm::AgentSession.new(current_user).search(params[:project_code], params[:q])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
module Api
|
||||
module V1
|
||||
class TasksController < BaseController
|
||||
def create
|
||||
render_agent Crm::AgentSession.new(current_user).create_task(params[:project_code], task_params)
|
||||
end
|
||||
|
||||
def complete
|
||||
render_agent Crm::AgentSession.new(current_user).complete_task(params[:project_code], params[:id])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def task_params
|
||||
source = params[:task].presence || params
|
||||
source.permit(
|
||||
:title, :description, :organization_id, :contact_id, :opportunity_id,
|
||||
:assigned_user_id, :due_at, :priority, :task_type
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Api
|
||||
module V1
|
||||
class TodayController < BaseController
|
||||
def show
|
||||
render_agent Crm::AgentSession.new(current_user).today(params[:project_code])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
class ApiTokensController < ApplicationController
|
||||
before_action :set_api_token, only: :destroy
|
||||
|
||||
def index
|
||||
@page_title = "Token API"
|
||||
@api_tokens = current_user.api_tokens.active.order(created_at: :desc)
|
||||
@revealed_token = session.delete(:revealed_api_token)
|
||||
end
|
||||
|
||||
def create
|
||||
token = ApiToken.issue!(user: current_user, name: params[:name])
|
||||
session[:revealed_api_token] = token.plaintext
|
||||
redirect_to api_tokens_path, notice: "Token creato. Copialo ora: non sarà più visibile."
|
||||
end
|
||||
|
||||
def destroy
|
||||
@api_token.revoke!
|
||||
redirect_to api_tokens_path, notice: "Token revocato."
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_api_token
|
||||
@api_token = current_user.api_tokens.active.find(params[:id])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
class McpController < Api::BaseController
|
||||
def handle
|
||||
request.body.rewind if request.body.respond_to?(:rewind)
|
||||
|
||||
server = EminuxCrmMcp.server_for(user: current_user)
|
||||
transport = MCP::Server::Transports::StreamableHTTPTransport.new(
|
||||
server,
|
||||
stateless: true,
|
||||
serve_subscriptions_listen: false,
|
||||
enable_json_response: true,
|
||||
allowed_hosts: EminuxCrmMcp.allowed_hosts,
|
||||
dns_rebinding_protection: !Rails.env.test?
|
||||
)
|
||||
|
||||
status, headers, body = transport.handle_request(request)
|
||||
headers.each { |key, value| response.set_header(key, value) }
|
||||
self.status = status
|
||||
self.response_body = body
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user