546 lines
28 KiB
HTML
546 lines
28 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>NUBES Fission Console</title>
|
||
<link rel="icon" type="image/png"
|
||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA4ZJREFUeJztm8+Lm0UYxz/fySZUoWIrVtbmXfyBP05Fa3e3rogsglJRK0p7E2svnrz6B3gQ8eRJFooXET14KKJ4FUQo3UQQ9CRC6W4WQW0VXZduTd7HQ9c22bzbTCY/ZpO8n0syb56Z+c437zPvhMzIzJhkXGwBsckNiC0gNrkBsQXEZsonSJLj4GNPgb2ACo9j9gDOvrSV6qkB6+sJ6WSJ5MIxUo4h5sDuI9U7tlZ57/+YTAN0zyOPUi+9jOxJxMOUj9wJOBBg117QvqGMwhNpcQ/J+rOkPIc0j+xeytyGsaWXa2+kvc312gxQMleHYgFtrQ9GYJmgmSfup3z15xuDNW/dGXOAjd680NgshlYdvcH2mfEwQIU0tOp4GNADuQGxBcRmPAyQgh/W42FAD+QGxBYQm/EwIF8HhJMbEFtAX3DreQqEkhsQW0BsxsMAlz8Gg8kNiC0gNuNhgJvK54BQcgNiC+gLhUlPgcuTbkAP5AbEFhCbcAOM4LyLitIW3eEGyP7qWUwMHFdaixNO1v4Av5pG8H/yfWfvesk7tq5GczHDAHnmtu727nTQbJYe8o51+qOl2BYgNv1a0iHvTgeNGq94x6buYnOx3YCU3/1asn06ePQZ744HiXTCO3a6/m1zsd0Ax/f+HTc+kU76598AUDL/LpjfjjWxYdXqRvOljDvAfezfO3dQvvCDtLjHu04f0czsaSx9y7uC8eP2S20GWO38Z+A7DwDwIOX135TMvylpKI9VJQv7lcx+hfEhurELsCPOfdDWVtZ5ASWzZ4GXArQ1QJfA/kTW6BhtLgX+7qL9IqTTmKa7GjgAumKry7dsv5q9Vfbfq29QLL1I9wulAtgB4ADmoy9kY4fodugAODuT2dpOJ0aUzH8E6asBXe0+zP5h7bvbzay+/aOdv+Ha8mnE5YEKGxqF17MGDzcxwMzqTBUWgc65vKuxT7cm9kx2TIHrATOzxzHOEpZ5sanYamXuZgEdJzlbqXyOmzrO6N0J56hVj3YK8prl7eK5L6g3DmFc6l3XEDCWbLWyYGYdf9h1TIGWYMmRHDlDaqcY0qKnO/Qr4oStLH/jXSPk4KSShf2ovoTZ80Db4iICv2D2ttWqS91WDDKgpYHy4adR8TWww8BdmN2KrOg5Zwoo+PV0">
|
||
<script>
|
||
if (window.location.protocol !== 'https:' && window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
|
||
window.location.replace('https://' + window.location.host + window.location.pathname + window.location.search + window.location.hash);
|
||
}
|
||
</script>
|
||
<link rel="stylesheet" href="css/styles.css">
|
||
<script src="js/config.js"></script>
|
||
<script src="js/api.js"></script>
|
||
<script src="js/ui.js"></script>
|
||
<script src="js/auth.js"></script>
|
||
<script src="js/triggers.js"></script>
|
||
<script src="js/modals.js"></script>
|
||
<script src="js/init.js"></script>
|
||
<script src="js/functions.js"></script>
|
||
<script src="js/fn-code.js"></script>
|
||
<script src="js/fn-archive.js"></script>
|
||
<script src="js/ai.js"></script>
|
||
<script src="js/app.js"></script>
|
||
</head>
|
||
|
||
<body>
|
||
<div id="login-overlay"
|
||
style="display:none; position:fixed; inset:0; background:rgba(0,0,0,.85); z-index:999; align-items:center; justify-content:center; padding:16px;">
|
||
<div class="panel" style="width:min(440px,100%);">
|
||
<div class="brand" style="margin-bottom:24px;">
|
||
<div class="brand-mark">N</div>
|
||
<div class="brand-text">
|
||
<div class="nubes">NUBES</div>
|
||
<div class="product">FISSION CONSOLE</div>
|
||
</div>
|
||
</div>
|
||
<div class="row" style="flex-direction:column; gap:6px; margin-bottom:12px;">
|
||
<label style="font-size:12px; color:#999;">Стенд</label>
|
||
<select id="l-env"
|
||
style="background:var(--bg-base); border:1px solid var(--border); color:var(--fg); padding:8px 10px; border-radius:6px;">
|
||
<option value="dev">Dev</option>
|
||
<option value="test" selected>Test</option>
|
||
<option value="prod">Prod</option>
|
||
</select>
|
||
</div>
|
||
<div class="row" style="flex-direction:column; gap:6px; margin-bottom:12px;">
|
||
<label style="font-size:12px; color:#999;">Токен или логин</label>
|
||
<textarea id="l-token" rows="5"
|
||
style="background:var(--bg-base); border:1px solid var(--border); color:var(--fg); padding:8px 10px; border-radius:6px; font-family:monospace; font-size:12px; resize:vertical; width:100%; box-sizing:border-box;"
|
||
placeholder="Введите токен или любой логин (не менее 6 символов)..."></textarea>
|
||
<div style="font-size:11px; color:var(--text-secondary); margin-top:4px;">Токен из ЛК: <strong
|
||
style="color:#8bc7ff;">Личный кабинет → Профиль пользователя → Токены</strong><br>Или введите любой логин не менее 6 символов для демо-доступа.</div>
|
||
</div>
|
||
<div id="l-error" style="display:none; color:#ff6b6b; font-size:13px; margin-bottom:10px;"></div>
|
||
<button id="l-btn" class="btn" style="width:100%;" onclick="doLogin()">Войти</button>
|
||
</div>
|
||
</div>
|
||
<!-- Overlay: инициализация нового окружения -->
|
||
<div id="init-overlay"
|
||
style="display:none; position:fixed; inset:0; background:rgba(0,0,0,.88); z-index:1000; align-items:center; justify-content:center; padding:16px;">
|
||
<div class="panel" style="width:min(520px,100%);">
|
||
<div style="margin-bottom:16px;">
|
||
<div style="font-size:16px; font-weight:700; margin-bottom:6px;">⚙️ Инициализация окружения</div>
|
||
<div style="font-size:13px; color:var(--text-secondary);">Первый вход. Настраиваем ваше окружение — это займёт
|
||
~5 минут.</div>
|
||
</div>
|
||
<div style="display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; margin-bottom:12px;">
|
||
<div style="background:#0d1827; border:1px solid var(--border); border-radius:6px; padding:10px;">
|
||
<div style="font-size:11px; color:var(--text-secondary); margin-bottom:4px;">Время настройки</div>
|
||
<div id="init-elapsed" style="font-size:18px; font-weight:700; color:#8bc7ff;">0с</div>
|
||
</div>
|
||
<div style="background:#0d1827; border:1px solid var(--border); border-radius:6px; padding:10px;">
|
||
<div style="font-size:11px; color:var(--text-secondary); margin-bottom:4px;">Текущий этап</div>
|
||
<div id="init-current-stage" style="font-size:14px; font-weight:600; color:#f4f7fb;">Ожидание старта</div>
|
||
</div>
|
||
</div>
|
||
<div id="init-summary"
|
||
style="display:none; margin-bottom:10px; background:#101c2b; border:1px solid var(--border); border-radius:6px; padding:10px 12px; font-size:13px; color:#d8e5f2;">
|
||
</div>
|
||
<div id="init-reason"
|
||
style="display:none; margin-bottom:10px; background:#2d2310; border:1px solid #6a5427; border-radius:6px; padding:10px 12px; font-size:13px; color:#ffd98a;">
|
||
</div>
|
||
<div id="init-error"
|
||
style="display:none; margin-bottom:10px; background:#2a1416; border:1px solid #7d3138; border-radius:6px; padding:10px 12px; font-size:13px; color:#ff9da5;">
|
||
</div>
|
||
<div id="init-log"
|
||
style="background:#000d1a; border:1px solid var(--border); border-radius:6px; padding:12px; font-family:monospace; font-size:12px; height:160px; overflow-y:auto; color:#4fc3f7; white-space:pre-wrap; line-height:1.6;">
|
||
</div>
|
||
<div id="init-stages" style="margin-top:14px; display:flex; flex-direction:column; gap:8px;"></div>
|
||
<div style="margin-top:14px; font-size:12px; color:var(--text-secondary);">Страница обновится автоматически когда
|
||
всё будет готово.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="navbar">
|
||
<div class="brand-mark" id="user-avatar">N</div>
|
||
<div class="brand-text" style="margin-left:auto; text-align:right;">
|
||
<div class="nubes">NUBES</div>
|
||
<div class="product">FISSION CONSOLE</div>
|
||
</div>
|
||
<div style="font-size:0.65rem; color:var(--text-secondary); margin-left:10px; align-self:center; opacity:0.7;">v1.3.50</div>
|
||
</div>
|
||
<div class="row" style="margin:0;">
|
||
<button class="btn ghost" onclick="reloadAll()">Refresh</button>
|
||
<button class="btn" onclick="openCreateCode()">✏️ Из кода</button>
|
||
<button class="btn" onclick="openCreateArchive()">📦 Из архива</button>
|
||
<button class="btn ghost" onclick="openHelp()">Help</button>
|
||
<button class="btn ghost" onclick="doLogout()" style="margin-left:8px;">Выход</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wrap">
|
||
<div class="grid">
|
||
<div class="card">
|
||
<div class="k">Окружения</div>
|
||
<div id="env-count" class="v">-</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="k">Пакеты</div>
|
||
<div id="pkg-count" class="v">-</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="k">Функции</div>
|
||
<div id="fn-count" class="v">-</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="k">HTTP-триггеры</div>
|
||
<div id="http-count" class="v">-</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="k">Крон-функции</div>
|
||
<div id="cron-count" class="v">-</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="box">
|
||
<div class="toolbar">
|
||
<div style="font-weight:600;">Функции</div>
|
||
<div class="hint">Actions: view, edit code, invoke, delete</div>
|
||
</div>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Имя</th>
|
||
<th>Окружение</th>
|
||
<th>Пакет</th>
|
||
<th>Создана</th>
|
||
<th>Изменена</th>
|
||
<th>Маршрут</th>
|
||
<th>Методы</th>
|
||
<th>Cron</th>
|
||
<th class="nowrap">Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="fn-rows"></tbody>
|
||
</table>
|
||
<div id="status" class="status"></div>
|
||
<div class="hint">Изменения применяются напрямую через CRD Fission.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Модалка: создать функцию из кода (prefix cc-) -->
|
||
<div id="create-code-modal" class="modal">
|
||
<div class="panel">
|
||
<h3>✏️ Создать функцию из кода</h3>
|
||
<div class="row">
|
||
<div class="field">
|
||
<label>Name</label>
|
||
<input id="cc-name" placeholder="demo-fn">
|
||
</div>
|
||
<div class="field">
|
||
<label>Language</label>
|
||
<select id="cc-lang" onchange="onLangChangeCode()">
|
||
<option value="python">Python</option>
|
||
<option value="nodejs">Node.js</option>
|
||
<option value="php">PHP</option>
|
||
<option value="ruby">Ruby</option>
|
||
</select>
|
||
</div>
|
||
<div class="field">
|
||
<label>Entrypoint</label>
|
||
<input id="cc-entry" value="main.main">
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="field">
|
||
<label>Route</label>
|
||
<input id="cc-route" placeholder="/demo-fn">
|
||
</div>
|
||
<div class="field">
|
||
<label>Методы (через запятую)</label>
|
||
<input id="cc-methods" value="GET">
|
||
</div>
|
||
<div class="field">
|
||
<label>Timeout (сек)</label>
|
||
<input id="cc-timeout" type="number" min="1" value="60">
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="field" style="min-width:240px; flex:0 0 260px;">
|
||
<label style="display:flex; align-items:center; gap:8px; color:var(--text-primary); margin-top:18px;">
|
||
<input id="cc-schedule-enabled" type="checkbox" onchange="toggleScheduleFields('cc')" style="width:auto;">
|
||
Выполнять по расписанию
|
||
</label>
|
||
</div>
|
||
<div class="field">
|
||
<label>Cron</label>
|
||
<input id="cc-cron" placeholder="*/5 * * * *" disabled>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div id="cc-code-area">
|
||
<textarea id="cc-code">def main():
|
||
return {"ok": True, "msg": "hello from fission console"}
|
||
</textarea>
|
||
<div style="margin-top:6px; display:flex; gap:6px; flex-wrap:wrap;">
|
||
<button class="btn ghost" id="cc-ai-btn" onclick="aiCheck('cc-code','cc-lang','cc-ai-result')">🔍 Проверить синтаксис линтером</button>
|
||
<button class="btn ghost" id="cc-gen-btn" onclick="showGenPrompt('cc')">✨ Сгенерировать код LLM</button>
|
||
<button class="btn ghost" id="cc-exp-btn" onclick="aiExplain('cc-code','cc-lang','cc-ai-result')">📖 LLM: Что делает?</button>
|
||
</div>
|
||
</div>
|
||
<div id="cc-gen-prompt" style="display:none; margin-top:8px; gap:6px; align-items:center;">
|
||
<input id="cc-gen-desc" type="text"
|
||
placeholder="Что должна делать функция? (напр: принять JSON, вернуть сумму чисел)"
|
||
style="flex:1; background:#2a2a42; border:1px solid #3a3a5c; border-radius:4px; color:#cdd6f4; padding:6px 8px; font-size:.82rem; outline:none; width:100%;"
|
||
onkeydown="if(event.key==='Enter')aiGenerate('cc')" />
|
||
<button class="btn" onclick="aiGenerate('cc')" style="white-space:nowrap;">▶ Генерировать LLM</button>
|
||
</div>
|
||
<div id="cc-ai-result"
|
||
style="display:none;margin-top:8px;padding:10px 12px;border-radius:6px;font-size:13px;line-height:1.5;white-space:pre-wrap;font-family:monospace;">
|
||
</div>
|
||
</div>
|
||
<div id="cc-error" class="modal-error"></div>
|
||
<div class="actions">
|
||
<button class="btn ghost" onclick="closeCreateCode()">Отмена</button>
|
||
<button id="cc-submit" class="btn" onclick="submitCreateCode()">Создать</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Модалка: создать функцию из архива (prefix ca-) -->
|
||
<div id="create-archive-modal" class="modal">
|
||
<div class="panel">
|
||
<h3>📦 Создать функцию из архива</h3>
|
||
<div class="row">
|
||
<div class="field">
|
||
<label>Name</label>
|
||
<input id="ca-name" placeholder="demo-fn">
|
||
</div>
|
||
<div class="field">
|
||
<label>Language</label>
|
||
<select id="ca-lang" onchange="onLangChangeArchive()">
|
||
<option value="python">Python</option>
|
||
<option value="nodejs">Node.js</option>
|
||
<option value="php">PHP</option>
|
||
<option value="ruby">Ruby</option>
|
||
</select>
|
||
</div>
|
||
<div class="field">
|
||
<label>Entrypoint</label>
|
||
<input id="ca-entry" value="main.main">
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="field">
|
||
<label>Route</label>
|
||
<input id="ca-route" placeholder="/demo-fn">
|
||
</div>
|
||
<div class="field">
|
||
<label>Методы (через запятую)</label>
|
||
<input id="ca-methods" value="GET">
|
||
</div>
|
||
<div class="field">
|
||
<label>Timeout (сек)</label>
|
||
<input id="ca-timeout" type="number" min="1" value="60">
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="field" style="min-width:240px; flex:0 0 260px;">
|
||
<label style="display:flex; align-items:center; gap:8px; color:var(--text-primary); margin-top:18px;">
|
||
<input id="ca-schedule-enabled" type="checkbox" onchange="toggleScheduleFields('ca')" style="width:auto;">
|
||
Выполнять по расписанию
|
||
</label>
|
||
</div>
|
||
<div class="field">
|
||
<label>Cron</label>
|
||
<input id="ca-cron" placeholder="*/5 * * * *" disabled>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div id="ca-archive-area">
|
||
<label>Архив (.zip)</label>
|
||
<input type="file" id="ca-archive-file" accept=".zip" style="display:block; margin-bottom:8px; color:var(--text-primary);">
|
||
<div style="display:flex; gap:6px; flex-wrap:wrap;">
|
||
<button id="ca-lint-btn" class="btn ghost" onclick="lintArchiveFile('ca')">🔍 Проверка архива линтером</button>
|
||
<button id="ca-explain-archive-btn" class="btn ghost" onclick="explainArchiveFile('ca')">📖 LLM: Что делает?</button>
|
||
</div>
|
||
</div>
|
||
<div id="ca-ai-result"
|
||
style="display:none;margin-top:8px;padding:10px 12px;border-radius:6px;font-size:13px;line-height:1.5;white-space:pre-wrap;font-family:monospace;">
|
||
</div>
|
||
</div>
|
||
<div id="ca-error" class="modal-error"></div>
|
||
<div class="actions">
|
||
<button class="btn ghost" onclick="closeCreateArchive()">Отмена</button>
|
||
<button id="ca-submit" class="btn" onclick="submitCreateArchive()">Создать</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="edit-modal" class="modal">
|
||
<div class="panel">
|
||
<h3 id="e-title">Редактирование кода</h3>
|
||
<div id="e-tf-warn"
|
||
style="display:none;background:#553300;color:#ffa;padding:8px 12px;border-radius:6px;margin-bottom:10px;font-size:13px;">
|
||
⚠️ Эта функция управляется Terraform. Изменения могут быть перезаписаны при следующем
|
||
<code>terraform apply</code>.
|
||
</div>
|
||
<input type="hidden" id="e-lang-hidden" value="">
|
||
<div class="row">
|
||
<div class="field">
|
||
<label>Name</label>
|
||
<input id="e-name" disabled>
|
||
</div>
|
||
<div class="field">
|
||
<label>Environment</label>
|
||
<input id="e-env" disabled>
|
||
</div>
|
||
<div class="field">
|
||
<label>Entrypoint</label>
|
||
<input id="e-entry" disabled>
|
||
</div>
|
||
<div class="field">
|
||
<label>Timeout (сек)</label>
|
||
<input id="e-timeout" type="number" min="1" value="60">
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="field" style="min-width:240px; flex:0 0 260px;">
|
||
<label style="display:flex; align-items:center; gap:8px; color:var(--text-primary); margin-top:18px;">
|
||
<input id="e-schedule-enabled" type="checkbox" onchange="toggleScheduleFields('e')" style="width:auto;">
|
||
Выполнять по расписанию
|
||
</label>
|
||
</div>
|
||
<div class="field">
|
||
<label>Cron</label>
|
||
<input id="e-cron" placeholder="*/5 * * * *" disabled>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div id="e-code-area">
|
||
<label>Код</label>
|
||
<textarea id="e-code"></textarea>
|
||
<div style="margin-top:6px; display:flex; gap:6px; flex-wrap:wrap;">
|
||
<button class="btn ghost" id="e-ai-btn" onclick="aiCheck('e-code','e-lang-hidden','e-ai-result')">🔍
|
||
Проверить синтаксис линтером</button>
|
||
<button class="btn ghost" id="e-exp-btn" onclick="aiExplain('e-code','e-lang-hidden','e-ai-result')">📖
|
||
LLM: Что делает?</button>
|
||
</div>
|
||
</div>
|
||
<div id="e-archive-area" style="display:none;">
|
||
<div id="e-archive-current" style="margin-bottom:10px; padding:8px 12px; background:var(--bg-alt); border-radius:6px; font-size:13px; color:var(--text-secondary);">
|
||
📦 <span id="e-archive-name"></span>
|
||
</div>
|
||
<label style="font-size:12px; color:var(--text-secondary); margin-bottom:4px; display:block;">Заменить архив (необязательно)</label>
|
||
<input type="file" id="e-archive-file" accept=".zip" style="display:block; margin-bottom:8px; color:var(--text-primary);">
|
||
<div style="display:flex; gap:6px; flex-wrap:wrap;">
|
||
<button id="e-lint-btn" class="btn ghost" onclick="lintArchiveFile('e')">🔍 Проверка архива линтером</button>
|
||
<button id="e-explain-archive-btn" class="btn ghost" onclick="explainArchiveFile('e')">📖 LLM: Что делает?</button>
|
||
</div>
|
||
</div>
|
||
<div id="e-ai-result"
|
||
style="display:none;margin-top:8px;padding:10px 12px;border-radius:6px;font-size:13px;line-height:1.5;white-space:pre-wrap;font-family:monospace;">
|
||
</div>
|
||
</div>
|
||
<div class="actions">
|
||
<button class="btn ghost" onclick="closeEdit()">Отмена</button>
|
||
<button id="e-submit" class="btn" onclick="submitEdit()">Сохранить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="help-modal" class="modal">
|
||
<div class="panel">
|
||
<h3>Help</h3>
|
||
|
||
<div class="help-section">
|
||
<h4>Поля при создании функции</h4>
|
||
<div class="help-list">
|
||
<div><strong>Name</strong> — имя Function в Fission. Используется в CRD и в связанных объектах.</div>
|
||
<div><strong>Language</strong> — язык runtime. От него зависят environment, шаблон кода и entrypoint по
|
||
умолчанию.</div>
|
||
<div><strong>Entrypoint</strong> — точка входа внутри пакета. Например, <span class="mono">main.main</span>
|
||
для Python или <span class="mono">main.php::handler</span> для PHP.</div>
|
||
<div><strong>Route</strong> — HTTP путь функции. Внешний пользовательский вызов идёт через <span
|
||
class="mono">/fn + ваш Route</span>.</div>
|
||
<div><strong>Методы</strong> — HTTP методы route, например <span class="mono">GET</span> или <span
|
||
class="mono">GET,POST</span>.</div>
|
||
<div><strong>Timeout (сек)</strong> — максимум времени выполнения именно этой функции.</div>
|
||
<div><strong>Код</strong> — исходный код функции.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="help-section">
|
||
<h4>Поля при редактировании функции</h4>
|
||
<div class="help-list">
|
||
<div><strong>Name</strong> — только для просмотра, не редактируется.</div>
|
||
<div><strong>Environment</strong> — только для просмотра, не редактируется из этой формы.</div>
|
||
<div><strong>Entrypoint</strong> — только для просмотра, не редактируется из этой формы.</div>
|
||
<div><strong>Timeout (сек)</strong> — редактируется и сохраняется в <span
|
||
class="mono">spec.functionTimeout</span>.</div>
|
||
<div><strong>Код</strong> — редактируется и обновляет Package функции.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="help-section">
|
||
<h4>Как вызвать функцию вне веб-интерфейса</h4>
|
||
<div class="help-list">
|
||
<div>Функции доступны через <span class="mono">/fn/...</span> с вашим токеном или демо-логином в заголовке <span class="mono">Authorization: Bearer</span>.</div>
|
||
<div>Gateway может возвращать редирект — используйте флаг <span class="mono">-L</span> в curl для его автоматического следования.</div>
|
||
<div>Работают обычные HTTP методы: <span class="mono">GET</span>, <span class="mono">POST</span>, <span
|
||
class="mono">PUT</span>, <span class="mono">DELETE</span> и другие, если они разрешены в поле Методы.
|
||
</div>
|
||
</div>
|
||
<div class="help-code">export TOKEN="<токен или демо-логин>"
|
||
curl -skL -X POST https://fission.kube5s.ru/fn/ВАШ_ROUTE \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"x":1,"y":2}'</div>
|
||
<div class="help-code">export TOKEN="<токен или демо-логин>"
|
||
curl -skL https://fission.kube5s.ru/fn/ВАШ_ROUTE \
|
||
-H "Authorization: Bearer $TOKEN"</div>
|
||
<div class="help-code">import requests
|
||
|
||
token = "ВАШ_ТОКЕН"
|
||
url = "https://fission.kube5s.ru/fn/ВАШ_ROUTE"
|
||
|
||
response = requests.post(
|
||
url,
|
||
headers={"Authorization": f"Bearer {token}"},
|
||
json={"x": 1, "y": 2},
|
||
timeout=60,
|
||
)
|
||
|
||
print(response.status_code)
|
||
print(response.text)</div>
|
||
</div>
|
||
|
||
<div class="help-section">
|
||
<h4>Где взять токен</h4>
|
||
<div class="help-list">
|
||
<div><strong>Токен из ЛК</strong> — Профиль пользователя → Токены. Подходит и для входа в console, и для вызова через <span class="mono">/fn/</span>.</div>
|
||
<div><strong>Демо-логин</strong> — любая строка не менее 6 символов. Тот же логин даёт тот же namespace — ваше окружение сохраняется.</div>
|
||
<div>Токен удобно класть в переменную окружения и потом подставлять в curl как <span
|
||
class="mono">$TOKEN</span>.</div>
|
||
</div>
|
||
<div class="help-code">export TOKEN="<вставьте сюда токен или демо-логин>"</div>
|
||
</div>
|
||
|
||
<div class="actions" style="justify-content:space-between; align-items:center;">
|
||
<span style="font-size:0.75rem; color:var(--text-secondary);">v1.3.50</span>
|
||
<button class="btn ghost" onclick="closeHelp()">Закрыть</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="invoke-modal" class="modal">
|
||
<div class="panel">
|
||
<h3 id="i-title">Вызов</h3>
|
||
<div>
|
||
<label>JSON тело запроса</label>
|
||
<textarea id="i-body">{"name":"world"}</textarea>
|
||
</div>
|
||
<div class="actions">
|
||
<button class="btn ghost" onclick="closeInvoke()">Отмена</button>
|
||
<button id="i-submit" class="btn" onclick="submitInvoke()">Вызвать</button>
|
||
</div>
|
||
<div id="i-status" style="margin-top:8px;font-size:13px;color:var(--text-secondary);min-height:20px;"></div>
|
||
<div id="i-meta"
|
||
style="display:none;margin-top:8px;background:#101c2b;border:1px solid var(--border);border-radius:6px;padding:10px 12px;font-size:13px;color:#d8e5f2;white-space:pre-wrap;">
|
||
</div>
|
||
<div style="margin-top:6px;">
|
||
<label>Response</label>
|
||
<textarea id="i-resp" readonly style="min-height:160px;"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const API_BASE = window.location.pathname.startsWith('/console') ? '/console/api' : '/api';
|
||
|
||
const S = {
|
||
envs: [],
|
||
fns: [],
|
||
httpTriggers: [],
|
||
timeTriggers: [],
|
||
currentEdit: null,
|
||
currentInvoke: null
|
||
};
|
||
|
||
checkAuth();
|
||
</script>
|
||
|
||
<!-- ================================================================
|
||
ai/ask feature — удалить весь блок до "end ai/ask feature"
|
||
================================================================ -->
|
||
<div id="assistant-panel" style="
|
||
position:fixed; bottom:0; right:24px;
|
||
width:720px; background:#1e1e2e; border:1px solid #3a3a5c;
|
||
border-bottom:none; border-radius:8px 8px 0 0;
|
||
font-family:inherit; z-index:900; box-shadow:0 -2px 12px rgba(0,0,0,.4);
|
||
">
|
||
<div id="assistant-header" onclick="toggleAssistant()" style="
|
||
display:flex; align-items:center; justify-content:space-between;
|
||
padding:8px 12px; cursor:pointer; background:#2a2a42; border-radius:8px 8px 0 0;
|
||
user-select:none;
|
||
">
|
||
<span style="font-weight:600; font-size:.85rem;">💬 LLM Ассистент</span>
|
||
<span id="assistant-toggle-icon" style="font-size:.75rem; opacity:.7;">▲</span>
|
||
</div>
|
||
<div id="assistant-body" style="padding:10px; display:flex; flex-direction:column; gap:8px;">
|
||
<div id="assistant-messages" style="
|
||
min-height:80px; max-height:400px; overflow-y:auto;
|
||
font-size:.82rem; line-height:1.5; color:#cdd6f4;
|
||
background:#13131f; border-radius:4px; padding:8px;
|
||
white-space:pre-wrap; word-break:break-word;
|
||
">Чем могу помочь?</div>
|
||
<div style="display:flex; gap:6px;">
|
||
<input id="assistant-input" type="text" placeholder="Введи вопрос..." style="flex:1; background:#2a2a42; border:1px solid #3a3a5c; border-radius:4px;
|
||
color:#cdd6f4; padding:6px 8px; font-size:.82rem; outline:none;"
|
||
onkeydown="if(event.key==='Enter')askAssistant()" />
|
||
<button onclick="askAssistant()" style="
|
||
background:#7c3aed; color:#fff; border:none; border-radius:4px;
|
||
padding:6px 12px; cursor:pointer; font-size:.82rem; white-space:nowrap;
|
||
">→</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</body>
|
||
|
||
</html> |