Connection Widget¶
The connection widget is a self-contained Vue 3 custom element (<conflux-config>) that merchants embed on their own site to configure a sync connection — choosing the sync direction, data types, frequency, and any other settings the integration exposes. It communicates with Conflux over a token-authenticated API; Conflux renders nothing server-side.
Why a custom element¶
Embedding a widget on an arbitrary merchant site means working inside markup Conflux does not control: different CSS frameworks, CSPs, and frontend stacks. A native web component built with defineCustomElement encapsulates its own shadow DOM styles and has no runtime dependency on any framework the host page might (or might not) provide. The merchant drops in one <script> tag and one HTML element — no npm install, no bundler config.
Two initiation flows¶
A connection can be established from either side, and the widget is relevant to both.
Flow A — plugin-initiated. The merchant installs the e-commerce plugin (WooCommerce, PrestaShop, etc.) on their own site. The plugin calls POST /api/system-connections/register with its credentials, Conflux verifies them, and — once verification succeeds — issues a widget_token back to the plugin. The plugin stores this token and embeds the <conflux-config> element with it so the merchant can complete the configuration without ever visiting the Conflux admin panel.
Flow B — tenant-initiated. A Conflux administrator creates the connection from inside the Filament admin panel. The panel shows a preview of the widget rendered using a short-lived admin-preview token; the merchant may later be directed to a hosted page that embeds the widget with their own token.
In both cases the widget itself is identical — the flow only differs in how the token was obtained and how the element was placed on the page.
Token authentication¶
Widget routes (/api/widget/*) are not behind Sanctum. They use AuthenticateWidgetToken middleware exclusively.
The middleware:
- Reads the
Authorization: Bearer <plaintext-token>header. - Hashes the plaintext with SHA-256 and queries
system_connection_access_tokens.token. - Resolves the matching
SystemConnectionand binds it onto the request aswidget_connection. - Updates
last_used_aton every successful request.
Tokens are long-lived and hash-stored, following the same one-way storage pattern as connection tokens — see System Connection Tokens. The name column records the token's origin context: plugin-widget for tokens issued during plugin-initiated registration, admin-preview for tokens created by the admin panel.
Schema-to-form lifecycle¶
The widget drives its form entirely from the schema returned by the API — it contains no hard-coded field definitions.
Fetch (GET /api/widget/configuration)
The controller resolves the SystemConnection's associated System, then resolves the plugin via PluginRegistry. If the plugin implements HasConnectionConfiguration, it calls connectionConfigurationSchema()->toArray() and returns:
{
"schema": {
"fields": [
{ "type": "select", "name": "direction", "label": "Sync direction", "required": true, "options": [...] },
{ "type": "multiselect", "name": "data_types", "label": "Data to sync", "required": true, "options": [...] },
{ "type": "radio", "name": "frequency", "label": "Sync frequency", "required": true, "options": [...] }
]
},
"configuration": {
"direction": "inbound",
"data_types": ["customers", "orders"],
"frequency": "daily"
}
}
The widget iterates schema.fields and renders the appropriate input for each type. Current values come from configuration and pre-populate the form.
If the plugin does not implement HasConnectionConfiguration, schema is null and the widget shows "No configuration available for this connection." — this is a valid state, not an error.
Save (PATCH /api/widget/configuration)
The widget sends { "configuration": { "field_name": "value", ... } }. The controller writes the submitted values into system_connections.configuration (a JSON column cast to array) and returns the persisted values.
The same request then queues App\Jobs\GenerateDefaultSyncRulesJob to re-derive the tenant's default SyncRules, provided the connection is active — so a shop owner who switches a data type back on starts syncing it once the job runs, without waiting for a re-activation. Generation is idempotent, so the job is queued on every save rather than only when data_types changed. The response is not held up for it, and a failure is not swallowed: it propagates out of the job so the queue retries it, landing in failed_jobs if retries are exhausted, rather than being lost silently. See Sync Rules — Rules and the opt-in are two gates.
Default configuration¶
When a SystemConnection is first created, SystemConnectionObserver::creating() checks whether the resolved plugin implements HasConnectionConfiguration. If it does, it writes defaultConnectionConfiguration() into configuration before the record is inserted. The widget therefore always opens with sensible values pre-selected rather than an empty form.
Source files¶
| File | Purpose |
|---|---|
resources/widget/ConfluxConfig.vue |
Vue 3 custom element component — template, fetch/save logic, scoped styles |
resources/widget/main.js |
Entry point; registers <conflux-config> via customElements.define |
vite.widget.config.js |
Vite build config; outputs public/widget.js |
app/Http/Controllers/Api/Widget/WidgetConfigurationController.php |
Handles GET and PATCH /api/widget/configuration |
app/Http/Middleware/AuthenticateWidgetToken.php |
Token hashing and connection binding |
routes/api.php |
Route group under /api/widget/ with AuthenticateWidgetToken middleware |
app/Filament/Resources/SystemConnections/Pages/ConfigureSystemConnectionWidget.php |
Admin panel preview page |
See also¶
- Embed the Widget — how to place the widget on an external site
- Implement Connection Config — how to add configuration fields to a new plugin
- Connection Config Fields reference — all field types and their serialized shapes
- Connection Flow — where the widget token is issued in the registration handshake
- System Connection Tokens — token hashing and storage