Newer
Older
smart-home-server / server / plan.md

План: Params Schema для Action Scripts

Цель

Scope-классы могут декларировать JSON-схему параметров при регистрации action-скрипта. Vue-клиент отображает динамическую форму в модалке вместо пустого {} при запуске. Сервер валидирует входящие параметры по схеме перед вызовом closure.

Дизайн (утверждён)

Формат в PHP Scope:

$this->add_action_script([
    "alias"         => "dim_lights",
    "name"          => "Диммер",
    "params_schema" => [
        "level" => [
            "type"     => "range",
            "label"    => "Яркость, %",
            "min"      => 0,
            "max"      => 100,
            "step"     => 5,
            "default"  => 50,
            "required" => true,
        ],
        "room" => [
            "type"    => "select",
            "label"   => "Комната",
            "options" => ["kitchen" => "Кухня", "hall" => "Зал"],
            "default" => "hall",
        ],
        "instant" => [
            "type"    => "toggle",
            "label"   => "Мгновенно",
            "default" => false,
        ],
    ],
], function($params) { ... });

Типы: text, number, range, select, toggle, textarea. Валидация PHP: тип, required, min/max для number/range, ключ в options для select. UI: модал GnModal с динамической формой; GnInput, GnRange, GnSelect, GnSwitch, GnTextarea.


Фаза 1 — PHP backend

1.1 Миграция

Файл: server/database/migrations/2026_06_08_000003_script_params_schema.php

  • ALTER TABLE scripts ADD COLUMN params_schema TEXT NULL

1.2 Entity Script

Файл: server/SHServ/Entities/Script.php

  • Добавить "params_schema" в $fields

1.3 Model Scripts — синхронизация и мерж

Файл: server/SHServ/Models/Scripts.php

  • prepare_script_to_view() — мержить params_schema из DB entity в ответ (если есть)
  • Новый приватный метод sync_params_schema_to_db($type, $uniq_name, $schema):
    • Если запись в БД существует и params_schema отличается от registry — UPDATE
    • Вызывать из get_scripts_list() при подготовке к view (lazy sync)
  • Новый метод get_params_schema($type, $uniq_name): ?array — декодировать JSON из БД

1.4 ControlScripts::add_action_script()

Файл: server/SHServ/Middleware/ControlScripts.php

  • Принимать "params_schema" из $attributes
  • Сохранять в ScriptsRegistry::$actions[$alias]["params_schema"]
  • add_regular_script() пока без изменений (можно заложить, но action — MVP)

1.5 ScriptsRegistry

Файл: server/SHServ/Middleware/ScriptsRegistry.php

  • Entry теперь: ["attributes", "code", "script", "params_schema"]

1.6 Валидация params по schema

Новый файл: server/SHServ/Helpers/ScriptParamsValidator.php

  • Метод validate($schema, $params): array — возвращает ["ok" => bool, "errors" => [...]]
  • Проверки по типам:
    • required — поле присутствует в $params
    • select — значение ∈ keys options
    • number/rangeis_numeric, в границах min/max
    • toggleis_bool
    • text/textareais_string

1.7 ScriptsRESTAPIController::run_action_script()

Файл: server/SHServ/Controllers/ScriptsRESTAPIController.php

  • После проверки alias и state, перед вызовом ControlScripts::run_action_script():
    1. Получить schema из ScriptsRegistry::$actions[$alias]["params_schema"] или из БД
    2. Если schema не null — валидировать $params через ScriptParamsValidator
    3. Если ошибка — ответ invalid_params с failed_fields
    4. Применить default для отсутствующих необязательных полей
    5. Затем вызвать ControlScripts::run_action_script($alias, $params)

1.8 ScriptsRESTAPIController::actions_scripts_list()

  • Убедиться что params_schema присутствует в ответе (через prepare_script_to_view)

Фаза 2 — Vue frontend

2.1 API module (без изменений)

scriptsApi.runAction(alias, params) уже поддерживает params — достаточно.

2.2 Store (минимальные изменения)

Файл: webclient/src/stores/scripts.js

  • runScript(alias, params) уже принимает params
  • Добавить state runModalScript: null — текущий скрипт для модалки (или управлять через emit)

2.3 Новый компонент ScriptRunModal.vue

Файл: webclient/src/components/script/ScriptRunModal.vue

  • Props: script (alias, name, params_schema)
  • GnModal :open + @update:open
  • Внутри: динамическая форма:
    • v-for="(config, name) in script.params_schema" :key="name"
    • Маппинг type → компонент:
      • textGnInput type="text"
      • numberGnInput type="number"
      • rangeGnRange (min, max, step)
      • selectGnSelect (:options — массив {value, label} из Object.entries(config.options))
      • toggleGnSwitch
      • textareaGnTextarea
    • v-model на реактивный объект formValues[name]
    • Инициализация formValues значениями default из schema
    • Валидация клиента: required → красная рамка/сообщение, GnInput/GnRange props
  • Footer: Run (disabled если есть required-ошибки) + Cancel
  • При submit: $emit('run', { alias: script.alias, params: formValues })

2.4 ActionScriptsGrid.vue

Файл: webclient/src/components/script/ActionScriptsGrid.vue

  • Метод run(alias) заменить на:
    function run(script) {
      if (!script.params_schema || Object.keys(script.params_schema).length === 0) {
        executeRun(script.alias, {});
        return;
      }
      activeScriptForModal.value = script;
      showRunModal.value = true;
    }
  • Добавить <ScriptRunModal :script="activeScriptForModal" :open="showRunModal" @run="onModalRun" @close="showRunModal = false" />
  • onModalRun({ alias, params })executeRun(alias, params)

2.5 ScriptDetailPage.vue

Файл: webclient/src/features/scripts/pages/ScriptDetailPage.vue

  • Аналогично: если action script имеет params_schema, Run открывает модал вместо прямого вызова

Фаза 3 — Тесты

3.1 PHP tests

Новый файл: server/tests/ScriptParamsValidatorTest.php

  • Проверка валидации по каждому типу (valid / invalid)
  • Проверка required и default
  • Проверка edge cases (null, пустая строка)

Файл: server/tests/ScriptsModelStateTest.php или новый ScriptParamsSchemaTest.php

  • Проверка что prepare_script_to_view возвращает params_schema
  • Проверка lazy sync в DB

3.2 Vue tests

Новый файл: webclient/src/components/script/__tests__/ScriptRunModal.spec.js

  • Рендеринг полей по schema
  • Валидация required
  • Событие submit с правильными params

Фаза 4 — Документация

4.1 docs/control-scripts-guide.md

  • Обновить раздел "Action-скрипты": добавить params_schema в пример
  • Добавить подраздел "Params Schema" с таблицей типов и их полей

4.2 docs/server-api-v1/scripts.md

  • В GET /api/v1/scripts/actions/list — добавить params_schema в пример ответа
  • В POST /api/v1/scripts/actions/run — добавить пример с params и ошибку invalid_params

Критические файлы

Файл Действие
server/database/migrations/2026_06_08_000003_script_params_schema.php Создать
server/SHServ/Entities/Script.php Добавить поле
server/SHServ/Models/Scripts.php Lazy sync + merge
server/SHServ/Middleware/ControlScripts.php Принимать params_schema
server/SHServ/Middleware/ScriptsRegistry.php Хранить params_schema
server/SHServ/Helpers/ScriptParamsValidator.php Создать
server/SHServ/Controllers/ScriptsRESTAPIController.php Валидация при run
webclient/src/components/script/ScriptRunModal.vue Создать
webclient/src/components/script/ActionScriptsGrid.vue Открывать модал
webclient/src/features/scripts/pages/ScriptDetailPage.vue Открывать модал

Риски

  • Lazy sync side-effect: get_scripts_list() делает UPDATE. Альтернатива: синхронизировать при старте сервера в ControlScripts::add_action_script() через Scripts::set_script_state() (который уже делает INSERT/UPDATE), но туда нужно добавить params_schema. Это чище. Решение: обновить set_script_state() чтобы при INSERT/UPDATE сохранять params_schema из registry.
  • Каскад миграции: params_schema TEXT NULL совместимо с существующими данными.
  • Vue компонент не в kit: создаём свой ScriptRunModal.vue на базе GnModal и kit-инпутов.

Решение по lazy sync

Вместо lazy sync в read-методе — обновить Scripts::set_script_state(). Когда add_action_script() вызывается при старте, ControlScripts может вызвать set_script_state("action", $alias, true) (или новый метод) для записи params_schema в БД. Но set_script_state() сейчас вызывается только через API enable/disable.

Наиболее чистый путь: в Scripts::get_scripts_list() при наличии $script_entity сравнивать params_schema и делать thin_builder->update() если отличается. Это один UPDATE на рестарт, приемлемо.