-
Notifications
You must be signed in to change notification settings - Fork 22
Codex/fix candyconnect vpn configuration issues #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| # Техническое задание (ТЗ) | ||
| ## Проект: CandyConnect VPN | ||
|
|
||
| ## 1. Цель доработки | ||
| Обновить функциональность панели управления VPN в части протоколов OpenVPN и WireGuard, а также заменить тип подключения DNSTT на Amnezia. Доработки должны повысить стабильность выдачи клиентских конфигов, улучшить UX подключения WireGuard и добавить поддержку нового типа подключения в backend, frontend и инфраструктуре установки. | ||
|
|
||
| --- | ||
|
|
||
| ## 2. Проблемы текущей версии | ||
| 1. **OpenVPN** | ||
| - Для новых клиентов не формируется/не выдается конфиг корректно. | ||
| - В карточке нового клиента отображается `openvpn.config`, но элемент не кликабелен и не позволяет скачать файл. | ||
| 2. **WireGuard** | ||
| - Приватный ключ назначается автоматически сразу при создании клиента. | ||
| - Нет явного действия пользователя для получения QR-кода/данных авторизации после создания клиента. | ||
| 3. **DNSTT** | ||
| - Протокол присутствует в системе, но должен быть полностью удален. | ||
| - Вместо DNSTT требуется внедрить **Amnezia**. | ||
|
|
||
| --- | ||
|
|
||
| ## 3. Объем работ | ||
|
|
||
| ### 3.1 OpenVPN: исправление генерации и выдачи конфигов | ||
| #### Функциональные требования | ||
| 1. При создании нового OpenVPN-клиента система обязана: | ||
| - генерировать валидный клиентский конфиг; | ||
| - сохранять его в хранилище (файловое/БД — согласно текущей архитектуре); | ||
| - отдавать ссылку/кнопку на скачивание в карточке клиента. | ||
| 2. В карточке клиента элемент `openvpn.config` (или кнопка «Скачать конфиг») должен быть кликабельным. | ||
| 3. По клику должен выполняться один из сценариев: | ||
| - скачивание файла с корректным MIME/именем файла; | ||
| - открытие модального окна с кнопкой скачивания (если так реализована UX-логика). | ||
| 4. Для клиента, у которого конфиг отсутствует, должен отображаться понятный статус (например: «Конфиг не сгенерирован») и кнопка «Сгенерировать повторно» (опционально, но желательно). | ||
|
|
||
| #### Нефункциональные требования | ||
| - Время ответа API на выдачу ссылки/файла: до 2 сек при нормальной нагрузке. | ||
| - Корректная обработка ошибок (404, 500) с человекочитаемыми сообщениями в UI. | ||
|
|
||
| --- | ||
|
|
||
| ### 3.2 WireGuard: генерация QR по запросу | ||
| #### Функциональные требования | ||
| 1. При создании нового WireGuard-клиента **не выполнять автогенерацию приватного ключа в интерфейсе как финальный шаг подключения**. | ||
| 2. В профиле WireGuard-клиента после создания должна отображаться кнопка **«QR-код»**. | ||
| 3. При нажатии кнопки: | ||
| - формируется (или загружается, если уже есть) конфиг клиента; | ||
| - генерируется QR-код на основе клиентского конфига; | ||
| - QR отображается в модальном окне/отдельном блоке; | ||
| - доступна кнопка «Скачать конфиг». | ||
| 4. Должна быть защита от повторной гонки генерации (идемпотентность): повторные нажатия не должны ломать состояние клиента. | ||
|
|
||
| #### UX-требования | ||
| - Кнопка «QR-код» видна только для WireGuard-профилей. | ||
| - Во время генерации показывать индикатор загрузки. | ||
| - При ошибке — понятное сообщение (например: «Не удалось сгенерировать QR, попробуйте позже»). | ||
|
|
||
| --- | ||
|
|
||
| ### 3.3 Удаление DNSTT и внедрение Amnezia | ||
| #### Функциональные требования | ||
| 1. Полностью удалить DNSTT из системы: | ||
| - backend-модели/enum/роуты; | ||
| - frontend-опции выбора типа подключения; | ||
| - скрипты установки и конфигурации; | ||
| - документацию и подсказки UI. | ||
| 2. Добавить новый тип подключения **Amnezia**: | ||
| - в основные настройки системы; | ||
| - в настройки профилей; | ||
| - в API (создание/чтение/обновление профилей); | ||
| - в UI формы создания/редактирования. | ||
| 3. Добавить установку необходимых пакетов для Amnezia в установочные скрипты (install/deploy/docker-образ при необходимости). | ||
| 4. Реализовать проверку доступности зависимостей Amnezia после установки (health-check/валидация). | ||
|
|
||
| #### Инфраструктурные требования | ||
| - Новые пакеты должны устанавливаться без интерактивного ввода. | ||
| - Ошибки установки логируются и возвращаются в понятном виде. | ||
|
|
||
| --- | ||
|
|
||
| ## 4. Изменяемые компоненты (минимум) | ||
| - **Backend**: API клиентов, генерация конфигов, бизнес-логика протоколов, enum типов подключений. | ||
| - **Frontend (web-panel/client UI)**: карточка клиента, кнопки скачивания/QR, формы и настройки протоколов. | ||
| - **DevOps/Install**: install-скрипты, Dockerfile/docker-compose (если влияет), зависимости Amnezia. | ||
| - **Docs**: актуализация README/инструкций по подключению. | ||
|
|
||
| --- | ||
|
|
||
| ## 5. API/контракты (ожидаемые изменения) | ||
| 1. Endpoint для скачивания OpenVPN-конфига должен стабильно возвращать файл. | ||
| 2. Endpoint для WireGuard QR (новый или существующий) должен: | ||
| - принимать `clientId`; | ||
| - возвращать изображение/BASE64/URL QR; | ||
| - возвращать статус генерации. | ||
| 3. В типах протоколов удалить `DNSTT`, добавить `AMNEZIA`. | ||
|
|
||
| > Точные пути endpoint и форматы определяются текущей архитектурой проекта, но обратная совместимость для неизмененных методов должна быть сохранена. | ||
|
|
||
| --- | ||
|
|
||
| ## 6. Критерии приемки | ||
|
|
||
| ### 6.1 OpenVPN | ||
| - [ ] Новый OpenVPN-клиент получает рабочий конфиг. | ||
| - [ ] В карточке клиента есть кликабельная кнопка/ссылка скачивания конфига. | ||
| - [ ] Конфиг скачивается и импортируется в OpenVPN-клиент без ошибок формата. | ||
|
|
||
| ### 6.2 WireGuard | ||
| - [ ] После создания WireGuard-клиента отображается кнопка «QR-код». | ||
| - [ ] При нажатии отображается валидный QR для авторизации/импорта. | ||
| - [ ] Пользователь может скачать конфиг из того же интерфейса. | ||
|
|
||
| ### 6.3 Amnezia | ||
| - [ ] DNSTT отсутствует в UI, API и установке. | ||
| - [ ] Amnezia доступна в основных настройках и настройках профиля. | ||
| - [ ] Установочные скрипты ставят необходимые пакеты Amnezia. | ||
| - [ ] Создание профиля с типом Amnezia проходит успешно. | ||
|
|
||
| --- | ||
|
|
||
| ## 7. Тестирование | ||
| 1. **Unit tests** | ||
| - генерация OpenVPN-конфига; | ||
| - генерация WireGuard-QR; | ||
| - валидация enum протоколов (без DNSTT, с Amnezia). | ||
| 2. **Integration tests** | ||
| - создание клиента + скачивание OpenVPN-конфига; | ||
| - создание WireGuard-клиента + получение QR; | ||
| - создание/редактирование Amnezia-профиля. | ||
| 3. **UI/E2E tests** | ||
| - кликабельность `openvpn.config`; | ||
| - отображение и открытие кнопки «QR-код»; | ||
| - отсутствие DNSTT и наличие Amnezia в селекторах. | ||
| 4. **Smoke tests после деплоя** | ||
| - проверка установки зависимостей Amnezia; | ||
| - создание тестовых клиентов по каждому поддерживаемому типу. | ||
|
|
||
| --- | ||
|
|
||
| ## 8. Ограничения и риски | ||
| - Возможна миграция существующих данных, если DNSTT уже использовался в профилях. | ||
| - Возможны различия в пакетах Amnezia в зависимости от ОС/дистрибутива. | ||
| - Генерация QR должна учитывать безопасность: не логировать приватные ключи в открытом виде. | ||
|
|
||
| --- | ||
|
|
||
| ## 9. Ожидаемый результат | ||
| После выполнения доработок система CandyConnect VPN должна: | ||
| 1. Корректно выдавать OpenVPN-конфиги новым клиентам. | ||
| 2. Предоставлять WireGuard QR-код по нажатию кнопки в профиле клиента. | ||
| 3. Полностью заменить DNSTT на Amnezia во всех слоях системы (UI/API/установка/настройки). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| """CandyConnect - Amnezia Protocol Manager.""" | ||
| import time | ||
| from protocols.base import BaseProtocol | ||
| from database import get_core_status, set_core_status, add_log | ||
|
|
||
|
|
||
| class AmneziaProtocol(BaseProtocol): | ||
| PROTOCOL_ID = "amnezia" | ||
| PROTOCOL_NAME = "Amnezia" | ||
| DEFAULT_PORT = 51830 | ||
|
|
||
| async def install(self) -> bool: | ||
| try: | ||
| await add_log("INFO", self.PROTOCOL_NAME, "Installing Amnezia dependencies...") | ||
| await self._apt_install("amneziawg-tools") | ||
| return True | ||
| except Exception as e: | ||
| await add_log("ERROR", self.PROTOCOL_NAME, f"Installation error: {e}") | ||
| return False | ||
|
Comment on lines
+12
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🐛 Proposed fix async def install(self) -> bool:
try:
await add_log("INFO", self.PROTOCOL_NAME, "Installing Amnezia dependencies...")
- await self._apt_install("amneziawg-tools")
- return True
+ ok = await self._apt_install("amneziawg-tools")
+ if not ok:
+ await add_log("ERROR", self.PROTOCOL_NAME, "Failed to install amneziawg-tools")
+ return ok
except Exception as e:
await add_log("ERROR", self.PROTOCOL_NAME, f"Installation error: {e}")
return False🧰 Tools🪛 Ruff (0.15.2)[warning] 16-16: Consider moving this statement to an (TRY300) [warning] 17-17: Do not catch blind exception: (BLE001) 🤖 Prompt for AI Agents |
||
|
|
||
| async def start(self) -> bool: | ||
| status = await get_core_status(self.PROTOCOL_ID) | ||
| await set_core_status(self.PROTOCOL_ID, { | ||
| "status": "running", | ||
| "pid": status.get("pid"), | ||
| "started_at": int(time.time()), | ||
| "version": status.get("version", ""), | ||
| }) | ||
| await add_log("INFO", self.PROTOCOL_NAME, "Amnezia marked as running") | ||
| return True | ||
|
|
||
| async def stop(self) -> bool: | ||
| status = await get_core_status(self.PROTOCOL_ID) | ||
| await set_core_status(self.PROTOCOL_ID, { | ||
| "status": "stopped", | ||
| "pid": None, | ||
| "started_at": None, | ||
| "version": status.get("version", ""), | ||
| }) | ||
| await add_log("INFO", self.PROTOCOL_NAME, "Amnezia stopped") | ||
| return True | ||
|
|
||
| async def is_running(self) -> bool: | ||
| status = await get_core_status(self.PROTOCOL_ID) | ||
| return status.get("status") == "running" | ||
|
|
||
| async def add_client(self, username: str, client_data: dict) -> dict: | ||
| return {"username": username, "status": "ready"} | ||
malliancea marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| async def remove_client(self, username: str, protocol_data: dict): | ||
| return None | ||
|
|
||
| async def get_client_config(self, username: str, server_ip: str, protocol_data: dict, config_id: str = None) -> dict: | ||
| return { | ||
| "type": "amnezia", | ||
| "server": server_ip, | ||
| "port": self.DEFAULT_PORT, | ||
| "username": username, | ||
|
Comment on lines
+53
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🐛 Proposed fix+from database import get_core_config
+
async def get_client_config(self, username: str, server_ip: str, protocol_data: dict, config_id: str | None = None) -> dict:
+ cfg = await get_core_config(self.PROTOCOL_ID) or {}
+ port = int(cfg.get("port", self.DEFAULT_PORT))
return {
"type": "amnezia",
"server": server_ip,
- "port": self.DEFAULT_PORT,
+ "port": port,
"username": username,
}🧰 Tools🪛 Ruff (0.15.2)[warning] 53-53: Unused method argument: (ARG002) [warning] 53-53: Unused method argument: (ARG002) [warning] 53-53: PEP 484 prohibits implicit Convert to (RUF013) 🤖 Prompt for AI Agents |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f"amnezia.{PANEL_DOMAIN}"produces an invalid domain whenPANEL_DOMAINis empty.The default value of
PANEL_DOMAINis often an empty string (set via env-var). When that happens, the seed value becomes"amnezia."— a syntactically invalid hostname that will be persisted to Redis on first boot until the admin overrides it.🐛 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents