Skip to content

fix(webhooks): missing DatabaseManager delegation breaks all schedule webhook endpoints (WEBHOOK-001 facade gap) #647

Description

@AndriiPasternak31

Summary

routers/schedules.py and routers/webhooks.py call four methods on the db facade (DatabaseManager in src/backend/database.py) that are not delegated to the underlying ScheduleOperations instance. The methods live orphaned in src/backend/db/schedules.py:518-598; there is no __getattr__ proxy on DatabaseManager.

Effect: every POST /api/agents/{name}/schedules/{id}/webhook (and the GET / DELETE variants, plus POST /api/webhooks/{token}) returns 500 on a live stack with AttributeError: 'DatabaseManager' object has no attribute 'generate_webhook_token'. The webhook trigger feature is non-functional in production.

This was introduced in commit c630931d (#291, ~Nov 2025) and went undetected because integration tests don't run in CI.

Reproduction (against a live stack)

TOKEN=$(curl -s -X POST http://localhost:8000/api/token -d 'username=admin&password=...' | jq -r .access_token)
AGENT="test-webhook-$(uuidgen | head -c 8)"

# Create agent and schedule (works)
curl -s -X POST http://localhost:8000/api/agents -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d "{\"name\":\"$AGENT\"}"
SID=$(curl -s -X POST "http://localhost:8000/api/agents/$AGENT/schedules" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"x","cron_expression":"0 0 1 1 *","message":"noop","enabled":true,"timezone":"UTC"}' | jq -r .id)

# Generate webhook token — FAILS with 500
curl -s -X POST "http://localhost:8000/api/agents/$AGENT/schedules/$SID/webhook" -H "Authorization: Bearer $TOKEN"
# => {"detail":"Internal Server Error"}
# Backend logs:
#   AttributeError: 'DatabaseManager' object has no attribute 'generate_webhook_token'

Affected callers

File:Line Call Method on ScheduleOperations
routers/schedules.py:493 db.generate_webhook_token(schedule_id) db/schedules.py:518
routers/schedules.py:522 db.get_webhook_status(schedule_id) db/schedules.py:578
routers/schedules.py:551 db.revoke_webhook_token(schedule_id) db/schedules.py:566
routers/webhooks.py:259 db.get_schedule_by_webhook_token(token) db/schedules.py:541

All four method bodies exist and are presumably correct (their unit-level callers work in the SQLite layer); only the facade delegation is missing.

Recommended fix

Add four pass-through methods to DatabaseManager in src/backend/database.py, in the existing "Schedule Management" section near line 705:

# Webhook token management (WEBHOOK-001, #291)
def generate_webhook_token(self, schedule_id: str):
    return self._schedule_ops.generate_webhook_token(schedule_id)

def get_schedule_by_webhook_token(self, token: str):
    return self._schedule_ops.get_schedule_by_webhook_token(token)

def revoke_webhook_token(self, schedule_id: str):
    return self._schedule_ops.revoke_webhook_token(schedule_id)

def get_webhook_status(self, schedule_id: str):
    return self._schedule_ops.get_webhook_status(schedule_id)

12 lines total, no logic changes.

Acceptance criteria

  • All four delegation methods added on DatabaseManager
  • tests/integration/test_webhook_rate_limit.py::test_webhook_rate_limit_returns_429_after_threshold (added in PR fix(security): lock down Redis — auth + ACL + network split (#589) #643) passes against a live stack
  • Manual reproduction above no longer 500s — returns 200 with a webhook_url field
  • Add a unit test that asserts every db.<method>(...) call site in routers/ and services/ resolves to a real method on DatabaseManager (lint-style guard against regression — would have caught this in CI)

Severity

HIGH — functional regression, not security. Rate limiter / ACL / network isolation are all sound; the feature is just unreachable.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions