Files
knock-gui/desktop/DEVELOPMENT.md

991 lines
31 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Руководство по разработке Knocker Desktop
## 🔍 Подробное описание архитектуры
### Архитектура Electron приложения
``` text
┌─────────────────────────────────────────────────────────────┐
│ MAIN PROCESS │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ src/main/main.js │ │
│ │ • Управление жизненным циклом приложения │ │
│ │ • Создание и управление окнами │ │
│ │ • Доступ к Node.js API (fs, dialog, shell) │ │
│ │ • IPC обработчики для файловых операций │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ IPC (Inter-Process Communication)
┌─────────────────────────────────────────────────────────────┐
│ RENDERER PROCESS │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ src/renderer/ │ │
│ │ • HTML/CSS/JS интерфейс │ │
│ │ • Взаимодействие с пользователем │ │
│ │ • HTTP запросы к API │ │
│ │ • Ограниченный доступ к системе │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ contextBridge
┌─────────────────────────────────────────────────────────────┐
│ PRELOAD SCRIPT │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ src/preload/preload.js │ │
│ │ • Безопасный мост между main и renderer │ │
│ │ • Доступ к Node.js API │ │
│ │ • Экспорт API через window.api │ │
│ │ • Изоляция от прямого доступа к Node.js │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### Детальное объяснение процессов
#### 1. Main Process (Основной процесс)
**Роль**: Ядро приложения, управляет всей жизнью приложения.
**Возможности**:
- Создание и управление окнами
- Доступ к Node.js API (файловая система, диалоги, системные функции)
- Обработка системных событий (закрытие приложения, фокус окон)
- IPC сервер для связи с renderer процессами
**Код в `src/main/main.js`**:
```javascript
const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron');
// Создание главного окна с настройками безопасности
function createWindow() {
mainWindow = new BrowserWindow({
width: 1100,
height: 800,
webPreferences: {
preload: path.join(__dirname, '../preload/preload.js'),
contextIsolation: true, // КРИТИЧНО: изолирует контекст
nodeIntegration: false, // КРИТИЧНО: отключает прямой доступ к Node.js
sandbox: false // Позволяет preload работать
}
});
}
// IPC обработчики - "серверная часть" для renderer
ipcMain.handle('file:open', async () => {
// Безопасная работа с файлами через main процесс
const res = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'YAML/Encrypted', extensions: ['yaml', 'yml', 'encrypted', 'txt'] }]
});
// Возвращаем данные в renderer процесс
return { canceled: res.canceled, filePath: res.filePaths[0], content: fs.readFileSync(res.filePaths[0], 'utf-8') };
});
```
#### 2. Renderer Process (Процесс рендеринга)
**Роль**: Отображение пользовательского интерфейса, взаимодействие с пользователем.
**Ограничения**:
- Нет прямого доступа к Node.js API
- Работает как обычная веб-страница
- Изолирован от файловой системы
- Может делать HTTP запросы
**Код в `src/renderer/renderer.js`**:
```javascript
// Используем безопасный API из preload
window.addEventListener('DOMContentLoaded', () => {
// Обработчики UI событий
document.getElementById('openFile').addEventListener('click', async () => {
// Вызов через contextBridge API
const result = await window.api.openFile();
if (!result.canceled) {
// Обновляем UI с данными файла
document.getElementById('configYAML').value = result.content;
}
});
// HTTP запросы к backend API
document.getElementById('execute').addEventListener('click', async () => {
const response = await fetch('http://localhost:8080/api/v1/knock-actions/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...basicAuthHeader(password) },
body: JSON.stringify(body)
});
});
});
```
#### 3. Preload Script (Preload скрипт)
**Роль**: Безопасный мост между main и renderer процессами.
**Особенности**:
- Выполняется в renderer процессе
- Имеет доступ к Node.js API
- Изолирован от глобального контекста renderer
- Создает безопасный API через `contextBridge`
**Код в `src/preload/preload.js`**:
```javascript
const { contextBridge, ipcRenderer } = require('electron');
// Создаем безопасный API для renderer процесса
contextBridge.exposeInMainWorld('api', {
// Файловые операции
openFile: () => ipcRenderer.invoke('file:open'),
saveAs: (payload) => ipcRenderer.invoke('file:saveAs', payload),
saveToPath: (payload) => ipcRenderer.invoke('file:saveToPath', payload),
revealInFolder: (filePath) => ipcRenderer.invoke('os:revealInFolder', filePath)
});
// Renderer процесс получает доступ к window.api
// Но НЕ имеет прямого доступа к require, fs, dialog и т.д.
```
### IPC (Inter-Process Communication) - Связь между процессами
#### Как работает IPC
``` text
┌─────────────┐ IPC Message ┌─────────────┐
│ Renderer │ ────────────────> │ Main │
│ Process │ │ Process │
│ │ <──────────────── │ │
└─────────────┘ IPC Response └─────────────┘
```
**Шаг 1**: Renderer процесс вызывает `window.api.openFile()`
**Шаг 2**: Preload скрипт отправляет IPC сообщение `'file:open'` в main процесс
**Шаг 3**: Main процесс обрабатывает сообщение и выполняет файловую операцию
**Шаг 4**: Main процесс возвращает результат через IPC
**Шаг 5**: Preload скрипт получает результат и возвращает его renderer процессу
#### Типы IPC сообщений в приложении
```javascript
// Main процесс (обработчики)
ipcMain.handle('file:open', handler); // Открытие файла
ipcMain.handle('file:saveAs', handler); // Сохранение файла
ipcMain.handle('file:saveToPath', handler); // Сохранение по пути
ipcMain.handle('os:revealInFolder', handler); // Показать в проводнике
// Preload скрипт (клиент)
ipcRenderer.invoke('file:open'); // Отправка запроса
ipcRenderer.invoke('file:saveAs', payload); // Отправка с данными
```
### Безопасность в Electron
#### Принципы безопасности
1. **Context Isolation** - изоляция контекста
```javascript
webPreferences: {
contextIsolation: true // Renderer не может получить доступ к Node.js
}
```
2. **Node Integration** - отключение интеграции Node.js
```javascript
webPreferences: {
nodeIntegration: false // Отключаем прямой доступ к require()
}
```
3. **Sandbox** - песочница
```javascript
webPreferences: {
sandbox: false // Позволяем preload работать
}
```
#### Почему такая архитектура?
**Проблема**: Renderer процесс работает с ненадежным контентом (HTML/JS от пользователя).
**Решение**: Изолируем renderer от Node.js API, но предоставляем безопасный доступ через preload.
```javascript
// ❌ НЕБЕЗОПАСНО (если включить nodeIntegration: true)
// В renderer процессе:
const fs = require('fs');
fs.readFileSync('/etc/passwd'); // Может прочитать системные файлы!
// ✅ БЕЗОПАСНО (через contextBridge)
// В renderer процессе:
const result = await window.api.openFile(); // Только разрешенные операции
```
## 🎯 Функциональность приложения
### Режимы работы
#### 1. Inline режим
```javascript
// Простые поля для быстрого ввода
const formData = {
password: 'user_password',
targets: 'tcp:127.0.0.1:22;tcp:192.168.1.1:80',
delay: '1s',
verbose: true,
waitConnection: false,
gateway: 'optional_gateway'
};
```
#### 2. YAML режим
```yaml
# Полная YAML конфигурация
targets:
- protocol: tcp
host: 127.0.0.1
ports: [22, 80]
wait_connection: true
- protocol: udp
host: 192.168.1.1
ports: [53]
delay: 1s
path: /etc/knocker/config.yaml # Путь на сервере
```
#### 3. Form режим
```javascript
// Табличная форма для добавления целей
const targets = [
{ protocol: 'tcp', host: '127.0.0.1', port: 22, gateway: '' },
{ protocol: 'udp', host: '192.168.1.1', port: 53, gateway: 'gw1' }
];
```
### Файловые операции
#### Открытие файлов
```javascript
// Main процесс
ipcMain.handle('file:open', async () => {
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'YAML/Encrypted', extensions: ['yaml', 'yml', 'encrypted', 'txt'] }]
});
if (result.canceled) return { canceled: true };
const filePath = result.filePaths[0];
const content = fs.readFileSync(filePath, 'utf-8');
return { canceled: false, filePath, content };
});
```
#### Сохранение файлов
```javascript
// Main процесс
ipcMain.handle('file:saveAs', async (event, payload) => {
const result = await dialog.showSaveDialog({
defaultPath: payload.suggestedName || 'config.yaml',
filters: [{ name: 'YAML/Encrypted', extensions: ['yaml', 'yml', 'encrypted', 'txt'] }]
});
if (result.canceled || !result.filePath) return { canceled: true };
fs.writeFileSync(result.filePath, payload.content, 'utf-8');
return { canceled: false, filePath: result.filePath };
});
```
### HTTP API интеграция
#### Basic Authentication
```javascript
function basicAuthHeader(password) {
const token = btoa(`knocker:${password}`);
return { Authorization: `Basic ${token}` };
}
// Использование в запросах
const response = await fetch('http://localhost:8080/api/v1/knock-actions/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...basicAuthHeader(password)
},
body: JSON.stringify(payload)
});
```
#### API endpoints
```javascript
const apiEndpoints = {
execute: '/api/v1/knock-actions/execute',
encrypt: '/api/v1/knock-actions/encrypt',
decrypt: '/api/v1/knock-actions/decrypt',
encryptFile: '/api/v1/knock-actions/encrypt-file'
};
```
### YAML обработка
#### Извлечение пути из YAML
```javascript
function extractPathFromYaml(text) {
try {
const doc = yaml.load(text);
if (doc && typeof doc === 'object' && typeof doc.path === 'string') {
return doc.path;
}
} catch (e) {
console.warn('Failed to parse YAML:', e);
}
return '';
}
```
#### Обновление пути в YAML
```javascript
function patchYamlPath(text, newPath) {
try {
const doc = text.trim() ? yaml.load(text) : {};
if (doc && typeof doc === 'object') {
doc.path = newPath || '';
return yaml.dump(doc, { lineWidth: 120 });
}
} catch (e) {
console.warn('Failed to update YAML path:', e);
}
return text;
}
```
#### Конвертация между режимами
```javascript
// Inline → YAML
function convertInlineToYaml(targetsStr, delay, waitConnection) {
const entries = targetsStr.split(';').filter(Boolean);
const config = {
targets: entries.map(entry => {
const [protocol, host, port] = entry.split(':');
return {
protocol: protocol || 'tcp',
host: host || '127.0.0.1',
ports: [parseInt(port) || 22],
wait_connection: waitConnection
};
}),
delay: delay || '1s'
};
return yaml.dump(config, { lineWidth: 120 });
}
// YAML → Inline
function convertYamlToInline(yamlText) {
const config = yaml.load(yamlText) || {};
const targets = [];
(config.targets || []).forEach(target => {
const protocol = target.protocol || 'tcp';
const host = target.host || '127.0.0.1';
const ports = target.ports || [target.port] || [22];
ports.forEach(port => {
targets.push(`${protocol}:${host}:${port}`);
});
});
return {
targets: targets.join(';'),
delay: config.delay || '1s',
waitConnection: !!(config.targets?.[0]?.wait_connection)
};
}
```
## 🔧 Разработка и отладка
### Настройка среды разработки
#### 1. Структура проекта
``` text
desktop/
├── src/
│ ├── main/
│ │ ├── main.js # Основной процесс (CommonJS)
│ │ └── main.ts # TypeScript версия (опционально)
│ ├── preload/
│ │ ├── preload.js # Preload скрипт (CommonJS)
│ │ └── preload.ts # TypeScript версия (опционально)
│ └── renderer/
│ ├── index.html # HTML разметка
│ ├── styles.css # Стили
│ ├── renderer.js # UI логика (ванильный JS)
│ └── renderer.ts # TypeScript версия (опционально)
├── assets/ # Иконки для сборки
├── dist/ # Собранные приложения
├── package.json # Конфигурация
├── README.md # Основная документация
└── DEVELOPMENT.md # Это руководство
```
#### 2. Зависимости
```json
{
"devDependencies": {
"electron": "^28.3.3", // Electron runtime
"electron-builder": "^26.0.12" // Сборка и пакетирование
},
"dependencies": {
"axios": "^1.12.2", // HTTP клиент (не используется в финальной версии)
"js-yaml": "^4.1.0" // YAML парсер
}
}
```
### Отладка
#### DevTools
```javascript
// В main.js автоматически открываются DevTools
mainWindow.webContents.openDevTools();
```
#### Логирование
```javascript
// Main процесс - логи в терминале
console.log('Main process:', data);
// Renderer процесс - логи в DevTools Console
console.log('Renderer process:', data);
// IPC отладка в preload
const originalInvoke = ipcRenderer.invoke;
ipcRenderer.invoke = function(channel, ...args) {
console.log(`IPC Request: ${channel}`, args);
return originalInvoke.call(this, channel, ...args).then(result => {
console.log(`IPC Response: ${channel}`, result);
return result;
});
};
```
#### Отладка файловых операций
```javascript
// В main.js добавить логирование
ipcMain.handle('file:open', async () => {
console.log('Opening file dialog...');
const result = await dialog.showOpenDialog({...});
console.log('Dialog result:', result);
// ...
});
```
### Тестирование
#### Локальное тестирование
```bash
# Запуск в режиме разработки
npm run dev
# Проверка функциональности:
# 1. Открытие файлов
# 2. Сохранение файлов
# 3. HTTP запросы к API
# 4. Переключение между режимами
# 5. Конвертация YAML ↔ Inline
```
#### Тестирование сборки
```bash
# Упаковка без установщика
npm run pack
# Полная сборка
npm run build
# Проверка на разных платформах
npm run build:win
npm run build:linux
npm run build:mac
```
## 📦 Сборка и распространение
### Electron Builder конфигурация
```json
{
"build": {
"appId": "com.knocker.desktop", // Уникальный ID приложения
"productName": "Knocker Desktop", // Имя продукта
"directories": {
"output": "dist" // Папка для сборки
},
"files": [
"src/**/*", // Исходный код
"node_modules/**/*" // Зависимости
],
"win": {
"target": "nsis", // Windows installer
"icon": "assets/icon.ico" // Иконка Windows
},
"linux": {
"target": "AppImage", // Linux portable app
"icon": "assets/icon.png" // Иконка Linux
},
"mac": {
"target": "dmg", // macOS disk image
"icon": "assets/icon.icns" // Иконка macOS
}
}
}
```
### Типы сборки
#### Windows
- **NSIS** - установщик с мастером установки
- **Portable** - портативная версия
- **Squirrel** - автообновления
#### Linux
- **AppImage** - портативное приложение
- **deb** - пакет для Debian/Ubuntu
- **rpm** - пакет для Red Hat/Fedora
- **tar.xz** - архив
#### macOS
- **dmg** - образ диска
- **pkg** - установщик пакета
- **mas** - Mac App Store
### Команды сборки
```bash
# Сборка для текущей платформы
npm run build
# Сборка для конкретных платформ
npm run build:win # Windows (NSIS)
npm run build:linux # Linux (AppImage)
npm run build:mac # macOS (DMG)
# Упаковка без установщика (для тестирования)
npm run pack
# Сборка без публикации
npm run dist
# Публикация (если настроено)
npm run publish
```
### Иконки и ресурсы
#### Требования к иконкам
``` text
assets/
├── icon.ico # Windows: 256x256, ICO формат
├── icon.png # Linux: 512x512, PNG формат
└── icon.icns # macOS: 512x512, ICNS формат
```
#### Создание иконок
```bash
# Из PNG в ICO (Windows)
convert icon.png -resize 256x256 icon.ico
# Из PNG в ICNS (macOS)
iconutil -c icns icon.iconset
```
### Автоматизация сборки
#### GitHub Actions пример
```yaml
name: Build Electron App
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.os }}
path: dist/
```
## 🚀 Производительность и оптимизация
### Оптимизация размера приложения
#### Исключение ненужных файлов
```json
{
"build": {
"files": [
"src/**/*",
"node_modules/**/*"
],
"asarUnpack": [
"node_modules/electron/**/*" // Исключаем Electron из asar
]
}
}
```
#### Tree shaking
```javascript
// Используем только нужные части библиотек
import { load, dump } from 'js-yaml'; // Вместо import * as yaml
```
### Оптимизация загрузки
#### Lazy loading
```javascript
// Загружаем YAML парсер только когда нужен
async function loadYamlParser() {
if (!window.jsyaml) {
await import('../../node_modules/js-yaml/dist/js-yaml.min.js');
}
}
```
#### Кэширование
```javascript
// Кэшируем результаты API запросов
const cache = new Map();
async function cachedApiCall(endpoint, data) {
const key = `${endpoint}:${JSON.stringify(data)}`;
if (cache.has(key)) {
return cache.get(key);
}
const result = await apiCall(endpoint, data);
cache.set(key, result);
return result;
}
```
## 🔒 Безопасность
### Принципы безопасности Electron
#### 1. Context Isolation
```javascript
webPreferences: {
contextIsolation: true // Изолирует контекст renderer от Node.js
}
```
#### 2. Node Integration
```javascript
webPreferences: {
nodeIntegration: false // Отключает прямой доступ к require()
}
```
#### 3. Sandbox
```javascript
webPreferences: {
sandbox: false // Позволяет preload работать (но только в preload)
}
```
#### 4. CSP (Content Security Policy)
```html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline';">
```
### Валидация входных данных
#### Проверка паролей
```javascript
function validatePassword(password) {
if (!password || password.length < 1) {
throw new Error('Пароль не может быть пустым');
}
return password;
}
```
#### Проверка файлов
```javascript
function validateFileContent(content) {
if (typeof content !== 'string') {
throw new Error('Неверный формат файла');
}
if (content.length > 10 * 1024 * 1024) { // 10MB лимит
throw new Error('Файл слишком большой');
}
return content;
}
```
#### Проверка API ответов
```javascript
async function safeApiCall(url, options) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('API call failed:', error);
throw error;
}
}
```
## 🐛 Устранение неполадок
### Частые проблемы и решения
#### 1. Приложение не запускается
```bash
# Проверка зависимостей
npm install
# Очистка и переустановка
rm -rf node_modules package-lock.json
npm install
# Проверка версии Node.js
node --version # Должна быть >= 16
```
#### 2. DevTools не открываются
```javascript
// Убедитесь что в main.js есть:
mainWindow.webContents.openDevTools();
// Или добавьте горячую клавишу:
mainWindow.webContents.on('before-input-event', (event, input) => {
if (input.control && input.shift && input.key.toLowerCase() === 'i') {
mainWindow.webContents.openDevTools();
}
});
```
#### 3. Файлы не открываются
```javascript
// Проверьте что backend запущен
const testConnection = async () => {
try {
const response = await fetch('http://localhost:8080/api/v1/health');
console.log('Backend is running');
} catch (error) {
console.error('Backend is not running:', error);
}
};
```
#### 4. Сборка не работает
```bash
# Очистка dist папки
rm -rf dist
# Проверка конфигурации
npm run build -- --debug
# Сборка с подробными логами
DEBUG=electron-builder npm run build
```
#### 5. IPC сообщения не работают
```javascript
// Проверьте что preload скрипт загружается
console.log('Preload loaded:', typeof window.api);
// Проверьте IPC каналы
ipcRenderer.invoke('test').then(result => {
console.log('IPC test result:', result);
});
```
### Отладка производительности
#### Профилирование
```javascript
// В main.js
const { performance } = require('perf_hooks');
const startTime = performance.now();
// ... код ...
const endTime = performance.now();
console.log(`Operation took ${endTime - startTime} milliseconds`);
```
#### Мониторинг памяти
```javascript
// В main.js
setInterval(() => {
const usage = process.memoryUsage();
console.log('Memory usage:', {
rss: Math.round(usage.rss / 1024 / 1024) + ' MB',
heapTotal: Math.round(usage.heapTotal / 1024 / 1024) + ' MB',
heapUsed: Math.round(usage.heapUsed / 1024 / 1024) + ' MB'
});
}, 5000);
```
### Логирование и мониторинг
#### Структурированное логирование
```javascript
// В main.js
const log = {
info: (message, data) => console.log(`[INFO] ${message}`, data),
error: (message, error) => console.error(`[ERROR] ${message}`, error),
debug: (message, data) => console.debug(`[DEBUG] ${message}`, data)
};
// Использование
log.info('Application started');
log.error('File operation failed', error);
```
#### Отслеживание ошибок
```javascript
// Глобальный обработчик ошибок
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// Можно отправить в сервис мониторинга
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
```
## 📚 Дополнительные ресурсы
### Документация
- [Electron Documentation](https://www.electronjs.org/docs)
- [Electron Builder](https://www.electron.build/)
- [Context Isolation](https://www.electronjs.org/docs/latest/tutorial/context-isolation)
- [IPC Communication](https://www.electronjs.org/docs/latest/tutorial/ipc)
### Лучшие практики
- [Electron Security](https://www.electronjs.org/docs/latest/tutorial/security)
- [Performance Best Practices](https://www.electronjs.org/docs/latest/tutorial/performance)
- [Distribution Guide](https://www.electronjs.org/docs/latest/tutorial/distribution)
### Инструменты разработки
- [Electron DevTools](https://www.electronjs.org/docs/latest/tutorial/devtools)
- [VS Code Electron Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-electron)
- [Electron Fiddle](https://www.electronjs.org/fiddle)
## 🤝 Вклад в разработку
### Процесс разработки
1. Форкните репозиторий
2. Создайте ветку для новой функции: `git checkout -b feature/new-feature`
3. Внесите изменения с тестами
4. Проверьте на всех платформах: `npm run build:win && npm run build:linux && npm run build:mac`
5. Создайте Pull Request с описанием изменений
### Стандарты кода
- Используйте ESLint для проверки JavaScript
- Комментируйте сложную логику
- Следуйте принципам безопасности Electron
- Тестируйте на всех поддерживаемых платформах
### Тестирование (another)
```bash
# Полный цикл тестирования
npm run dev # Тест в режиме разработки
npm run pack # Тест упакованной версии
npm run build # Тест финальной сборки
npm run build:win # Тест Windows версии
npm run build:linux # Тест Linux версии
npm run build:mac # Тест macOS версии
```
Это руководство покрывает все аспекты разработки Electron приложения Knocker Desktop. Используйте его как справочник при работе с проектом.