Files
knock-gui/article/embed-gui-guide.md

7.3 KiB
Raw Blame History

Встраиваем веб-GUI в консольную утилиту: практический гайд

Содержание

  1. Введение
  2. Идея и архитектура
  3. Минимальный GUI
  4. Сборка фронтенда
  5. Встраивание в Go-сервис
  6. API: контракт и примеры
  7. Запуск и проверка
  8. 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. Основной экран — одна форма и кнопка запуска.

Ключевой шаблон компонента:

<div class="container">
  <p-card header="Port Knocker (Минимальный UI)">
    <form [formGroup]="form" (ngSubmit)="execute()" class="p-fluid">
      <div class="grid">
        <div class="col-12">
          <label>Targets</label>
          <input pInputText type="text" formControlName="targets" placeholder="tcp:host:port;udp:host:port" class="w-full" />
        </div>
        <div class="col-12 md:col-6">
          <label>Delay</label>
          <input pInputText type="text" formControlName="delay" placeholder="1s" class="w-full" />
        </div>
        <div class="col-12 md:col-6 flex align-items-center gap-2">
          <p-checkbox formControlName="waitConnection" [binary]="true"></p-checkbox>
          <label class="checkbox-label">Wait connection</label>
        </div>
        <div class="col-12">
          <button pButton type="submit" label="Execute" class="w-full" [loading]="executing" [disabled]="executing || form.invalid"></button>
        </div>
      </div>
    </form>
  </p-card>
</div>

Логика отправки запроса:

this.http.post('/api/v1/knock-actions/execute', {
  targets: v.targets,
  delay: v.delay,
  waitConnection: v.waitConnection
}).subscribe(...)

Сборка фронтенда

Собираем Angular-приложение и копируем артефакты в каталог, из которого Go-сервис будет обслуживать статику.

Скрипт сборки:

#!/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 в бинарник.

Пример (концептуально):

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:

{
  "targets": "tcp:127.0.0.1:22;udp:1.2.3.4:53",
  "delay": "1s",
  "waitConnection": false
}

Response 200:

{ "status": "ok" }

Пример curl:

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. Собрать фронт и скопировать файлы:
cd /home/su/projects/articles/embed-gui-article/ui
./build-for-embeding.sh /home/su/projects/articles/embed-gui-article/back/cmd/public
  1. Запустить бэкенд (варианты):
  • Через make/скрипт, если есть.
  • Локально go run ./back (или соответствующая команда в вашем проекте).
  1. Открыть в браузере / и проверить, что форма грузится, а Execute бьёт в API.

FAQ и типичные ошибки

  • Статика не находится: проверьте путь копирования и что сервер обслуживает каталог.
  • SPA роуты дают 404 при F5: настройте отдачу index.html по умолчанию.
  • CORS при раздельных портах: либо проксируйте, либо включите CORS в сервере.

Этот гайд показывает минимальный путь: собрать веб-UI, скопировать в папку статики бэкенда и обеспечить один APIэндпоинт. Для production можно добавить embed.FS, версионирование артефактов и CI/CD.