feat: add communication history push, invoice auto-trigger, health checks, rate limiting
- Override account.move action_post to auto-push invoice PDF to WC on posting - Add _cron_health_check to ping all instances and flag unreachable ones - Add health check cron record (every 10 minutes) to data/cron.xml - Add IP-based rate limiting (100 req/min) to webhook controller - Fill in API endpoints: order/documents, order/status, order/messages with real data from woo.order, stock.picking, and mail.message - Implement return/create API endpoint with product mapping and line creation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request, Response
|
||||
@@ -21,6 +23,25 @@ def _normalize_url(url):
|
||||
class WooWebhookController(http.Controller):
|
||||
"""Receive inbound WooCommerce webhook deliveries."""
|
||||
|
||||
# Simple in-memory rate limiter: {ip: [(timestamp, ...),]}
|
||||
_rate_tracker = defaultdict(list)
|
||||
_RATE_LIMIT = 100 # max requests per minute
|
||||
_RATE_WINDOW = 60 # seconds
|
||||
|
||||
@classmethod
|
||||
def _check_rate_limit(cls, ip):
|
||||
"""Return True if the IP is within rate limits, False if exceeded."""
|
||||
now = time.time()
|
||||
cutoff = now - cls._RATE_WINDOW
|
||||
# Clean old entries
|
||||
cls._rate_tracker[ip] = [
|
||||
ts for ts in cls._rate_tracker[ip] if ts > cutoff
|
||||
]
|
||||
if len(cls._rate_tracker[ip]) >= cls._RATE_LIMIT:
|
||||
return False
|
||||
cls._rate_tracker[ip].append(now)
|
||||
return True
|
||||
|
||||
def _find_instance(self, source_url):
|
||||
"""Find a woo.instance matching the webhook source URL."""
|
||||
instances = request.env['woo.instance'].sudo().search([
|
||||
@@ -40,12 +61,19 @@ class WooWebhookController(http.Controller):
|
||||
def _verify_and_dispatch(self, dispatch_method):
|
||||
"""
|
||||
Common handler for all webhook endpoints.
|
||||
- Rate limits by IP
|
||||
- Detects ping
|
||||
- Finds instance by source URL
|
||||
- Verifies HMAC signature
|
||||
- Delegates to dispatch_method(instance, data)
|
||||
Returns a Response.
|
||||
"""
|
||||
# Rate limiting
|
||||
remote_ip = request.httprequest.remote_addr or 'unknown'
|
||||
if not self._check_rate_limit(remote_ip):
|
||||
_logger.warning("Rate limit exceeded for IP %s", remote_ip)
|
||||
return Response('Too Many Requests', status=429)
|
||||
|
||||
headers = request.httprequest.headers
|
||||
topic = headers.get('X-WC-Webhook-Topic', '')
|
||||
source_url = headers.get('X-WC-Webhook-Source', '')
|
||||
@@ -115,7 +143,6 @@ class WooWebhookController(http.Controller):
|
||||
"WooCommerce order webhook received. Instance: %s, Topic: %s, Order ID: %s",
|
||||
instance.name, topic, data.get('id'),
|
||||
)
|
||||
# Delegate to model — will be implemented in later tasks
|
||||
if hasattr(instance, '_sync_order_from_wc'):
|
||||
instance._sync_order_from_wc(data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user