Login/register pages now correctly unwrap API response {data: {token, user}}.
Cleaned up leftover apiFetch code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
83 lines
3.1 KiB
Python
83 lines
3.1 KiB
Python
# Task Team Connector — Inbound webhook controller
|
|
import hashlib
|
|
import hmac
|
|
import logging
|
|
|
|
from odoo import http
|
|
from odoo.http import request
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TaskTeamWebhookController(http.Controller):
|
|
|
|
@http.route(
|
|
'/task_team/webhook/sync',
|
|
type='json',
|
|
auth='public',
|
|
methods=['POST'],
|
|
csrf=False,
|
|
)
|
|
def sync_webhook(self, **kwargs):
|
|
"""Receive task-sync events from Task Team API."""
|
|
ICP = request.env['ir.config_parameter'].sudo()
|
|
secret = ICP.get_param('task_team_connector.webhook_secret', '')
|
|
|
|
# HMAC-SHA256 signature verification (when secret is configured)
|
|
if secret:
|
|
signature = request.httprequest.headers.get('X-Webhook-Signature', '')
|
|
body = request.httprequest.get_data()
|
|
expected = 'sha256=' + hmac.new(
|
|
secret.encode(), body, hashlib.sha256
|
|
).hexdigest()
|
|
if not hmac.compare_digest(expected, signature):
|
|
_logger.warning('Task Team webhook: invalid HMAC signature')
|
|
return {'error': 'invalid_signature', 'code': 401}
|
|
|
|
data = request.get_json_data()
|
|
event = data.get('event', 'task_updated')
|
|
_logger.info('Task Team inbound webhook: event=%s', event)
|
|
|
|
if event in ('task_created', 'task_updated'):
|
|
task_data = data.get('task') or data
|
|
request.env['project.task'].sudo().tt_upsert_from_webhook(task_data)
|
|
return {'status': 'ok', 'event': event}
|
|
|
|
if event == 'task_deleted':
|
|
tt_id = data.get('id') or data.get('tt_task_id')
|
|
if tt_id:
|
|
task = request.env['project.task'].sudo().search(
|
|
[('tt_task_id', '=', tt_id)], limit=1
|
|
)
|
|
if task:
|
|
task.with_context(tt_no_sync=True).write({'active': False})
|
|
return {'status': 'ok', 'event': event}
|
|
|
|
if event == 'task_completed':
|
|
# Mark the Odoo task stage as closed if possible
|
|
tt_id = data.get('tt_task_id')
|
|
if tt_id:
|
|
task = request.env['project.task'].sudo().search(
|
|
[('tt_task_id', '=', tt_id)], limit=1
|
|
)
|
|
if task and task.project_id:
|
|
closed_stage = request.env['project.task.type'].sudo().search(
|
|
[
|
|
('project_ids', 'in', task.project_id.id),
|
|
('is_closed', '=', True),
|
|
],
|
|
limit=1,
|
|
)
|
|
if closed_stage:
|
|
task.with_context(tt_no_sync=True).write(
|
|
{'stage_id': closed_stage.id}
|
|
)
|
|
return {'status': 'ok', 'event': event}
|
|
|
|
_logger.debug('Task Team webhook: unhandled event type %s', event)
|
|
return {'status': 'ok', 'event': event, 'note': 'unhandled'}
|
|
|
|
@http.route('/task_team/webhook/health', type='json', auth='public', methods=['GET'])
|
|
def health(self, **kwargs):
|
|
return {'status': 'ok', 'module': 'task_team_connector'}
|