Download the PHP package aimeos/pagible-webhooks without Composer

On this page you can find all versions of the php package aimeos/pagible-webhooks. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package pagible-webhooks

Pagible Webhooks

Signed, tenant-scoped webhook notifications for Pagible CMS lifecycle events, delivered through the Laravel queue. The core package doesn't need an event bus, an outbox or any webhook dependency.

This package is part of the Pagible CMS monorepo.

Installation

Enable webhooks and run a dedicated worker on an asynchronous queue connection:

The sync driver delivers immediately without a worker, but failed deliveries aren't retried. The null driver queues nothing and logs cms.webhook.delivery_blocked with the reason invalid_queue.

Configuration

Settings are in config/cms/webhooks.php:

Key Default Description
enabled false Turn webhooks on (CMS_WEBHOOKS_ENABLED)
queue.connection app default Queue connection (CMS_WEBHOOKS_QUEUE_CONNECTION)
queue.name cms-webhooks Queue name (CMS_WEBHOOKS_QUEUE)
timeout 10 Seconds to wait for the response
limit 25 Subscriptions per tenant
endpoints [] Operator endpoints for internal services
deny_cidrs [] IP addresses and CIDR ranges denied for all destinations, e.g. ['10.1.0.0/16', '10.2.0.5']

Lowering the limit doesn't delete existing subscriptions, but none can be added until the tenant is below the limit again. An invalid deny_cidrs entry blocks all deliveries until it's fixed.

Operations

A broken webhook configuration never stops the application, it only stops the affected deliveries. Run the check on every deploy. It lists each problem with the setting to fix and fails if there are any:

The command also reports:

Workers and timeouts

Each destination is a separate job and a slow endpoint occupies a worker for up to the HTTP timeout. Monitor queue depth, oldest job age and failed jobs, and add workers if scheduled publications create bursts.

Workers abort a delivery after timeout + 13 seconds for connecting, resolving the host name and recording the result (23 seconds by default). Set the queue's retry_after higher, otherwise running deliveries are released twice.

Resolving the host name uses up the response time, and a single DNS query can't be interrupted. Configure short resolver timeouts on the worker hosts, e.g. options timeout:1 attempts:2 in /etc/resolv.conf. Otherwise unresponsive name servers keep deliveries running until the worker aborts them without recording the failure.

Retries

Temporary failures pause all deliveries to that destination for 30 seconds, and consecutive ones for 2, 10 and then 30 minutes each:

A Retry-After header in seconds extends the pause, up to 30 minutes. After the pause, one delivery probes the destination while the others wait. Any other response, including 4xx, resets the backoff and resumes all deliveries. Responses like 400, 404 or 410 are terminal and never retried. Unexpected errors, e.g. database errors, go to the exception handler and are retried with the same delays.

Deliveries still queued after 24 hours are dropped. A delivery that would only resume after it expires comes back one minute before instead, and is sent if the pause was lifted in the meantime (e.g. by a successful test event), otherwise it's recorded as failed.

The pause state lives in the application cache, which must be shared by all servers and workers. Without it, deliveries are retried one by one.

Deliveries that can't be pushed to the queue, e.g. because the queue server is down, are lost. The error is reported to the exception handler and the subscriptions show "Queue unavailable".

Pagible doesn't limit the payload size, so the queue backend and the receivers must accept the encrypted queue messages and HTTP requests.

Log entries

delivery_retried, delivery_expired, delivery_blocked, endpoint_invalid, extra_dropped and extra_failed are logged at most once every 10 minutes for the same problem and destination:

Entry Logged when
cms.webhook A subscription was created, updated, secret_rotated or purged, with the editor, ID, status, events, endpoint and changed fields
cms.webhook.delivered An operator endpoint received a delivery (only if CMS_LOG_CHANNEL is set)
cms.webhook.delivery_retried A delivery failed temporarily, with the reason and HTTP status
cms.webhook.delivery_failed A delivery failed for good
cms.webhook.delivery_expired A delivery was queued for more than 24 hours, i.e. workers can't keep up or were stopped too long
cms.webhook.delivery_blocked Nothing was queued because of invalid_queue, queue_failed or invalid_policy
cms.webhook.endpoint_invalid An operator endpoint is misconfigured and was skipped
cms.webhook.extra_dropped Fields added by WebhookConfig::payload() exceeded 1 KB or couldn't be encoded as JSON for some changes and were left out, with the event name
cms.webhook.extra_failed The callback of WebhookConfig::payload() failed, with the event name, the exception class and the location where it was thrown, the event was sent without the fields

Subscriptions

Tenants manage their subscriptions in the admin panel or through GraphQL.

Health

After each attempt, last_success_at (updated at most once a minute) or last_error (reason, HTTP status and time) is updated. The admin panel shows the last error, paused subscriptions with the end of the pause (paused_until), and a warning above the list if webhooks are disabled or blocked (cmsWebhookServer in GraphQL). Response bodies aren't read, only the HTTP status counts. Health updates don't change updated_at.

Reason Meaning
http_error HTTP error status, 3xx is shown as "Redirects aren't followed"
timeout, connection_failed, resolution_failed, transport_error Network errors
response_headers_too_large The response headers are too large
destination_not_allowed The URL points to a denied address
invalid_policy Blocked because of an invalid deny_cidrs entry
queue_failed The delivery couldn't be pushed to the queue
invalid_encryption The secret can't be decrypted with the current keys
delivery_failed The queue worker failed

Application integrations

Managing subscriptions in code

Aimeos\Cms\WebhookManager offers the operations of the admin panel, e.g. to create a subscription for each new tenant. All methods work in the current tenant and require a user with the config:webhook permission. Without user (null), they throw a PermissionException unless they run inside Permission::system(), e.g. in a listener for new tenants:

save( $id, $events, $status, $user, $name ) changes the events, status and name, rotate( $id, $user ) returns a new secret, ping( $id, $user ) sends a test event and purge( $ids, $user ) deletes subscriptions.

Adding payload fields

Add fields of your application to the changes in the boot() method of a service provider, e.g. the public URL of a page or the ID of a related record. The callback receives all changes of an event at once, so bulk events need only one query, and returns the fields per change ID:

The fields are added as extra object to the data of each change, so they never collide with the fields of the CMS. Changes without fields have no extra key. Each delivery is queued as a separate job containing the whole payload and bulk events contain up to 1000 changes, so the fields of a change are limited to 1 KB as JSON. Larger ones and fields which can't be encoded as JSON, e.g. due to invalid UTF-8, are dropped and logged as cms.webhook.extra_dropped, the fields of the other changes are kept. Add IDs or short values receivers can look up instead of content, because queues limit the job size too, e.g. Amazon SQS to 256 KB. The callback runs after the database transaction is committed but before the deliveries are queued, so keep it fast. If it throws an exception, the event is delivered without the fields and the error is logged as cms.webhook.extra_failed. The callback runs in the tenant of the changes, so Tenancy::value() and the CMS models use the right tenant even in commands and queue workers. Limit queries of your own models to $change->tenant if they don't use the CMS tenancy. The fields are readable by everyone with the config:webhook permission, who can send the payloads to any URL, and by all operator endpoints, so only add fields these users are allowed to see in your application. All subscriptions of the tenant and all endpoints receive the same payload and webhook URLs usually point to other systems, so don't add confidential or personal data like names and e-mail addresses of editors or customers. WebhookConfig::payload( null ) removes the callback.

Reacting in the same application

To react to content changes within your Laravel application, listen to the CMS events directly instead of sending webhooks to yourself. Change::listen() of the core package passes the same changes, also from queued listeners, without HTTP requests and signatures:

Receiving webhooks

These events are sent:

The payload contains the event name, the tenant, the UTC event time (the same for all attempts) and the content and version IDs. Page events also contain the route if available:

The optional extra object contains the fields added by the application. Bulk events contain an ordered array of {id, version_id} references in data, page references also contain the lang of the page variant. Fetch the details through the CMS API if needed.

The page.deleted, page.restored and page.purged events tell by lang what has been affected: If lang is set, only the page variant in that language has been deleted, restored or purged. If lang is missing, the whole page including all its language variants has been affected.

With a Tenancy callback registered, events without a tenant are dropped, so don't use the default (empty) tenant ID for a site in multi-tenant installations.

Verifying requests

Requests are signed like Standard Webhooks, so their libraries can verify them:

Without a library, verify each request before processing it:

  1. Reject missing or malformed headers.
  2. Reject a webhook-timestamp outside your allowed clock skew, e.g. five minutes.
  3. Compute v1, + base64 of the HMAC-SHA256 over <webhook-id>.<webhook-timestamp>.<raw body>, keyed with the base64 decoded part of the secret after whsec_.
  4. Accept the request if any received signature matches in a constant-time comparison.
  5. Claim the webhook-id atomically and return 2xx for duplicates. Keep the claims for at least 24 hours plus the clock skew.

A Laravel receiver (in routes/api.php or excluded from CSRF verification):

Deliveries can arrive out of order, so treat each one as a hint and fetch the current state through the CMS API, or ignore events older than the last one processed for the same item. Return 2xx for events you don't handle, e.g. webhook.ping, so they aren't recorded as failed.

Destination security

Subscriptions Operator endpoints
Scheme and port HTTPS on port 443 HTTP or HTTPS on any port
Private and loopback addresses Blocked Allowed
Link-local (e.g. 169.254.169.254), multicast, transition and reserved addresses Blocked Blocked
deny_cidrs Blocked Blocked
Host names from /etc/hosts No Yes

For both, redirects and environment proxies are disabled. DNS is resolved right before each call and the allowed addresses are pinned in cURL, which falls back to the next one if a server is unreachable. The delivery is rejected if no allowed address remains.

Operator endpoints

Internal services such as search indexers or cache purgers are configured in config/cms/webhooks.php instead of the admin panel. They receive the events of all tenants, which the tenant_id of the payload tells apart:

Maintenance

Rotating APP_KEY

Secrets and queued deliveries are encrypted with the application key and aren't re-encrypted with a new one. Keep the old key in APP_PREVIOUS_KEYS so they can still be decrypted. If the key was rotated because it leaked, consider the secrets compromised too and let the tenants rotate them.

Once the old key is removed, deliveries still queued with it fail, and subscriptions whose secret wasn't rotated since fail without retries until the tenant rotates the secret or deletes them. The admin panel asks for the rotation and php artisan cms:webhooks:check reports the number of affected subscriptions.

Removing a tenant

Subscriptions are stored in the cms_webhooks table by tenant_id, so delete them together with the other data of the tenant. Queued deliveries of deleted subscriptions are dropped.

Admin translations

The webhook panel keeps its gettext sources in admin/i18n. Use $pgettext('webhooks', ...) for UI strings, then update and build the catalogs:

The compiled JSON catalogs are staged in the ignored admin/public/i18n directory and published to admin/dist/i18n with the panel bundle. Commit the PO sources and admin/dist, not the staging copies.

Admin tests

The component tests in admin/cypress run with the Cypress setup of the Pagible admin package, so they only work in the monorepo and its npm dependencies must be installed in ../admin first:

The tests also mount the committed admin/dist bundle and CI fails if it doesn't match the sources, so rebuild and commit it together with every change in admin/src or admin/i18n.


All versions of pagible-webhooks with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
ext-curl Version *
aimeos/pagible-core Version ~0.13
guzzlehttp/psr7 Version ^2.6||^3.0
symfony/http-foundation Version ^7.0||^8.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package aimeos/pagible-webhooks contains the following files

Loading the files please wait ...