M2-T01: add config JSON API (GET/PUT /api/config)
- new app/api/routes/api/ package with shared require_session (401) and require_csrf (presence-only X-CSRF-Token, 403) dependencies - GET /api/config returns masked config sections; PUT /api/config reuses save_config_updates (blank secret keeps old; invalid -> 422, no write) - session-protected; PUT also CSRF-protected - register router in app/main.py; regenerate openapi/ - tests/test_api_config.py
This commit is contained in:
@@ -270,6 +270,86 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/config": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"api-config"
|
||||
],
|
||||
"summary": "Get Config",
|
||||
"description": "Return all configuration sections. Secret field values are masked (empty string).",
|
||||
"operationId": "get_config_api_config_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ConfigResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"api-config"
|
||||
],
|
||||
"summary": "Put Config",
|
||||
"description": "Save configuration updates.\n\n- Blank secret value keeps the existing stored value (no change).\n- Invalid values return 422 and nothing is written to the database.",
|
||||
"operationId": "put_config_api_config_put",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-CSRF-Token",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "X-Csrf-Token"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ConfigUpdateRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ConfigUpdateResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/homeassistant/publish": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -472,6 +552,114 @@
|
||||
],
|
||||
"title": "Body_logout_logout_post"
|
||||
},
|
||||
"ConfigField": {
|
||||
"properties": {
|
||||
"env_name": {
|
||||
"type": "string",
|
||||
"title": "Env Name"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"title": "Label"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"title": "Value"
|
||||
},
|
||||
"secret": {
|
||||
"type": "boolean",
|
||||
"title": "Secret"
|
||||
},
|
||||
"input_type": {
|
||||
"type": "string",
|
||||
"title": "Input Type"
|
||||
},
|
||||
"configured": {
|
||||
"type": "boolean",
|
||||
"title": "Configured"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"env_name",
|
||||
"label",
|
||||
"value",
|
||||
"secret",
|
||||
"input_type",
|
||||
"configured"
|
||||
],
|
||||
"title": "ConfigField"
|
||||
},
|
||||
"ConfigResponse": {
|
||||
"properties": {
|
||||
"sections": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ConfigSection"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Sections"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"sections"
|
||||
],
|
||||
"title": "ConfigResponse"
|
||||
},
|
||||
"ConfigSection": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name"
|
||||
},
|
||||
"fields": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ConfigField"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Fields"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"fields"
|
||||
],
|
||||
"title": "ConfigSection"
|
||||
},
|
||||
"ConfigUpdateRequest": {
|
||||
"properties": {
|
||||
"updates": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object",
|
||||
"title": "Updates"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"updates"
|
||||
],
|
||||
"title": "ConfigUpdateRequest",
|
||||
"description": "Flat mapping of env_name → value, mirroring the existing form semantics."
|
||||
},
|
||||
"ConfigUpdateResponse": {
|
||||
"properties": {
|
||||
"sections": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ConfigSection"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Sections"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"sections"
|
||||
],
|
||||
"title": "ConfigUpdateResponse"
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
|
||||
@@ -168,6 +168,60 @@ paths:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/api/config:
|
||||
get:
|
||||
tags:
|
||||
- api-config
|
||||
summary: Get Config
|
||||
description: Return all configuration sections. Secret field values are masked
|
||||
(empty string).
|
||||
operationId: get_config_api_config_get
|
||||
responses:
|
||||
'200':
|
||||
description: Successful Response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ConfigResponse'
|
||||
put:
|
||||
tags:
|
||||
- api-config
|
||||
summary: Put Config
|
||||
description: 'Save configuration updates.
|
||||
|
||||
|
||||
- Blank secret value keeps the existing stored value (no change).
|
||||
|
||||
- Invalid values return 422 and nothing is written to the database.'
|
||||
operationId: put_config_api_config_put
|
||||
parameters:
|
||||
- name: X-CSRF-Token
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
title: X-Csrf-Token
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ConfigUpdateRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Successful Response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ConfigUpdateResponse'
|
||||
'422':
|
||||
description: Validation Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HTTPValidationError'
|
||||
/homeassistant/publish:
|
||||
post:
|
||||
tags:
|
||||
@@ -302,6 +356,84 @@ components:
|
||||
required:
|
||||
- csrf_token
|
||||
title: Body_logout_logout_post
|
||||
ConfigField:
|
||||
properties:
|
||||
env_name:
|
||||
type: string
|
||||
title: Env Name
|
||||
label:
|
||||
type: string
|
||||
title: Label
|
||||
value:
|
||||
type: string
|
||||
title: Value
|
||||
secret:
|
||||
type: boolean
|
||||
title: Secret
|
||||
input_type:
|
||||
type: string
|
||||
title: Input Type
|
||||
configured:
|
||||
type: boolean
|
||||
title: Configured
|
||||
type: object
|
||||
required:
|
||||
- env_name
|
||||
- label
|
||||
- value
|
||||
- secret
|
||||
- input_type
|
||||
- configured
|
||||
title: ConfigField
|
||||
ConfigResponse:
|
||||
properties:
|
||||
sections:
|
||||
items:
|
||||
$ref: '#/components/schemas/ConfigSection'
|
||||
type: array
|
||||
title: Sections
|
||||
type: object
|
||||
required:
|
||||
- sections
|
||||
title: ConfigResponse
|
||||
ConfigSection:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
title: Name
|
||||
fields:
|
||||
items:
|
||||
$ref: '#/components/schemas/ConfigField'
|
||||
type: array
|
||||
title: Fields
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- fields
|
||||
title: ConfigSection
|
||||
ConfigUpdateRequest:
|
||||
properties:
|
||||
updates:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
title: Updates
|
||||
type: object
|
||||
required:
|
||||
- updates
|
||||
title: ConfigUpdateRequest
|
||||
description: Flat mapping of env_name → value, mirroring the existing form semantics.
|
||||
ConfigUpdateResponse:
|
||||
properties:
|
||||
sections:
|
||||
items:
|
||||
$ref: '#/components/schemas/ConfigSection'
|
||||
type: array
|
||||
title: Sections
|
||||
type: object
|
||||
required:
|
||||
- sections
|
||||
title: ConfigUpdateResponse
|
||||
HTTPValidationError:
|
||||
properties:
|
||||
detail:
|
||||
|
||||
Reference in New Issue
Block a user