dd_area_maintenance_api
See also: JSON API v1 · dd_utils_api · dispatch
The maintenance area's server surface: run a maintenance widget's method, load a panel's value, and administer component locks.
Registered actions (src/core/api/handlers/dd_area_maintenance_api.ts): widget_request, get_widget_value, lock_components_actions. Widget dispatch and the widget catalog live in src/core/area_maintenance/widgets/registry.ts; the widget modules sit alongside it.
How to call
- POST JSON to
/api/v1/json(or the client-relative/dedalo/core/api/v1/json) withdd_api: "dd_area_maintenance_api"andaction: "<method>".
Notes
- All three actions are global-admin only. The maintenance area is an admin surface and the gate runs before anything else; a non-admin gets
perm.denied(HTTP 403). - The dashboard is a widget framework: each panel is one widget, run through
widget_request(execute) orget_widget_value(load). There is no generic class dispatcher — a maintenance operation is always a method of a named widget, and only the methods that widget's module registers are reachable. - Envelope: v2. A widget answers with its payload; the handler is the one site that turns it into a body, so success is
{ ok: true, request_id, data, … }wheredatais the widget's value. The names the maintenance panels read by their own name —msg,errors, and each widget's own extras — ride at the top level as extension keys, and an absentmsgor an emptyerrorsemits no key at all. A refusal is{ ok: false, request_id, error: { code, category, message, label_key, retryable } }. There is noresultkey; the v1{ result, msg, errors }shape was removed on 2026-08-16.
Errors (all actions)
| code | status | when |
|---|---|---|
perm.denied |
403 | the caller is not a global admin. |
request.invalid_options |
400 | options is present and is not an object (widget_request). |
maintenance.widget_unknown |
400 | source.model names no widget in the catalog (or is not a valid identifier). |
maintenance.widget_unavailable |
503 | the widget exists but deliberately answers no panel-load (get_widget_value). |
tool.method_not_allowed |
400 | the method is not registered by that widget's module. Resolution is an own-property lookup with a function guard, so an inherited Object member can never reflect as a handler. |
maintenance.invalid_fn_action |
400 | lock_components_actions with an fn_action outside the two supported values. |
widget_request
Purpose
Execute a maintenance widget's method.
Accepts
source: object (required)model: string (required) — the widget id (e.g.database_info,counters_status,dataframe_control).action: string (required) — the method the widget's module registers.
options: object (optional) — the method's arguments.
Returns
data is the widget method's payload. Counter administration rides this same channel — it is the modify_counter method of the counters_status widget (source.model: "counters_status", source.action: "modify_counter"), not an action of its own.
Example request
{
"dd_api": "dd_area_maintenance_api",
"action": "widget_request",
"source": { "model": "dataframe_control", "action": "run_check" },
"options": {}
}
Example response
{
"ok": true,
"request_id": "c0ffee80",
"data": { "checked": 24, "orphans": [] }
}
get_widget_value
Purpose
Load a panel's value — always the widget's own getValue.
Accepts
source: object (required)model: string (required) — the widget id.
Returns
data is the panel value.
Warning
A widget may deliberately register no panel load. dataframe_control is the case in point: its scan walks every matrix table end to end, so on a large install a single panel open would be minutes of database work. It is reachable only through widget_request (run_check / run_fix), and get_widget_value on it answers maintenance.widget_unavailable.
Example request
{
"dd_api": "dd_area_maintenance_api",
"action": "get_widget_value",
"source": { "model": "database_info" }
}
lock_components_actions
Purpose
Area-level administration of component locks: list the users currently holding one, or force-release locks.
Accepts
options: object (required)fn_action: string (required) —get_active_usersorforce_unlock_all_components.user_id: int (optional,force_unlock_all_componentsonly) — release one user's locks. Absent or empty releases all users' locks.
Returns
get_active_users—datais the read's own boolean status; the live lock map rides as the top-levelar_user_actionsextension key, which is where the panel reads it. Each entry carriesuser_id,full_username,component_tipo,component_model,component_label,section_tipo,section_label,section_id(an int) anddate.force_unlock_all_components—dataistrue,msgnarrates the outcome, andfreed(an extension key) counts the released locks.
Per-component focus/blur soft-locks are not here: they are dd_utils_api::update_lock_components_state / get_lock_status.
Example request
{
"dd_api": "dd_area_maintenance_api",
"action": "lock_components_actions",
"options": { "fn_action": "get_active_users" }
}
Example response
{
"ok": true,
"request_id": "c0ffee81",
"data": true,
"ar_user_actions": [
{
"user_id": 1,
"full_username": "Root",
"component_tipo": "oh16",
"component_model": "component_input_text",
"component_label": "Title",
"section_tipo": "oh1",
"section_label": "Oral History",
"section_id": 368,
"date": "2026-08-24 10:30:00"
}
]
}
Note
The lock key store keeps section_id as text — a named exemption of the int-canonical rule, because the key is a composite shared with the previous engine's own event store. It is canonicalized to an int on the way out, so what reaches the wire is always a record address.