Files
fission-console/doc/descriptions/function-create-flow.md
T

7.5 KiB
Raw Blame History

Создание функции — текущий flow (май 2026)

UI (браузер)

Форма создания (create-modal)

Поля в index.html:

  • c-lang — select (python / nodejs / go / ruby / php)
  • c-name — имя функции
  • c-entry — entrypoint (заполняется автоматически по языку из LANG_TEMPLATES)
  • c-route — HTTP-путь
  • c-methods — методы, через запятую
  • c-timeout — таймаут в секундах
  • c-schedule-enabled + c-cron — cron-расписание (опционально)
  • c-codetextarea с исходным кодом функции (единственный способ передать код)

Кнопки рядом с c-code:

  • aiCheck(...) — линтер (POST /console/api/ai/check)
  • aiExplain(...) — объяснение через LLM (POST /console/api/ai/ask)

При нажатии «Создать» вызывается submitCreate() (js/functions.js):

await requestJSON(API_BASE + '/functions', 'POST', {
  name, language, entrypoint, route, methods, timeout,
  code: document.getElementById('c-code').value   // <-- всегда текст
});

Форма редактирования (edit-modal)

Аналогична. submitEdit() шлёт:

await requestJSON(API_BASE + '/functions/' + name + '/code', 'PUT', {
  code: document.getElementById('e-code').value,
  timeout: ...
});

Backend

POST /console/api/functions → handleCreateFunction

Структура запроса (model.CreateFunctionRequest):

{ "name": "fn", "language": "python", "code": "def main(): ...",
  "entrypoint": "main", "route": "/ns/fn", "methods": ["GET"],
  "timeout": 60, "ttl": "" }

Обязательные: name, language (или environment), code.

Путь для интерпретируемых языков (python / nodejs / ruby / php):

code (строка)
  → buildDeployArchive(lang, code)
      python/ruby/php: []byte(code) (raw, не zip)
      nodejs: BuildJSDeployZip(code) → zip с package.json + main.js
  → buildDeploySpec(ctx, bytes)
      → uploadToStoragesvc() → multipart POST → storagesvc → archiveURL
      → fallback: base64 literal (если storagesvc недоступен)
  → Package.spec.deployment = { type: "url", url: "..." }
               или           = { type: "literal", literal: "base64..." }

Путь для Go:

code
  → BuildGoSourceZip(code) → zip с исходником
  → uploadToStoragesvc → archiveURL
  → Package.spec.source = { ... }   (source package, компилируется builder job)
  Package.spec.buildcommand = "build"

После создания Package → создаётся Function → HTTPTrigger. При ошибке на любом шаге — откат (best-effort DELETE предыдущих объектов).

PUT /console/api/functions/{name}/code → handleUpdateFunctionCode

{ "code": "...", "timeout": 60 }

Обязателен: code.

Алгоритм:

  1. Читает текущую Function → берёт fission-console/language из аннотации
  2. buildDeployArchive(lang, code) → байты
  3. Создаёт новый Package с именем {name}-pkg-{timestamp_base36}
  4. Обновляет Function.spec.package.packageref на новый пакет
  5. Удаляет старый Package (best-effort)

Почему новый Package, а не обновление старого: executor кэширует function-service по functionUid — не видит изменений в том же Package. Новое имя гарантирует cache miss.


Линтер (ai/check) и LLM (ai/ask)

POST /console/api/ai/check

{ "language": "python", "code": "def main(): ..." }

Работает только со строкой кода. Запускает:

  • python: python3 -m py_compile <tmpfile.py>
  • nodejs: node --check <tmpfile.js>
  • ruby: ruby -c <tmpfile.rb>
  • php: php -l <tmpfile.php>
  • go: go/parser.ParseFile(...) прямо в процессе (без tmpfile)

Временный файл создаётся в os.TempDir(), удаляется через defer.

POST /console/api/ai/ask

Отправляет код в LLM (объяснение, анализ). Детали в ai_prompts.go.


Что сейчас НЕ поддерживается

  • Загрузка архива (.zip) из файла — ни в UI, ни в API
  • Архив можно только скачать (через extractPackageSourceCodefetchPackageArchive)

Planned: загрузка архива

API

Добавить в CreateFunctionRequest и UpdateCodeRequest поле archive_b64 string. Логика в handler: если archive_b64 != "" — пропустить buildDeployArchive, сразу uploadToStoragesvc(base64.Decode(archive_b64)).

Валидация:

  • magic bytes PK\x03\x04 (zip)
  • максимум 10MB (против 1MB для кода)

UI

Раздельные режимы в форме — кнопки-переключатели «✏️ Код» / «📦 Архив»:

  • режим «Код» — textarea (как сейчас)
  • режим «Архив» — <input type="file" accept=".zip">, textarea скрыта

В submitCreate / submitEditFileReader.readAsDataURL(file) → base64 → поле archive_b64.

Линтер и LLM для архива

Проблема: линтер (ai/check) и LLM (ai/ask) принимают code — строку. Архив — zip из нескольких файлов. Варианты:

Вариант A — на бэке, при загрузке После распаковки архива (уже есть decodeArchiveBytesToSource в package.go):

  • Извлечь главный файл (entrypoint)
  • Прогнать через линтер
  • Результат вернуть в том же ответе на create/update (поле lint_result)

Плюсы: не меняет контракт линтера, нет доп. round-trip. Минусы: линтится только один файл из архива.

Вариант B — на фронте, до отправки FileReader.readAsText(file) — не работает для zip напрямую. Можно использовать JSZip (npm/CDN) — распаковать в браузере → взять главный файл → передать в ai/check.

Плюсы: линтинг до загрузки, быстрая обратная связь. Минусы: добавляется зависимость (JSZip ~100KB).

Вариант C — гибрид (рекомендован)

  1. Фронт: при выборе архива — использовать JSZip, найти entrypoint-файл по языку, показать его в readonly-textarea, кнопки линтера и LLM работают с этим текстом.
  2. Отправка: archive_b64 (весь zip) идёт на бэк как есть.
  3. Бэк (опционально): после сохранения прогнать decodeArchiveBytesToSource → линтер, вернуть предупреждение если нашёл ошибки.

Так пользователь видит и код и результат линтера до деплоя, и полный архив уходит без изменений.