# Встраиваем веб-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.