Skip to content

Commit b39b5ad

Browse files
Patch76claude
andauthored
refactor(internal): collapse settings-UI route registration into one table (#1504)
* refactor(internal): collapse settings-UI route registration into one table The add-on and secret-prefix mounts registered the same ~24 routes twice (once plain for HA ingress, once under the secret prefix), so every new endpoint needed two edits and the two blocks could silently drift. Replace both blocks with a single route table mounted under each prefix via a small helper; the add-on-only root mount (`/` -> root_page) stays separate. No behaviour change: the (path, methods, handler) sequence registered in all four modes (add-on +/- secret, standalone +/- secret) is byte-identical to before, verified by capturing both. Net -105 lines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(internal): clarify route-mount comment after self-review The mount comment said routes are registered "under both prefixes", which overstates: the table is mounted at root in add-on mode and under the secret prefix when set — a deployment hits either, both, or neither. Reword to match the conditional behaviour. Comment-only; no code change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(internal): scope the route-table comment to this function (#1504 review) The comment claimed "Every settings-UI route …", but the sidecar-only `POST /api/settings/shutdown` is a settings-UI route registered in `stdio_settings_sidecar.py`, not this table. Scope the claim to "Every route this function mounts" so a future reader doesn't trust the table as the global route inventory. Comment-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 28dedf4 commit b39b5ad

1 file changed

Lines changed: 44 additions & 148 deletions

File tree

src/ha_mcp/settings_ui.py

Lines changed: 44 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -5994,162 +5994,58 @@ def register_settings_routes(
59945994
)
59955995
return
59965996

5997+
# Every route this function mounts except the add-on-only root mount is defined
5998+
# once in this table and mounted under each active prefix below: at root
5999+
# in add-on mode (so HA ingress can proxy localhost:9583/), and under the
6000+
# secret path when one is set (Docker / standalone direct access). A
6001+
# deployment hits either, both, or — guarded above — neither. Deriving
6002+
# the mounts from one table keeps them from drifting; the frontend uses
6003+
# relative fetches (./api/settings/...) so the handlers work at any prefix.
6004+
routes: list[tuple[str, list[str], str]] = [
6005+
("/settings", ["GET"], "settings_page"),
6006+
("/api/settings/tools", ["GET"], "get_tools"),
6007+
("/api/settings/tools", ["POST"], "save_tools"),
6008+
("/api/settings/restart", ["POST"], "restart_addon"),
6009+
("/api/settings/info", ["GET"], "settings_info"),
6010+
("/api/settings/features", ["GET"], "get_feature_flags"),
6011+
("/api/settings/features", ["POST"], "save_feature_flags"),
6012+
# Advanced settings endpoints
6013+
("/api/settings/advanced", ["GET"], "get_advanced_settings"),
6014+
("/api/settings/advanced", ["POST"], "save_advanced_settings"),
6015+
# Auto-backup endpoints (#1288)
6016+
("/api/settings/backups", ["GET"], "list_backups"),
6017+
("/api/settings/backups", ["DELETE"], "delete_backups_bulk"),
6018+
("/api/settings/backups/{name}", ["GET"], "view_backup"),
6019+
("/api/settings/backups/{name}/diff", ["GET"], "diff_backup"),
6020+
("/api/settings/backups/{name}/restore", ["POST"], "restore_backup"),
6021+
("/api/settings/backups/{name}", ["DELETE"], "delete_backup"),
6022+
("/api/settings/backup-config", ["GET"], "get_backup_config"),
6023+
("/api/settings/backup-config", ["POST"], "save_backup_config"),
6024+
# Tool security policies endpoints
6025+
("/api/policy/config", ["GET"], "policy_get_config"),
6026+
("/api/policy/config", ["PUT"], "policy_put_config"),
6027+
("/api/policy/pending", ["GET"], "policy_get_pending"),
6028+
("/api/policy/approve", ["POST"], "policy_post_approve"),
6029+
("/api/policy/deny", ["POST"], "policy_post_deny"),
6030+
("/api/policy/tool-schema", ["GET"], "policy_get_tool_schema"),
6031+
("/api/policy/value-source", ["GET"], "policy_get_value_source"),
6032+
]
6033+
6034+
def _mount(prefix: str) -> None:
6035+
for path, methods, handler_key in routes:
6036+
mcp.custom_route(f"{prefix}{path}", methods=methods)(handlers[handler_key])
6037+
59976038
if is_addon:
59986039
# Root mount lets HA ingress proxy localhost:9583/ → settings UI.
59996040
# Direct port 9583 LAN access also reaches these routes; in this
60006041
# respect they share the existing add-on networking model where
60016042
# port 9583 is exposed via host_network and the secret path is
60026043
# the auth for direct access. Document this in DOCS.md.
60036044
mcp.custom_route("/", methods=["GET"])(handlers["root_page"])
6004-
mcp.custom_route("/settings", methods=["GET"])(handlers["settings_page"])
6005-
mcp.custom_route("/api/settings/tools", methods=["GET"])(handlers["get_tools"])
6006-
mcp.custom_route("/api/settings/tools", methods=["POST"])(
6007-
handlers["save_tools"]
6008-
)
6009-
mcp.custom_route("/api/settings/restart", methods=["POST"])(
6010-
handlers["restart_addon"]
6011-
)
6012-
mcp.custom_route("/api/settings/info", methods=["GET"])(
6013-
handlers["settings_info"]
6014-
)
6015-
mcp.custom_route("/api/settings/features", methods=["GET"])(
6016-
handlers["get_feature_flags"]
6017-
)
6018-
mcp.custom_route("/api/settings/features", methods=["POST"])(
6019-
handlers["save_feature_flags"]
6020-
)
6021-
# Advanced settings endpoints
6022-
mcp.custom_route("/api/settings/advanced", methods=["GET"])(
6023-
handlers["get_advanced_settings"]
6024-
)
6025-
mcp.custom_route("/api/settings/advanced", methods=["POST"])(
6026-
handlers["save_advanced_settings"]
6027-
)
6028-
# Auto-backup endpoints (#1288)
6029-
mcp.custom_route("/api/settings/backups", methods=["GET"])(
6030-
handlers["list_backups"]
6031-
)
6032-
mcp.custom_route("/api/settings/backups", methods=["DELETE"])(
6033-
handlers["delete_backups_bulk"]
6034-
)
6035-
mcp.custom_route("/api/settings/backups/{name}", methods=["GET"])(
6036-
handlers["view_backup"]
6037-
)
6038-
mcp.custom_route("/api/settings/backups/{name}/diff", methods=["GET"])(
6039-
handlers["diff_backup"]
6040-
)
6041-
mcp.custom_route("/api/settings/backups/{name}/restore", methods=["POST"])(
6042-
handlers["restore_backup"]
6043-
)
6044-
mcp.custom_route("/api/settings/backups/{name}", methods=["DELETE"])(
6045-
handlers["delete_backup"]
6046-
)
6047-
mcp.custom_route("/api/settings/backup-config", methods=["GET"])(
6048-
handlers["get_backup_config"]
6049-
)
6050-
mcp.custom_route("/api/settings/backup-config", methods=["POST"])(
6051-
handlers["save_backup_config"]
6052-
)
6053-
# Tool security policies endpoints
6054-
mcp.custom_route("/api/policy/config", methods=["GET"])(
6055-
handlers["policy_get_config"]
6056-
)
6057-
mcp.custom_route("/api/policy/config", methods=["PUT"])(
6058-
handlers["policy_put_config"]
6059-
)
6060-
mcp.custom_route("/api/policy/pending", methods=["GET"])(
6061-
handlers["policy_get_pending"]
6062-
)
6063-
mcp.custom_route("/api/policy/approve", methods=["POST"])(
6064-
handlers["policy_post_approve"]
6065-
)
6066-
mcp.custom_route("/api/policy/deny", methods=["POST"])(
6067-
handlers["policy_post_deny"]
6068-
)
6069-
mcp.custom_route("/api/policy/tool-schema", methods=["GET"])(
6070-
handlers["policy_get_tool_schema"]
6071-
)
6072-
mcp.custom_route("/api/policy/value-source", methods=["GET"])(
6073-
handlers["policy_get_value_source"]
6074-
)
6045+
_mount("")
60756046

60766047
if secret_prefix:
60776048
# Mount under the MCP secret path so Docker / standalone clients
60786049
# need the same secret to reach the UI as they do for the MCP
6079-
# endpoint. The frontend uses relative fetches (./api/settings/...)
6080-
# so the JS works at either prefix unchanged.
6081-
mcp.custom_route(f"{secret_prefix}/settings", methods=["GET"])(
6082-
handlers["settings_page"]
6083-
)
6084-
mcp.custom_route(f"{secret_prefix}/api/settings/tools", methods=["GET"])(
6085-
handlers["get_tools"]
6086-
)
6087-
mcp.custom_route(f"{secret_prefix}/api/settings/tools", methods=["POST"])(
6088-
handlers["save_tools"]
6089-
)
6090-
mcp.custom_route(f"{secret_prefix}/api/settings/restart", methods=["POST"])(
6091-
handlers["restart_addon"]
6092-
)
6093-
mcp.custom_route(f"{secret_prefix}/api/settings/info", methods=["GET"])(
6094-
handlers["settings_info"]
6095-
)
6096-
mcp.custom_route(f"{secret_prefix}/api/settings/features", methods=["GET"])(
6097-
handlers["get_feature_flags"]
6098-
)
6099-
mcp.custom_route(f"{secret_prefix}/api/settings/features", methods=["POST"])(
6100-
handlers["save_feature_flags"]
6101-
)
6102-
# Advanced settings endpoints
6103-
mcp.custom_route(f"{secret_prefix}/api/settings/advanced", methods=["GET"])(
6104-
handlers["get_advanced_settings"]
6105-
)
6106-
mcp.custom_route(f"{secret_prefix}/api/settings/advanced", methods=["POST"])(
6107-
handlers["save_advanced_settings"]
6108-
)
6109-
# Auto-backup endpoints (#1288)
6110-
mcp.custom_route(f"{secret_prefix}/api/settings/backups", methods=["GET"])(
6111-
handlers["list_backups"]
6112-
)
6113-
mcp.custom_route(f"{secret_prefix}/api/settings/backups", methods=["DELETE"])(
6114-
handlers["delete_backups_bulk"]
6115-
)
6116-
mcp.custom_route(
6117-
f"{secret_prefix}/api/settings/backups/{{name}}", methods=["GET"]
6118-
)(handlers["view_backup"])
6119-
mcp.custom_route(
6120-
f"{secret_prefix}/api/settings/backups/{{name}}/diff", methods=["GET"]
6121-
)(handlers["diff_backup"])
6122-
mcp.custom_route(
6123-
f"{secret_prefix}/api/settings/backups/{{name}}/restore", methods=["POST"]
6124-
)(handlers["restore_backup"])
6125-
mcp.custom_route(
6126-
f"{secret_prefix}/api/settings/backups/{{name}}", methods=["DELETE"]
6127-
)(handlers["delete_backup"])
6128-
mcp.custom_route(
6129-
f"{secret_prefix}/api/settings/backup-config", methods=["GET"]
6130-
)(handlers["get_backup_config"])
6131-
mcp.custom_route(
6132-
f"{secret_prefix}/api/settings/backup-config", methods=["POST"]
6133-
)(handlers["save_backup_config"])
6134-
# Tool security policies endpoints
6135-
mcp.custom_route(f"{secret_prefix}/api/policy/config", methods=["GET"])(
6136-
handlers["policy_get_config"]
6137-
)
6138-
mcp.custom_route(f"{secret_prefix}/api/policy/config", methods=["PUT"])(
6139-
handlers["policy_put_config"]
6140-
)
6141-
mcp.custom_route(f"{secret_prefix}/api/policy/pending", methods=["GET"])(
6142-
handlers["policy_get_pending"]
6143-
)
6144-
mcp.custom_route(f"{secret_prefix}/api/policy/approve", methods=["POST"])(
6145-
handlers["policy_post_approve"]
6146-
)
6147-
mcp.custom_route(f"{secret_prefix}/api/policy/deny", methods=["POST"])(
6148-
handlers["policy_post_deny"]
6149-
)
6150-
mcp.custom_route(f"{secret_prefix}/api/policy/tool-schema", methods=["GET"])(
6151-
handlers["policy_get_tool_schema"]
6152-
)
6153-
mcp.custom_route(f"{secret_prefix}/api/policy/value-source", methods=["GET"])(
6154-
handlers["policy_get_value_source"]
6155-
)
6050+
# endpoint.
6051+
_mount(secret_prefix)

0 commit comments

Comments
 (0)