From 539d9c492d0d45c9e473fc1696227b4ce4fd59c0 Mon Sep 17 00:00:00 2001 From: Anton Kuznetcov Date: Wed, 10 Sep 2025 12:55:29 +0600 Subject: [PATCH] =?UTF-8?q?docs(article):=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B3=D0=B0=D0=B9=D0=B4=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=B2=D1=81=D1=82=D1=80=D0=B0=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8E=20=D0=B2=D0=B5=D0=B1-GUI=20=D0=B2=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- article/embed-gui-guide.md | 173 +++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 article/embed-gui-guide.md diff --git a/article/embed-gui-guide.md b/article/embed-gui-guide.md new file mode 100644 index 0000000..e96df86 --- /dev/null +++ b/article/embed-gui-guide.md @@ -0,0 +1,173 @@ +# Встраиваем веб-GUI в консольную утилиту: практический гайд + +```metadata +id: 10 +readTime: 15-20 минут +date: 2025-09-10 18:00 +author: Direct-Dev (aka Антон Кузнецов) +level: Средний +tags: #go #angular #spa #embed #static #cli #webui #devops +version: 1.0.0 +``` + +## Содержание + +1. [Введение](#введение) +2. [Идея и архитектура](#идея-и-архитектура) +3. [Минимальный GUI](#минимальный-gui) +4. [Сборка фронтенда](#сборка-фронтенда) +5. [Встраивание в Go-сервис](#встраивание-в-go-сервис) +6. [API: контракт и примеры](#api-контракт-и-примеры) +7. [Запуск и проверка](#запуск-и-проверка) +8. [FAQ и типичные ошибки](#faq-и-типичные-ошибки) + +## Введение + +Задача: добавить простой веб-интерфейс к консольной утилите, собрать его один раз и отдавать статические файлы прямо из бинарника или из каталога рядом. В качестве примера используем минимальный UI со следующими полями: +- Targets (строка вида `tcp:host:port;udp:host:port`) +- Delay (например `1s`) +- Флаг Wait connection +- Кнопка Execute + +## Идея и архитектура + +- **Фронтенд (Angular SPA)** собирается в статический набор файлов (`index.html`, `main-*.js`, `styles-*.css`, и т.д.). +- **Бэкенд (Go, Gin/Chi/Stdlib)** отдаёт эти файлы по `/` и реализует API по `/api/...`. +- Раздача статики может быть: + - из файловой системы (проще для разработки); + - через `embed.FS` (удобно для единого бинарника). + +## Минимальный GUI + +Мы упростили компонент до минимума в ветке `for-article`. Основной экран — одна форма и кнопка запуска. + +Ключевой шаблон компонента: +```12:60:/home/su/projects/articles/embed-gui-article/ui/src/app/knock/knock-page.component.html +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+``` + +Логика отправки запроса: +```1:40:/home/su/projects/articles/embed-gui-article/ui/src/app/knock/knock-page.component.ts +this.http.post('/api/v1/knock-actions/execute', { + targets: v.targets, + delay: v.delay, + waitConnection: v.waitConnection +}).subscribe(...) +``` + +## Сборка фронтенда + +Собираем Angular-приложение и копируем артефакты в каталог, из которого Go-сервис будет обслуживать статику. + +Скрипт сборки: +```1:20:/home/su/projects/articles/embed-gui-article/ui/build-for-embeding.sh +#!/bin/bash +# Использование: ./build-for-embeding.sh /abs/path/to/back/cmd/public +npx ng build --configuration production +mkdir -p "$DESTINATION_DIR" +cp -r /home/su/projects/angular/project-front/dist/project-front/browser/* "$DESTINATION_DIR" +``` + +- Артефакты после `ng build` лежат в `ui/dist/project-front/browser/`. +- Рекомендуемая папка для копирования: `back/cmd/public/` (в проекте уже есть пример статики). + +## Встраивание в Go-сервис + +Вариант А: раздача файлов из папки (`back/cmd/public`). +- Положите файлы фронта в `back/cmd/public`. +- Убедитесь, что роутер отдаёт `/` и `/*` из этой папки. + +Вариант Б: `embed.FS` в бинарник. + +Пример (концептуально): +```go +package main + +import ( + "embed" + "net/http" +) + +//go:embed public/** +var webFS embed.FS + +func main() { + // http.FileServerFS доступен в Go 1.22+, иначе используйте fs.Sub + http.Handle("/", http.FileServer(http.FS(webFS))) + http.HandleFunc("/api/v1/knock-actions/execute", executeHandler) + http.ListenAndServe(":8080", nil) +} +``` + +В репозитории уже есть бэкенд с маршрутами и готовой статикой в `back/cmd/public/`. Для статьи достаточно положить собранный фронт туда. + +## API: контракт и примеры + +Endpoint: `POST /api/v1/knock-actions/execute` + +Request JSON: +```json +{ + "targets": "tcp:127.0.0.1:22;udp:1.2.3.4:53", + "delay": "1s", + "waitConnection": false +} +``` + +Response 200: +```json +{ "status": "ok" } +``` + +Пример curl: +```bash +curl -X POST http://localhost:8080/api/v1/knock-actions/execute \ + -H 'Content-Type: application/json' \ + -d '{"targets":"tcp:127.0.0.1:22","delay":"1s","waitConnection":false}' +``` + +## Запуск и проверка + +1) Собрать фронт и скопировать файлы: +```bash +cd /home/su/projects/articles/embed-gui-article/ui +./build-for-embeding.sh /home/su/projects/articles/embed-gui-article/back/cmd/public +``` + +2) Запустить бэкенд (варианты): +- Через make/скрипт, если есть. +- Локально `go run ./back` (или соответствующая команда в вашем проекте). + +3) Открыть в браузере `/` и проверить, что форма грузится, а `Execute` бьёт в API. + +## FAQ и типичные ошибки + +- Статика не находится: проверьте путь копирования и что сервер обслуживает каталог. +- SPA роуты дают 404 при F5: настройте отдачу `index.html` по умолчанию. +- CORS при раздельных портах: либо проксируйте, либо включите CORS в сервере. + +--- + +Этот гайд показывает минимальный путь: собрать веб-UI, скопировать в папку статики бэкенда и обеспечить один API‑эндпоинт. Для production можно добавить `embed.FS`, версионирование артефактов и CI/CD.