fix desktop

This commit is contained in:
2025-10-03 16:51:29 +06:00
parent cce64b19e8
commit e28d0ec77e
6 changed files with 249 additions and 212 deletions

View File

@@ -352,11 +352,11 @@ Renderer остался «чистым» фронтом: получает `windo
## Глава 4. Секретный Go-движок внутри
Написан маленький Go-хелпер, который делает РОВНО то, что нужно:
Написан маленький Go-хелпер, который делает кнокинг портов в соответствии с конфигурацией:
- читает из stdin JSON: `targets`, `delay`, `gateway`,
- превращает в конфиг,
- вызывает `internal.PortKnocker()` (в нём `SO_BINDTODEVICE`, `LocalAddr`, TCP/UDP),
- превращает их в конфиг,
- вызывает `internal.PortKnocker()` (в нём `SO_BINDTODEVICE`),
- печатает в stdout один короткий JSON: «успех/ошибка».
```go
@@ -512,7 +512,7 @@ clearTimeout(timeout);
## Глава 6. UI только для UI
В `renderer` ничего такого что непосредственно работает с ядром системы.
В `renderer` ничего такого что непосредственно работает с ядром системы.
Собираем `targets`, `delay`, забираем `gateway` с формы — и даём команду:
```js
@@ -552,18 +552,18 @@ Renderer ничего не знает про сокеты, туннели и Go.
При разработке запускаем `npm run dev` — он сначала соберёт Go, потом стартанёт Electron. В проде `electron-builder` положит бинарь в `resources/bin`. Пользователь вообще не в курсе, что внутри слишком умный Go сидит и пинает пакеты в нужные цели.
Можно конечно и не Го-хелпер запилить - например на Rust или на С++
Можно конечно и не Го-хелпер запилить - например на Rust (смотри последнюю версию проекта в репозитории) или на С++
---
## Глава 8. Почему мы пошли этим путём (немного философии)
## Глава 8. Почему именно так
- Node — шикарен для UI и связки слоёв, но ему не хватает «низкоуровневого директа» в сокетах.
- Go умеет то, что нам нужно: `SO_BINDTODEVICE`, привязка к интерфейсу, «я сказал — поедешь тут».
- Electron — как контейнер: упаковали веб, настроили мосты, подложили Go, включили DevTools красота.
- Node — хорош для UI и связки слоёв, но ему не хватает «низкоуровневого директа» в сокетах.
- Go умеет то, что нужно: `SO_BINDTODEVICE`, привязка к интерфейсу.
- Electron — как контейнер: упаковали веб, настроили мосты, подложили Go, включили DevTools = красота.
Минус: надо собирать маленький бинарь.
Плюс: он работает невидимо и делает «как надо».
Минус: надо собирать маленький бинарничек, который немного увеличит размер пакета (так как он и так ого-го-то несущественно).
Плюс: он работает невидимо и делает что требуется.
---
@@ -573,13 +573,209 @@ Renderer ничего не знает про сокеты, туннели и Go.
- Следите за stdout хелпера — там должен быть только JSON (мы жёстко это контролируем).
- Путь к бинарю в деве и проде разный — мы это учли (ищем сначала `bin/`, потом `resources/bin`).
Мем-чекилст:
- [x] «Всё уходит в WireGuard» → ставим `gateway` → Go рулит.
- [x] «JSON не парсится» → убрали verbose → теперь парсится.
- [x] «Где бинарь?» → смотрим `resources/bin`.
---
## Эпилог
Мы сделали десктопное приложение, которое вообще-то «веб», но изнутри умеет кое что еще.
---
## Эпилог: помогло?
## А как же фреймворки?
Мы сделали десктопное приложение, которое вообще-то «веб», но изнутри умеет очень взрослые вещи. Пользователь нажимает кнопку — а Go в это время спорит с системой маршрутизации, выигрывает и бьёт туда, куда надо.
На Vanilla js все просто и понятно писать но очень многсловно и трудно сделать все структурировано и красиво "по фен-шую".
Давайте коротенько рассмотрим, а как же нам портировать имеющийся ui ангуляр проект, который героически был вшит в go-knocker в десктопном проекте electron.
Итак, у нас есть готовый или не очень Angular проект который уже работает как веб-приложение. Теперь нужно интегрировать его в Electron так, чтобы получить все возможности поюзать его как графическое приложение.
### Архитектурное решение
Мы создали отдельную папку `desktop-angular/`, которая содержит:
1. **Electron обертку** - основной процесс и настройки
2. **Копию Angular проекта** в `src/frontend/` - для удобства разработки
3. **IPC сервисы** - для взаимодействия с нативным кодом
4. **Кастомные модальные окна** - нативные диалоги Electron
### Структура проекта
``` text
desktop-angular/
├── src/
│ ├── main/ # Electron main process
│ │ ├── main.js # Основной процесс
│ │ ├── modal.html # Кастомные модальные окна
│ │ ├── open-dialog.html # Диалог открытия файлов
│ │ └── save-dialog.html # Диалог сохранения файлов
│ ├── preload/ # Безопасный мост
│ │ └── preload.js # API для рендерера
│ └── frontend/ # Angular приложение
│ ├── src/app/
│ │ ├── ipc.service.ts # Сервис для IPC
│ │ ├── modal.service.ts # Сервис модальных окон
│ │ └── root.component.ts # Главный компонент
│ └── package.json # Зависимости Angular
├── package.json # Конфигурация Electron
└── bin/ # Скомпилированный Go бэкенд
```
### Ключевые особенности реализации
#### 1. Двойной режим работы
В `main.js` реализована логика, которая определяет режим работы:
```javascript
const isDev = process.env.NODE_ENV !== "production" && !app.isPackaged;
if (isDev) {
// В режиме разработки загружаем с ng serve
win.loadURL('http://localhost:4200');
win.webContents.openDevTools();
} else {
// В продакшене загружаем собранные файлы
const indexPath = app.isPackaged
? path.join(process.resourcesPath, 'ui-dist', 'index.html')
: path.resolve(__dirname, '../frontend/dist/project-front/browser/index.html');
win.loadFile(indexPath);
}
```
#### 2. IPC сервис для Angular
Создан специальный сервис `IpcService`, который предоставляет удобный API для взаимодействия с Electron:
```typescript
@Injectable({
providedIn: 'root'
})
export class IpcService {
async showNativeModal(config: ModalConfig): Promise<string> {
return await (window as any).api.showNativeModal(config);
}
async openFileDialog(config: FileDialogConfig): Promise<string[]> {
return await (window as any).api.openFileDialog(config);
}
async saveFileDialog(config: SaveDialogConfig): Promise<string> {
return await (window as any).api.saveFileDialog(config);
}
async loadFileContent(filePath: string): Promise<string> {
return await (window as any).api.loadFileContent(filePath);
}
async saveFileContent(filePath: string, content: string): Promise<void> {
return await (window as any).api.saveFileContent(filePath, content);
}
}
```
#### 3. Кастомные нативные диалоги
Вместо стандартных системных диалогов созданы кастомные HTML/CSS/JS диалоги, которые:
- Работают даже если Angular UI "зависла"
- Имеют единый стиль с приложением
- Поддерживают превью файлов
- Имеют расширенную функциональность
#### 4. Интеграция с Go бэкендом
Angular приложение работает с тем же Go бэкендом, что и веб-версия:
```typescript
export class KnockService {
private apiBase = 'http://localhost:8080/api/v1';
async knock(config: KnockConfig): Promise<KnockResult> {
const response = await fetch(`${this.apiBase}/knock`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
return await response.json();
}
}
```
### Скрипты сборки
В `package.json` настроены удобные команды:
```json
{
"scripts": {
"dev": "concurrently -k -n UI,ELECTRON -c green,cyan \"cd src/frontend && npm start\" \"wait-on http://localhost:4200 && cross-env NODE_ENV=development electron .\"",
"build:ui": "cd src/frontend && npm run build",
"go:build": "bash -lc 'mkdir -p bin && cd ../back && go build -o ../desktop-angular/bin/full-go-knocker .'",
"dist": "npm run build:ui && npm run go:build && cross-env NODE_ENV=production electron-builder"
}
}
```
### Конфигурация упаковки
Electron Builder настроен для включения всех необходимых файлов:
```json
{
"build": {
"files": [
"src/main/**/*",
"src/preload/**/*",
"package.json",
"bin/**/*"
],
"extraResources": [
{
"from": "src/frontend/dist/project-front/browser",
"to": "ui-dist"
},
{
"from": "bin",
"to": "bin"
}
]
}
}
```
### Преимущества такого подхода
1. **Переиспользование кода** - Angular приложение остается тем же самым
2. **Нативные возможности** - доступ к файловой системе, системным диалогам
3. **Единая кодовая база** - один Angular проект для веба и десктопа
4. **Удобная разработка** - hot reload в режиме разработки
5. **Простая сборка** - автоматическая упаковка всех компонентов
### Результат
Получилось полноценное десктопное приложение, которое:
- Выглядит и работает как нативное
- Использует весь функционал Angular
- Интегрировано с Go бэкендом
- Имеет кастомные нативные диалоги
- Упаковывается в единый исполняемый файл
Это решение демонстрирует, как можно элегантно интегрировать современный веб-фреймворк в десктопное приложение, сохраняя все преимущества обеих платформ.
---
## Заключение
Мы рассмотрели два подхода к созданию десктопных приложений:
1. **Vanilla JavaScript** - простой, быстрый, но многословный
2. **Angular интеграция** - структурированный, переиспользуемый, но более сложный
Оба подхода имеют свои преимущества и недостатки. Выбор зависит от ваших потребностей:
- Для быстрого прототипа или простого UI - Vanilla JS
- Для сложного приложения с переиспользованием веб-кода - Angular
Главное - правильно спроектировать архитектуру взаимодействия между Electron и вашим UI, используя IPC и preload скрипты для безопасной передачи данных.