Приложение теперь может сворачиваться в трей вместо закрытия: обход продолжает работать в фоне, иконка в трее показывает статус и позволяет включить/выключить обход без открытия окна. Добавлен автозапуск самого приложения вместе с системой (свёрнутым в трей).
184 lines
9.5 KiB
TypeScript
184 lines
9.5 KiB
TypeScript
/**
|
||
* Единый контракт общения main ↔ renderer.
|
||
*
|
||
* Файл импортируется всеми тремя процессами (main, preload, renderer), поэтому
|
||
* здесь не должно быть импортов из `electron` или из DOM — только типы и константы.
|
||
*
|
||
* Чтобы добавить новый канал:
|
||
* 1. описать его в `IpcHandlers` (аргументы + результат);
|
||
* 2. зарегистрировать обработчик в `src/main/ipc.ts` (TS не даст ошибиться в типах);
|
||
* 3. пробросить метод наружу в `src/preload/index.ts`.
|
||
*/
|
||
|
||
export type ThemeSource = 'system' | 'light' | 'dark'
|
||
|
||
export interface ThemeState {
|
||
/** Что выбрал пользователь. */
|
||
source: ThemeSource
|
||
/** Что реально показывать сейчас (учитывает системную тему). */
|
||
shouldUseDarkColors: boolean
|
||
}
|
||
|
||
/**
|
||
* Фоновая работа: трей + автозапуск самого приложения.
|
||
*
|
||
* Не путать с `ZapretStatus.autoStart` — тот про автозапуск обхода (служба Windows,
|
||
* LaunchDaemon macOS) вообще без приложения.
|
||
*/
|
||
export interface BackgroundSettings {
|
||
/** Закрытие окна прячет его в трей, а не завершает приложение. */
|
||
closeToTray: boolean
|
||
/** Приложение запускается вместе с системой, сразу свёрнутым в трей. */
|
||
launchAtLogin: boolean
|
||
/**
|
||
* Иконка в трее реально создалась. На Linux без AppIndicator трея нет — тогда прятать
|
||
* туда окно нельзя, и всю группу настроек показывать бессмысленно.
|
||
*/
|
||
trayAvailable: boolean
|
||
}
|
||
|
||
/** Состояние обхода блокировок. */
|
||
export type ZapretState = 'stopped' | 'starting' | 'running' | 'stopping' | 'error'
|
||
|
||
/**
|
||
* Чем именно обходим на этой платформе — на всех трёх один и тот же движок уровня пакетов
|
||
* (форки zapret/nfqws), запросы прав отличаются:
|
||
* • `utunws-pf` — macOS: LaunchDaemon (`utunws` через utun+BPF), пароль администратора
|
||
* на каждое включение/выключение (`osascript`);
|
||
* • `winws-service` — Windows: служба с драйвером WinDivert;
|
||
* • `nfqws-nftables` — Linux: демон nfqws + правила nftables (NFQUEUE), root через pkexec
|
||
* на каждое включение;
|
||
* • `unsupported` — платформа без поддержки (не macOS/Windows/Linux).
|
||
*/
|
||
export type ZapretBackend = 'utunws-pf' | 'winws-service' | 'nfqws-nftables' | 'unsupported'
|
||
|
||
export interface ZapretStatus {
|
||
state: ZapretState
|
||
backend: ZapretBackend
|
||
/** Выбранный пресет из `zapret:strategies`. */
|
||
strategyId: string
|
||
/** Когда обход включён, ISO-8601. */
|
||
startedAt: string | null
|
||
/** Текст последней ошибки — показываем пользователю как есть. */
|
||
error: string | null
|
||
/** Windows: установлена ли служба (её ставит и настраивает само приложение при первом включении). */
|
||
serviceInstalled: boolean
|
||
/** macOS/Windows: автозапуск с системой без запущенного приложения. На Linux не используется. */
|
||
autoStart: boolean
|
||
}
|
||
|
||
/** Готовый набор параметров обхода. Подбирается под провайдера. */
|
||
export interface ZapretStrategy {
|
||
id: string
|
||
name: string
|
||
description: string
|
||
platforms: NodeJS.Platform[]
|
||
}
|
||
|
||
export interface ZapretLogLine {
|
||
level: 'info' | 'error'
|
||
line: string
|
||
}
|
||
|
||
/**
|
||
* Списки сайтов, применяемые обоими движками (см. `src/main/zapret/lists.ts`).
|
||
* Встроенное содержимое берётся из Flowseal/zapret-discord-youtube (MIT) — пользователь
|
||
* может редактировать любой список полностью, включая встроенные.
|
||
*/
|
||
export type ZapretListId = 'general' | 'google' | 'exclude' | 'ipset-exclude' | 'ipset-all'
|
||
|
||
export interface ZapretList {
|
||
id: ZapretListId
|
||
/** Домены (general/google/exclude) или IP/CIDR (ipset-exclude/ipset-all), уже без пустых строк и комментариев. */
|
||
entries: string[]
|
||
/** Совпадает ли содержимое с текущей встроенной версией — пользователь ничего не менял. */
|
||
isDefault: boolean
|
||
}
|
||
|
||
/** Сайт, который проверяет `src/main/zapret/diagnostics.ts`. */
|
||
export interface ZapretDiagnosticTarget {
|
||
id: string
|
||
label: string
|
||
host: string
|
||
}
|
||
|
||
/**
|
||
* Результат проверки одного сайта (`src/main/zapret/diagnostics.ts`): успех — это успешный
|
||
* TLS-хендшейк до хоста через текущий бэкенд обхода, не полноценный HTTP-запрос.
|
||
*/
|
||
export interface ZapretDiagnosticResult extends ZapretDiagnosticTarget {
|
||
ok: boolean
|
||
ms: number | null
|
||
error: string | null
|
||
}
|
||
|
||
/**
|
||
* Каналы вида renderer → main → ответ (`ipcRenderer.invoke` / `ipcMain.handle`).
|
||
* Ключ — имя канала, значение — сигнатура обработчика.
|
||
*/
|
||
export interface IpcHandlers {
|
||
'theme:get': () => ThemeState
|
||
|
||
/**
|
||
* Версия приложения — `app.getVersion()`, то есть `"version"` из package.json той сборки,
|
||
* которая сейчас запущена. Раньше renderer держал её отдельной константой «в синхроне
|
||
* с package.json» вручную: константа осталась на 0.4.0 и релизы 0.5.0 и 0.5.1 показали
|
||
* пользователю чужую версию. Спрашивать у main — единственный способ не разъехаться.
|
||
*/
|
||
'app:version': () => string
|
||
|
||
/** Windows: подтверждает диалогом ОС, запускает деинсталлятор и закрывает приложение. */
|
||
'app:uninstall': () => void
|
||
|
||
'app:background-get': () => BackgroundSettings
|
||
/**
|
||
* Меняет переданные поля и возвращает состояние целиком. `launchAtLogin` идёт в систему
|
||
* (планировщик задач, SMAppService, каталог автозапуска) и может не примениться — тогда
|
||
* канал отвечает ошибкой, а не молча возвращает старое значение.
|
||
*/
|
||
'app:background-set': (patch: Partial<BackgroundSettings>) => BackgroundSettings
|
||
|
||
'zapret:status': () => ZapretStatus
|
||
'zapret:strategies': () => ZapretStrategy[]
|
||
'zapret:start': (strategyId: string) => ZapretStatus
|
||
'zapret:stop': () => ZapretStatus
|
||
/**
|
||
* Выбор пресета. Если обход выключен — просто запоминается. Если включён — на macOS
|
||
* переустанавливает LaunchDaemon с utunws (потребуется пароль администратора), на Windows
|
||
* перенастраивает службу (потребуется UAC), на Linux перезапускает nfqws и правила
|
||
* nftables (потребуется пароль через pkexec).
|
||
*/
|
||
'zapret:set-strategy': (strategyId: string) => ZapretStatus
|
||
/** macOS/Windows: автозапуск с системой; требует пароль администратора/UAC. */
|
||
'zapret:set-autostart': (enabled: boolean) => ZapretStatus
|
||
|
||
'zapret:lists': () => ZapretList[]
|
||
'zapret:list-save': (id: ZapretListId, entries: string[]) => ZapretList
|
||
'zapret:list-reset': (id: ZapretListId) => ZapretList
|
||
|
||
/**
|
||
* Запустить проверку соединения и сразу вернуть список проверяемых сайтов — сами
|
||
* результаты приходят по одному событием `zapret:diagnostic-result`, как только будет
|
||
* готов каждый. Сайты проверяются параллельно и отвечают вразнобой: ждать самый
|
||
* медленный, чтобы показать все разом, значит держать пользователя перед пустым
|
||
* экраном до последнего таймаута.
|
||
*/
|
||
'zapret:diagnose': () => ZapretDiagnosticTarget[]
|
||
}
|
||
|
||
export type IpcChannel = keyof IpcHandlers
|
||
|
||
/** Каналы вида main → renderer (`webContents.send` / `ipcRenderer.on`). */
|
||
export interface IpcEvents {
|
||
'theme:changed': ThemeState
|
||
'zapret:status-changed': ZapretStatus
|
||
'zapret:log': ZapretLogLine
|
||
/** Один готовый результат запущенной через `zapret:diagnose` проверки. */
|
||
'zapret:diagnostic-result': ZapretDiagnosticResult
|
||
}
|
||
|
||
export type IpcEventChannel = keyof IpcEvents
|
||
|
||
/** Разрешённые протоколы для `shell.openExternal` — всё остальное main отклоняет. */
|
||
export const ALLOWED_EXTERNAL_PROTOCOLS = ['https:', 'mailto:'] as const
|