diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2044482 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.py] +indent_style = space +indent_size = 4 +max_line_length = 100 + +[*.{yml,yaml,json,ts,tsx,js,css,html}] +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc74b5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Python +__pycache__/ +*.py[cod] +.venv/ +.venv*/ +*.egg-info/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.coverage +coverage.xml +htmlcov/ + +# UI +node_modules/ +ui/dist/ +ui/.vite/ + +# Runtime / состояние шлюза (живёт в томах, не в репозитории) +data/ +config/ +*.db +*.db-shm +*.db-wal + +# Секреты и конфиги клиентов +.env +.env.* +!.env.example +*.conf +!deploy/*.conf.example + +# IDE / OS +.idea/ +.vscode/ +.DS_Store diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ca1bcb1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,24 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.9 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.11.2 + hooks: + - id: mypy + files: ^backend/ + additional_dependencies: [pydantic>=2.9] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-yaml + - id: check-merge-conflict + - id: check-added-large-files + args: [--maxkb=512] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ae4f41a --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +PY ?= python3 +VENV ?= .venv +BIN := $(VENV)/bin +IMAGE ?= awg-dash:dev +COMPOSE_DEV := deploy/docker-compose.dev.yml + +.PHONY: help venv lint fmt test build dev-up dev-down dev-logs test-netns + +help: + @grep -E '^[a-zA-Z_-]+:' $(MAKEFILE_LIST) | sed 's/:.*//' | sort -u + +venv: + $(PY) -m venv $(VENV) + $(BIN)/pip install -U pip + $(BIN)/pip install -r requirements-dev.txt + +lint: + $(BIN)/ruff check backend + $(BIN)/mypy backend + +fmt: + $(BIN)/ruff format backend + $(BIN)/ruff check --fix backend + +test: + $(BIN)/pytest + +build: + @test -f deploy/Dockerfile || { echo 'deploy/Dockerfile ещё нет — задача #18'; exit 1; } + docker build -f deploy/Dockerfile -t $(IMAGE) . + +dev-up: + @test -f $(COMPOSE_DEV) || { echo '$(COMPOSE_DEV) ещё нет — задача #21'; exit 1; } + docker compose -f $(COMPOSE_DEV) up -d --build + +dev-down: + docker compose -f $(COMPOSE_DEV) down + +dev-logs: + docker compose -f $(COMPOSE_DEV) logs -f --tail=100 + +test-netns: + @test -d tests/netns || { echo 'tests/netns ещё нет — задача #21'; exit 1; } + bash tests/netns/run.sh diff --git a/README.md b/README.md index 0b0ddfb..c7709a7 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,64 @@ # awg-dash -# awg-dash 🚀 - -**awg-dash** — это легковесная и удобная веб-панель (Dashboard) для визуального управления сервером **Amnezia WireGuard (AWG)**. Она позволяет создавать, удалять и просматривать конфигурации пользователей в один клик через современный веб-интерфейс, защищенный от блокировок. - -[![License: MIT](https://shields.io)](https://opensource.org) -[![GitHub Stars](https://shields.io)](https://github.com) - ---- - -## ✨ Особенности / Features - -- 🛠️ **Полная поддержка Amnezia WireGuard:** Тонкая настройка параметров обфускации (`Jc`, `Jmin`, `Jmax`, `S1`, `S2`, `H1`, `H2`, `H3`, `H4`) прямо из панели. -- 👥 **Управление пользователями:** Создание, удаление, временная заморозка и управление лимитами пиров (клиентов). -- 📊 **Мониторинг в реальном времени:** Графики потребления трафика, статус подключения клиентов и общая нагрузка на сервер. -- 📱 **QR-коды и конфиги:** Мгновенная генерация QR-кодов и скачивание `.conf` файлов для импорта в официальное приложение Amnezia. -- 🔒 **Безопасность:** Авторизация по паролю/токену, поддержка сессий и возможность работы за обратным прокси (Nginx, Caddy). -- 🐳 **Docker-first:** Быстрый запуск одной командой через Docker Compose. - ---- - -## 🛠️ Технологический стек / Tech Stack - -- **Backend:** Node.js (Express) / Python (FastAPI) / Go *(выберите нужное)* -- **Frontend:** Vue.js / React / Простой HTML+JS *(выберите нужное)* -- **База данных:** SQLite / JSON-file (минималистично, без тяжелых БД) -- **Ядро:** Amnezia WireGuard (модифицированная утилита `wg`) - ---- - -## 🚀 Быстрый старт / Quick Start - -### Способ 1: Через Docker Compose (Рекомендуемый) - -1. Клонируйте репозиторий: - ```bash - git clone https://github.com - cd awg-dash - ``` \ No newline at end of file +VPN-шлюз **AmneziaWG** с интеллектуальной маршрутизацией: один Docker-образ = туннель + policy routing + split-tunnel (GeoIP / DNS) + REST API v1 + Web UI (тёмная тема). + +- Эпик и декомпозиция: Gitea issue **#15** (`embeddevio/awg-dash`). +- Исполнение: Hermes Kanban, борда **`awg-dash`** (`hermes kanban --board awg-dash list`). +- PoC-референс: `embeddevio/amneziawg-docker-gateway` (bash + iptables, `docs/technical-specification.md`). + +## Архитектура + +Два процесса в одном образе: + +| Процесс | Пользователь | Ответственность | +|---|---|---| +| `awg-agent` | root | awg/wg-интерфейс, iptables/ipset, dnsmasq, policy routing, метрики | +| `awg-api` | непривилегированный | FastAPI: REST API v1, WebSocket, статика UI | + +Обмен — unix-socket `/run/awg/agent.sock`; состояние — SQLite в `/data`, конфиги `.conf` в `/config`. +Принятые решения зафиксированы в `docs/adr/`. + +## Быстрый старт (после релиза 1.0) + +```bash +docker run -dit --name=vpn-gateway \ + --network=host \ + --device=/dev/net/tun:/dev/net/tun \ + --cap-add=NET_ADMIN --cap-add=SYS_MODULE \ + --restart always \ + -v /srv/awg/config:/config -v /srv/awg/data:/data \ + registry.example.com/vpn-gateway:latest +``` + +Web UI: `http://:8080`. До релиза образ собирается локально: `make build`. + +## Разработка + +```bash +make venv # локальное окружение (.venv) +make lint # ruff + mypy +make test # pytest + coverage (порог 70%) +make build # docker build образа (задача #18) +make dev-up # dev-стенд (задача #21) +``` + +Сетевые тесты выполняются в netns-стенде (`tests/netns/`), без реального VPN-провайдера. + +## Структура репозитория + +| Путь | Назначение | +|---|---| +| `backend/awg_agent/` | привилегированный агент: туннель, маршрутизация, GeoIP, DNS, метрики | +| `backend/awg_api/` | FastAPI-приложение (REST + WS + статика UI) | +| `backend/tests/` | unit/integration-тесты (pytest) | +| `ui/` | React + TypeScript + Tailwind + shadcn-ui | +| `deploy/` | Dockerfile, compose-профили, entrypoint, Grafana-дашборд | +| `docs/adr/` | архитектурные решения | +| `tests/netns/` | сетевой тестовый стенд | + +## Процесс разработки + +1 задача = 1 issue = 1 ветка `feat/-slug` = 1 PR. + +Гейты мержа: CI green, coverage нового кода ≥ 70 %, security scan low/none, ревью владельца роли. +Merge в `main` и деплой в prod — только TechLead после гейтов. Все операции с репозиторием — через Gitea MCP. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4fb2f12 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,32 @@ +[project] +name = "awg-dash" +version = "0.0.1" +description = "AmneziaWG gateway with intelligent routing: REST API + Web UI + split-tunnel" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] + +[tool.ruff] +line-length = 100 +target-version = "py312" +extend-exclude = ["ui", ".venv", "backend/tests"] + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "N", "UP", "B", "ASYNC", "S", "RUF"] +ignore = ["S101"] + +[tool.ruff.lint.per-file-ignores] +"backend/tests/*" = ["S", "D"] + +[tool.mypy] +python_version = "3.12" +mypy_path = "backend" +files = ["backend"] +strict = true +ignore_missing_imports = true + +[tool.pytest.ini_options] +testpaths = ["backend/tests"] +pythonpath = ["backend"] +addopts = "-q --cov=awg_agent --cov=awg_api --cov-report=term-missing --cov-fail-under=70" +asyncio_mode = "auto" diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..9b729f2 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,8 @@ +-r requirements.txt + +ruff>=0.6.9 +mypy>=1.11 +pytest>=8.3 +pytest-cov>=5.0 +pytest-asyncio>=0.24 +pre-commit>=3.8 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dd3b550 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,12 @@ +# Runtime (контроль-плоскость) +fastapi>=0.115 +uvicorn[standard]>=0.32 +pydantic>=2.9 +pydantic-settings>=2.5 +aiosqlite>=0.20 +httpx>=0.27 +structlog>=24.4 +prometheus-client>=0.21 + +# Сетевой слой вызывается через бинари (awg, iptables, ipset, dnsmasq), +# поэтому python-зависимостей для сети не требуется.