# Встраиваем веб-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.1 ``` ## Содержание - [Встраиваем веб-GUI в консольную утилиту: практический гайд](#встраиваем-веб-gui-в-консольную-утилиту-практический-гайд) - [Содержание](#содержание) - [Введение](#введение) - [Как быстро повторить (клонируем, собираем, запускаем)](#как-быстро-повторить-клонируем-собираем-запускаем) - [Структура проекта](#структура-проекта) - [Идея и архитектура](#идея-и-архитектура) - [Минимальный GUI](#минимальный-gui) - [Сборка фронтенда](#сборка-фронтенда) - [Встраивание в Go-сервис](#встраивание-в-go-сервис) - [API: контракт и примеры](#api-контракт-и-примеры) - [Запуск и проверка](#запуск-и-проверка) - [FAQ и типичные ошибки](#faq-и-типичные-ошибки) ## Введение Давайте по‑простому. Хотим к консольной утилите прикрутить небольшой веб‑интерфейс, чтобы не гонять команды вручную. Собрали один раз — и отдаем статику прямо из Go‑бинарника (или из рядом лежащей папки). В примере оставим только самое нужное: - Targets (строка вида `tcp:host:port;udp:host:port...`) - Delay (например `1s`) - Флаг Wait connection - Кнопка Execute Если нужен «боевой» вариант — всегда можно нарастить поля и логику. Но начнем с минимума. ## Как быстро повторить (клонируем, собираем, запускаем) 1 Клонируем репозиторий и переключаемся на ветку для статьи: ```bash # HTTPS клон git clone https://direct-dev.ru/gitea/GiteaAdmin/knock-gui.git cd knock-gui # Переход на ветку с упрощенным UI git checkout for-article ``` Источник репозитория: [`https://direct-dev.ru/gitea/GiteaAdmin/knock-gui`](https://direct-dev.ru/gitea/GiteaAdmin/knock-gui) 2 Ставим зависимости фронта и собираем UI: ```bash cd ui # Установим зависимости проекта (node/npm должны быть установлены) npm ci # Собираем продовую сборку ./build-for-embeding.sh ../back/cmd/public cd .. # или через make make embed-ui ``` 3 Запускаем бэкенд (Go 1.21+): ```bash # Обязательный пароль для Basic-Auth (GUI и API) export GO_KNOCKER_SERVE_PASS=changeme # Порт опционален (по умолчанию 8888) export GO_KNOCKER_SERVE_PORT=8888 # Вариант A: запустить из исходников cd back go run ./ # или Вариант B: собрать бинарник и запустить go build -o knocker-serve ./ ./knocker-serve serve # ну или так make run PASS=superpass PORT=8888 ``` 4 Открываем в браузере: ```text http://localhost:8888 ``` Браузер попросит логин/пароль (Basic‑Auth). Логин любой (например `knocker`), пароль — тот, что в `GO_KNOCKER_SERVE_PASS`. После этого UI загрузится, и та же авторизация «подхватится» для API‑вызовов. 5 Заполняем нужные таргеты: tcp:10.10.10.10:8080;udp:10.10.10.20:8888 6 Нажимаем Execute — и смотрим результат. Ошибка авторизации? Проверьте пароль в переменной окружения и перезагрузите страницу. ## Структура проекта Чтобы лучше ориентироваться, вот ключевые директории и файлы (сокращенно): ```text knock-gui/ ├── ui/ # Angular SPA (минимальный GUI) │ ├── src/app/knock/ │ │ ├── knock-page.component.ts │ │ └── knock-page.component.html │ ├── build-for-embeding.sh # Скрипт сборки и копирования артефактов в back │ └── ... ├── back/ # Go backend (встроенная статика + API) │ ├── cmd/ │ │ ├── serve.go # запуск сервера, basic-auth, CORS │ │ ├── static_routes.go # раздача встроенной статики (SPA routing) │ │ └── knock_routes.go # эндпойнты API │ ├── internal/knocker.go # логика работы │ └── main.go └── article/ # материалы статьи ``` ## Идея и архитектура - **Фронтенд (Angular SPA)** — статические файлы (`index.html`, `*.js`, `*.css`). - **Бэкенд (Go + Gin)** — раздаёт эти файлы и держит REST API. Для статики используем `embed.FS` — удобно распространять единый бинарник. - **Безопасность** — простой Basic‑Auth. Переменная `GO_KNOCKER_SERVE_PASS` обязательна: без пароля сервер не стартует. Под капотом статику обслуживает `setupStaticRoutes`, обратите внимание на SPA‑маршрутизацию (фоллбэк на `index.html`): ```1:30:/home/su/projects/articles/embed-gui-article/back/cmd/static_routes.go // Если файл не найден и это маршрут SPA — показываем index.html ``` ## Минимальный GUI Оставили только то, что реально нужно для «пнул порт — и поехали». Поля формы и кнопка запуска — никаких лишних переключателей. Ключевой шаблон компонента: ```12:60:/home/su/projects/articles/embed-gui-article/ui/src/app/knock/knock-page.component.html