# AegisOne Engineering — План миграции инфраструктуры и переписывания сервисной части > **Статус:** План утверждён, готов к реализации > **Дата:** 2026-05-17 > **Команда:** Владелец + AI-ассистент > **Примечание:** Если при реализации возникнут вопросы — лучше уточнить, чем переделывать потом. --- ## 1. Текущее состояние ### 1.1. Публичная часть (aegisone.ru) - **Технологии:** PHP 7.4, MySQL, inline CSS/JS - **Хостинг:** Shared-хостинг (проблемы: HTTP 302-редирект, блокировка `/assets/`, отключённый `RewriteEngine`, PHP 7.4 тупик) - **Страницы:** Главная, услуги, блог, контакты, форма обратной связи - **SEO:** `robots.txt`, `sitemap.xml`, канонические URL - **Решение:** **Не трогать.** Перенести на VPS как есть в Docker-контейнер. ### 1.2. Сервисная часть (service.aegisone.ru) - **Технологии:** PHP 7.4, MySQL, inline CSS/JS - **Функционал:** Админ-панель (управление пользователями, объектами, SLA, опросниками, блогом, документами), расчёты (Risk Score, Object Index, SLA price), паспорта объектов - **Проблемы:** PHP 7.4 ограничивает развитие, нет автоматизации отчётов, нет интеграций - **Решение:** **Полностью переписать на Python/FastAPI + Jinja2** ### 1.3. Инфраструктура - **VPS:** Уже есть, на нём один проект (FastAPI + Uvicorn + Celery + Nginx + PostgreSQL + Redis + Alembic) - **Хостинг:** Платный, планируется отказ - **Git:** Нет централизованного сервера, работа на локальной машине - **SSL:** От регистратора домена (статический, неудобно для мультидомена) - **Домены:** `aegisone.ru` (публичная часть), планируется `service.aegisone.ru` (админка) --- ## 2. Целевая архитектура ``` ┌─────────────────────────────────────────────────────────────────────┐ │ VPS │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ Nginx (reverse proxy + SSL) │ │ │ │ порт 80/443, Let's Encrypt │ │ │ │ │ │ │ │ aegisone.ru ──────────────► php-fpm container (порт 9000) │ │ │ │ service.aegisone.ru ─────► fastapi container (порт 8000) │ │ │ │ other-project.ru ────────► project2 container (порт 8001) │ │ │ │ git.aegisone.ru ─────────► gitea container (порт 3000) │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │ │ AegisOne PHP │ │ AegisOne Python │ │ Other Project │ │ │ │ (публичная) │ │ (админка) │ │ (FastAPI) │ │ │ │ │ │ │ │ │ │ │ │ • Главная │ │ • Аутентификация│ │ • ... │ │ │ │ • Услуги │ │ • Пользователи │ │ │ │ │ │ • Блог │ │ • Объекты │ │ │ │ │ │ • Контакты │ │ • Клиенты │ │ │ │ │ │ • FAQ │ │ • SLA │ │ │ │ │ │ • Карусель │ │ • Опросники │ │ │ │ │ │ • Форма │ │ • Расчёты │ │ │ │ │ │ │ │ • Отчёты (PDF) │ │ │ │ │ │ │ │ • Документы │ │ │ │ │ │ │ │ • Yandex Disk │ │ │ │ │ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │ │ │ │ │ │ │ └──────────┬──────────┘ │ │ │ │ │ │ │ ┌──────────▼──────────┐ ┌─────────▼────────┐ │ │ │ PostgreSQL │ │ PostgreSQL │ │ │ │ (AegisOne БД) │ │ (Project 2 БД) │ │ │ │ │ │ │ │ │ │ • users │ │ • ... │ │ │ │ • customers │ │ │ │ │ │ • objects │ │ │ │ │ │ • sla_contracts │ │ │ │ │ │ • questionnaire_* │ │ │ │ │ │ • blog_posts │ │ │ │ │ │ • cases │ │ │ │ │ │ • documents │ │ │ │ │ │ • photos │ │ │ │ │ │ • audit_log │ │ │ │ │ └─────────────────────┘ └──────────────────┘ │ │ │ │ │ ┌──────────▼──────────┐ │ │ │ Redis │ (кэш, Celery broker) │ │ └─────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Celery Workers (Python) │ │ │ │ │ │ │ │ • Генерация отчётов (PDF, Excel) │ │ │ │ • Загрузка фото на Yandex Disk │ │ │ │ • Интеграция с 1С (по мере необходимости) │ │ │ │ • Фоновые расчёты, уведомления │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ Gitea │ │ Certbot │ │ │ │ (Git-сервер) │ │ (Let's Encrypt) │ │ │ │ │ │ │ │ │ │ • aegisone-php │ │ • aegisone.ru │ │ │ │ • aegisone-py │ │ • service.aeg.. │ │ │ │ • project2 │ │ • other.ru │ │ │ │ • CI/CD Actions │ │ • автообновление│ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Yandex Disk API (облачное хранилище) │ │ │ │ │ │ │ │ /AegisOne/ │ │ │ │ ├── objects/{object_id}/ │ │ │ │ │ ├── photos/{photo_id}.jpg │ │ │ │ │ └── documents/{doc_id}/ │ │ │ │ │ ├── {photo_id}.jpg │ │ │ │ │ └── report_{doc_id}.pdf │ │ │ │ └── ... │ │ │ │ │ │ │ │ В PostgreSQL хранятся: file_id, file_url, привязка к объекту│ │ │ └─────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## 3. Технологический стек ### 3.1. Публичная часть (без изменений) | Компонент | Технология | Примечание | |---|---|---| | Язык | PHP 7.4 | Не обновлять, не трогать | | БД | MySQL | Мигрировать на PostgreSQL позже (опционально) | | Веб-сервер | Nginx + PHP-FPM | В Docker-контейнере | | CSS/JS | Inline | Без изменений | ### 3.2. Сервисная часть (Python) | Компонент | Технология | Примечание | |---|---|---| | Язык | Python 3.11+ | | | Фреймворк | FastAPI | REST API + серверный рендеринг | | Шаблоны | Jinja2 | Серверный рендеринг, как сейчас PHP | | Веб-сервер | Uvicorn | ASGI-сервер | | ORM | SQLAlchemy 2.0 + Alembic | Миграции БД | | БД | PostgreSQL 15+ | Единая БД для всех проектов | | Аутентификация | JWT (python-jose) | Токены для API и сессий | | Фоновые задачи | Celery + Redis | Отчёты, загрузка файлов, интеграции | | Генерация отчётов | WeasyPrint (PDF), openpyxl (Excel) | | | Yandex Disk | yandex-disk SDK / REST API | OAuth 2.0 | | Валидация | Pydantic | Схемы данных | ### 3.3. Инфраструктура | Компонент | Технология | Примечание | |---|---|---| | Контейнеризация | Docker + Docker Compose | | | Reverse proxy | Nginx | Маршрутизация по доменам | | SSL | Let's Encrypt + Certbot | Автообновление | | Git-сервер | Gitea | Лёгкий, встроенный CI/CD | | CI/CD | Gitea Actions | Автоматический деплой | | Мониторинг | (опционально) Uptime Kuma | | ### 3.4. Мобильные приложения (будущее) | Компонент | Технология | Примечание | |---|---|---| | Фреймворк | Flutter | | | API | FastAPI REST endpoints | Те же, что для веб-админки | | Аутентификация | JWT | | --- ## 4. Почему Jinja2, а не SPA ### 4.1. Сравнение | Критерий | Jinja2 (SSR) | SPA (React/Vue) | |---|---|---| | Серверная нагрузка | Рендерит HTML на сервере | Отдаёт статику + JSON API | | Масштабирование | Горизонтальное: Nginx → N FastAPI инстансов | CDN для статики + Nginx → API | | Сложность разработки | Один стек, один деплой | Два стека, два деплоя, CORS | | Скорость разработки | Быстро | Медленнее | | Команда | 1-2 Python-разработчика | Python + JS/TS разработчики | | SEO | Не нужен (админка закрыта) | Не нужен | | Мобильные приложения | API тот же — Flutter подключается | API тот же — Flutter подключается | | Поддержка | Проще | Сложнее (два кодовых базы) | ### 4.2. Масштабирование Jinja2 по России При росте нагрузки: 1. **Вертикальное:** Увеличить ресурсы VPS (CPU, RAM) 2. **Горизонтальное:** Несколько инстансов FastAPI за Nginx (load balancing) 3. **Кэширование:** Redis для кэширования тяжёлых запросов 4. **CDN:** Для статики (CSS, JS, изображения) — Cloudflare или аналог Jinja2 **не является узким местом**. Узким местом будет БД или внешние API (Yandex Disk, 1С). ### 4.3. Вердикт **Jinja2 — правильный выбор.** Для B2B-админки SPA — это оверинжиниринг. Jinja2 даёт: - Быструю разработку - Простую поддержку - Легкое масштабирование - Единую кодовую базу - Готовность к мобильным приложениям (тот же API) --- ## 5. Этапы реализации ### Этап 1: Docker-фундамент на VPS (1-2 недели) **Цель:** Подготовить VPS для мультипроектной работы с Docker. #### 1.1. Установка Docker и Docker Compose ```bash # На VPS (Ubuntu/Debian) apt update && apt upgrade -y curl -fsSL https://get.docker.com | sh systemctl enable --now docker curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose ``` #### 1.2. Базовая структура директорий на VPS ``` /opt/ ├── nginx-proxy/ # Nginx reverse proxy + Certbot │ ├── docker-compose.yml │ ├── nginx.conf │ └── certs/ # Let's Encrypt сертификаты ├── gitea/ # Git-сервер │ ├── docker-compose.yml │ └── data/ # Данные Gitea ├── aegisone-php/ # Публичная часть (PHP) │ ├── docker-compose.yml │ └── src/ # Исходный код (git clone) ├── aegisone-py/ # Сервисная часть (Python) │ ├── docker-compose.yml │ ├── app/ # FastAPI приложение │ ├── migrations/ # Alembic миграции │ └── requirements.txt └── project2/ # Существующий проект └── docker-compose.yml ``` #### 1.3. Nginx reverse proxy + Certbot - Один Nginx-контейнер на портах 80/443 - Маршрутизация по `server_name`: - `aegisone.ru` → `aegisone-php:80` - `service.aegisone.ru` → `aegisone-py:8000` - `git.aegisone.ru` → `gitea:3000` - `other-project.ru` → `project2:8000` - Certbot в отдельном контейнере или встроенный (nginx-proxy + acme-companion) #### 1.4. Gitea (Git-сервер) - Docker-образ `gitea/gitea:latest` - Порт 3000 → `git.aegisone.ru` - PostgreSQL для данных Gitea - Встроенный CI/CD (Gitea Actions) - Репозитории: - `aegisone-php` (публичная часть, перенос с текущего хостинга) - `aegisone-py` (новая сервисная часть) - `project2` (существующий проект) - `infra` (конфигурации Docker, Nginx, CI/CD) #### 1.5. Обёртка существующего проекта в Docker - Создать `docker-compose.yml` для project2 - Протестировать работоспособность - Настроить домен `other-project.ru` **Критерий завершения этапа:** - [ ] Docker + Docker Compose установлены - [ ] Nginx reverse proxy работает, маршрутизирует по доменам - [ ] Certbot выдаёт SSL-сертификаты (Let's Encrypt) - [ ] Gitea запущен на `git.aegisone.ru` - [ ] Существующий проект работает в Docker - [ ] Все домены доступны по HTTPS --- ### Этап 2: Перенос AegisOne на VPS (1 неделя) **Цель:** Перенести публичную часть с хостинга на VPS, отключить хостинг. #### 2.1. Docker-контейнер для PHP ```yaml # /opt/aegisone-php/docker-compose.yml services: nginx: image: nginx:alpine ports: - "9080:80" # Внутренний порт, внешний через reverse proxy volumes: - ./src:/var/www/html - ./nginx.conf:/etc/nginx/conf.d/default.conf depends_on: - php php: image: php:7.4-fpm volumes: - ./src:/var/www/html environment: - DB_HOST=mysql - DB_NAME=aegisone - DB_USER=aegisone - DB_PASS= mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: MYSQL_DATABASE: aegisone MYSQL_USER: aegisone MYSQL_PASSWORD: volumes: - mysql_data:/var/lib/mysql volumes: mysql_data: ``` #### 2.2. Перенос файлов - Скачать все файлы с хостинга (FTP/SFTP) - Загрузить в `/opt/aegisone-php/src/` - Настроить `config.php` для нового окружения (DB_HOST=mysql) #### 2.3. Миграция БД - Экспорт MySQL с хостинга: `mysqldump -u user -p db_name > backup.sql` - Импорт в Docker MySQL: `docker exec -i aegisone-php-mysql mysql -u aegisone -p aegisone < backup.sql` - Проверить работоспособность #### 2.4. Настройка домена - DNS: `aegisone.ru` → IP VPS - Nginx reverse proxy: `server_name aegisone.ru` → `aegisone-php:9080` - SSL: Certbot для `aegisone.ru` #### 2.5. Тестирование и отключение хостинга - Проверить все страницы: главная, услуги, блог, контакты, форма - Проверить карусель, FAQ, тему (светлая/тёмная) - Проверить Яндекс.Карту с переключением темы - После подтверждения — отключить хостинг **Критерий завершения этапа:** - [ ] Публичная часть работает на VPS - [ ] Все страницы загружаются корректно - [ ] Форма обратной связи работает - [ ] Яндекс.Карта переключает тему - [ ] Домен `aegisone.ru` указывает на VPS - [ ] Хостинг отключён --- ### Этап 3: Python-админка (FastAPI + Jinja2) (3-5 недель) **Цель:** Полностью переписать сервисную часть на Python. #### 3.1. Структура проекта ``` aegisone-py/ ├── docker-compose.yml ├── Dockerfile ├── requirements.txt ├── alembic.ini ├── migrations/ # Alembic миграции ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI приложение │ ├── config.py # Настройки (env vars) │ ├── database.py # SQLAlchemy engine, session │ ├── models/ # SQLAlchemy модели │ │ ├── __init__.py │ │ ├── user.py │ │ ├── customer.py │ │ ├── object.py │ │ ├── sla.py │ │ ├── questionnaire.py │ │ ├── blog.py │ │ ├── case.py │ │ ├── document.py │ │ ├── photo.py │ │ └── audit_log.py │ ├── schemas/ # Pydantic схемы │ │ ├── __init__.py │ │ ├── user.py │ │ ├── customer.py │ │ └── ... │ ├── api/ # API endpoints │ │ ├── __init__.py │ │ ├── auth.py # Логин, JWT │ │ ├── users.py │ │ ├── customers.py │ │ ├── objects.py │ │ ├── sla.py │ │ ├── questionnaire.py │ │ ├── blog.py │ │ ├── cases.py │ │ ├── documents.py │ │ └── reports.py │ ├── templates/ # Jinja2 шаблоны │ │ ├── base.html # Базовый шаблон │ │ ├── login.html │ │ ├── dashboard.html │ │ ├── users/ │ │ ├── customers/ │ │ ├── objects/ │ │ ├── sla/ │ │ ├── questionnaire/ │ │ ├── blog/ │ │ ├── documents/ │ │ └── reports/ │ ├── static/ # CSS, JS, изображения │ │ ├── css/ │ │ │ ├── service.css # Стили админки │ │ │ └── ... │ │ ├── js/ │ │ │ ├── main.js │ │ │ └── ... │ │ └── img/ │ ├── services/ # Бизнес-логика │ │ ├── __init__.py │ │ ├── auth_service.py │ │ ├── calc_service.py # Расчёты (Risk Score, Object Index, SLA) │ │ ├── report_service.py # Генерация отчётов │ │ ├── yandex_disk_service.py # Интеграция с Yandex Disk │ │ └── ... │ ├── tasks/ # Celery задачи │ │ ├── __init__.py │ │ ├── reports.py │ │ ├── yandex_disk.py │ │ └── ... │ └── utils/ # Утилиты │ ├── __init__.py │ ├── password.py │ └── ... └── tests/ # Тесты (опционально) ``` #### 3.2. Миграция данных из MySQL в PostgreSQL - Создать Alembic миграции для всех таблиц - Написать скрипт миграции данных: - `users` → `users` - `customers` → `customers` - `objects` → `objects` - `sla_contracts` → `sla_contracts` - `questionnaire_sessions`, `questionnaire_answers`, `questionnaire_items` → аналогично - `blog_posts` → `blog_posts` - `cases` → `cases` - `audit_log` → `audit_log` - Протестировать миграцию на копии БД #### 3.3. Аутентификация - JWT-токены (python-jose) - Логин/пароль (bcrypt) - Роли: `owner`, `engineer`, `technician` - Сессии через cookies (для Jinja2) + JWT (для API) - CSRF-защита для форм #### 3.4. CRUD-модули (по порядку приоритета) **3.4.1. Пользователи (`/service/users/`)** - Список, создание, редактирование, удаление - Роль `owner` нельзя удалить, нельзя изменить роль - Блокировка/разблокировка - Аудит действий **3.4.2. Клиенты (`/service/customers/`)** - Список, создание, редактирование, удаление - ИНН, КПП, юридический адрес, контакты - Связь с объектами и SLA **3.4.3. Объекты (`/service/objects/`)** - Список, создание, редактирование, удаление - Привязка к клиенту - Тип, адрес, площадь, сотрудники - Risk Score, Complexity Index, Object Index (авторасчёт) - SLA price (авторасчёт) **3.4.4. SLA контракты (`/service/sla/`)** - Список, создание, редактирование, удаление - Привязка к клиенту и объекту - Тарифы: Базовый, Оптимальный, Максимальный - Время реакции, периодичность ТО - Статус: active, expired, cancelled, negotiation **3.4.5. Опросник (`/service/questionnaire/`)** - Создание новой сессии - 5 шагов: Коммерческий, Технический, Эксплуатация, Риски, Расчёт SLA - Вопросы из БД (`questionnaire_items`) - Сохранение ответов - Авторасчёт: Risk Score, Complexity Index, Infrastructure Load, Service History, Object Index, SLA Price - Формирование паспорта объекта **3.4.6. Блог (`/service/blog/`)** - Список, создание, редактирование, удаление - Категории: audit, sla, incident, supervision, documentation, risk, cases - HTML-редактор (toolbar) - Автогенерация slug из заголовка - Статус: draft, published **3.4.7. Примеры из практики (`/service/cases/`)** - Список, создание, редактирование, удаление - Заголовок, текст, эффект - Сортировка, активность **3.4.8. Документы (`/service/documents/`)** - Список документов (из `service/docs/*.md`) - Ручная сортировка - Переименование - Гранулярные права: view, edit, cancel (по ролям) - JSON-хранилище прав (`docs_permissions.json`) #### 3.5. Расчёты (перенос из PHP) - `calc_risk_score()` — Risk Score - `calc_complexity()` — Complexity Index - `calc_infrastructure_load()` — Infrastructure Load - `calc_service_history()` — Service History - `calc_object_index()` — Object Index - `calc_sla_price()` — SLA Price - `risk_multiplier()` — множитель риска - `risk_label()` — текстовая метка риска - `object_class()` — класс объекта - `fmt_money()` — форматирование денег Все функции переписать на Python, покрыть тестами. #### 3.6. Дашборд (`/service/dashboard/`) - Метрики: количество объектов, активных SLA, открытых задач - Графики (Chart.js или аналог) - Быстрые действия - Последние активности (audit_log) #### 3.7. Фронтенд (Jinja2) - Базовый шаблон (`base.html`) с: - Боковой панелью (sidebar) с навигацией - Верхней панелью (header) с пользователем и переключателем темы - Основным контентом - Темы: system → dark → light (localStorage, как сейчас) - Кастомные скроллбары - Тултипы на кнопках - Модальные окна (создание/редактирование) - Таблицы с поиском и пагинацией - Адаптивность (мобильная версия) **Критерий завершения этапа:** - [ ] Все CRUD-модули работают - [ ] Расчёты корректны (сравнить с PHP-версией) - [ ] Миграция данных завершена - [ ] Фронтенд полностью функционален - [ ] Темы переключаются - [ ] Мобильная версия работает - [ ] `service.aegisone.ru` доступен по HTTPS --- ### Этап 4: Интеграции (2-3 недели) **Цель:** Добавить генерацию отчётов, интеграцию с Yandex Disk, фоновые задачи. #### 4.1. Yandex Disk API - Регистрация приложения в Yandex OAuth - Получение OAuth-токена - SDK: `yandex-disk` или REST API через `requests` - Структура папок: ``` /AegisOne/ ├── objects/{object_id}/ │ ├── photos/ │ │ └── {photo_id}.jpg │ └── documents/{doc_id}/ │ ├── {photo_id}.jpg │ └── report_{doc_id}.pdf └── ... ``` - Эндпоинты: - `POST /api/documents/{id}/upload-photo` — загрузка фото - `GET /api/documents/{id}/photos` — список фото - `DELETE /api/photos/{id}` — удаление фото - В БД: таблица `photos` (id, document_id, object_id, yandex_file_id, yandex_file_url, uploaded_at, uploaded_by) #### 4.2. Генерация отчётов - **PDF:** WeasyPrint (HTML → PDF) - Шаблон паспорта объекта - Шаблон отчёта по аудиту - Шаблон SLA-контракта - **Excel:** openpyxl - Экспорт списка объектов - Экспорт SLA-контрактов - Экспорт отчётов по KPI - Фоновая генерация через Celery (для тяжёлых отчётов) #### 4.3. Celery (фоновые задачи) - Broker: Redis - Задачи: - Генерация PDF-отчётов - Загрузка фото на Yandex Disk - Отправка уведомлений (email, Telegram) - Интеграция с 1С (по мере необходимости) - Мониторинг: Flower (веб-интерфейс для Celery) #### 4.4. Интеграция с 1С (опционально, по мере необходимости) - REST API 1С или OData - Синхронизация: клиенты, объекты, договоры - Фоновая задача Celery **Критерий завершения этапа:** - [ ] Yandex Disk API интегрирован - [ ] Фото загружаются и привязываются к документам - [ ] PDF-отчёты генерируются - [ ] Excel-экспорт работает - [ ] Celery запущен, задачи выполняются - [ ] (Опционально) Интеграция с 1С настроена --- ### Этап 5: CI/CD и автоматизация деплоя (1 неделя) **Цель:** Настроить автоматический деплой при push в Git. #### 5.1. Gitea Actions (CI/CD) - Встроенный CI/CD в Gitea (совместим с GitHub Actions) - Workflow для `aegisone-py`: ```yaml name: Deploy AegisOne Python on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build and push Docker image run: | docker build -t registry.git.aegisone.ru/aegisone-py:latest . docker push registry.git.aegisone.ru/aegisone-py:latest - name: Deploy to VPS run: | ssh user@vps "cd /opt/aegisone-py && docker compose pull && docker compose up -d" ``` #### 5.2. Структура репозиториев в Gitea ``` git.aegisone.ru/ ├── aegisone/aegisone-php # Публичная часть (PHP) ├── aegisone/aegisone-py # Сервисная часть (Python) ├── aegisone/infra # Docker, Nginx, CI/CD конфиги └── aegisone/project2 # Существующий проект ``` #### 5.3. Автоматический деплой - Push в `main` → Gitea Actions → сборка Docker-образа → деплой на VPS - Rollback: `docker compose down && docker compose up -d` с предыдущим образом - Миграции БД: Alembic `upgrade head` автоматически при деплое #### 5.4. Локальная разработка - `docker-compose.dev.yml` для локальной разработки - Hot-reload для Python (uvicorn --reload) - Локальная БД (PostgreSQL в Docker) - Синхронизация с VPS: `git push` → автоматический деплой **Критерий завершения этапа:** - [ ] Gitea Actions настроены для всех проектов - [ ] Push в `main` → автоматический деплой - [ ] Rollback работает - [ ] Миграции БД применяются автоматически - [ ] Локальная разработка через Docker --- ## 6. Миграция данных: MySQL → PostgreSQL ### 6.1. Таблицы для миграции | MySQL таблица | PostgreSQL таблица | Примечание | |---|---|---| | `users` | `users` | Без изменений | | `login_attempts` | `login_attempts` | Без изменений | | `audit_log` | `audit_log` | Без изменений | | `objects` | `objects` | + `customer_id` FK | | `object_assignments` | `object_assignments` | Без изменений | | `tasks` | `tasks` | Без изменений | | `task_comments` | `task_comments` | Без изменений | | `customers` | `customers` | Новая таблица | | `sla_contracts` | `sla_contracts` | + `customer_id` FK, `contract_number`, `description`, `response_time_hours` | | `incidents` | `incidents` | Без изменений | | `engineer_kpi` | `engineer_kpi` | Без изменений | | `questionnaire_sessions` | `questionnaire_sessions` | Без изменений | | `questionnaire_answers` | `questionnaire_answers` | Без изменений | | `questionnaire_items` | `questionnaire_items` | Новая таблица | | `object_passports` | `object_passports` | Без изменений | | `blog_posts` | `blog_posts` | ENUM категории изменён | | `cases` | `cases` | Без изменений | ### 6.2. Скрипт миграции ```python # scripts/migrate_mysql_to_postgres.py import mysql.connector import psycopg2 from psycopg2.extras import execute_batch # Подключение к MySQL mysql_conn = mysql.connector.connect(host='...', user='...', password='...', database='...') mysql_cursor = mysql_conn.cursor(dictionary=True) # Подключение к PostgreSQL pg_conn = psycopg2.connect(host='...', user='...', password='...', database='...') pg_cursor = pg_conn.cursor() # Миграция каждой таблицы tables = ['users', 'customers', 'objects', 'sla_contracts', ...] for table in tables: mysql_cursor.execute(f"SELECT * FROM {table}") rows = mysql_cursor.fetchall() if rows: columns = rows[0].keys() placeholders = ', '.join(['%s'] * len(columns)) cols = ', '.join(columns) execute_batch( pg_cursor, f"INSERT INTO {table} ({cols}) VALUES ({placeholders}) ON CONFLICT DO NOTHING", [tuple(row[col] for col in columns) for row in rows] ) pg_conn.commit() ``` ### 6.3. Проверка миграции - Сравнить количество записей в MySQL и PostgreSQL - Проверить FK-связи - Протестировать CRUD-операции на PostgreSQL - Откат: сохранить бэкап MySQL до миграции --- ## 7. Безопасность ### 7.1. Аутентификация и авторизация - JWT-токены с expiration (1 час) - Refresh tokens (7 дней) - Роли: `owner`, `engineer`, `technician` - Гранулярные права для документов (view, edit, cancel) - CSRF-защита для форм ### 7.2. Защита данных - Пароли: bcrypt (cost factor 12) - HTTPS для всех доменов (Let's Encrypt) - Firewall: только порты 80, 443, 22 (SSH) - Docker: изоляция контейнеров, нет root в контейнерах - БД: нет внешнего доступа, только из Docker-сети ### 7.3. Бэкапы - PostgreSQL: `pg_dump` ежедневно (cron) - Yandex Disk: файлы уже в облаке - Gitea: бэкап репозиториев (tar) - Хранение бэкапов: отдельный диск или облако ### 7.4. Мониторинг - (Опционально) Uptime Kuma для мониторинга доступности - Логи: Docker logs + ротация - Алерты: email/Telegram при ошибках --- ## 8. Риски и митигация | Риск | Вероятность | Влияние | Митигация | |---|---|---|---| | Ошибка миграции данных | Средняя | Высокое | Тестирование на копии, бэкап MySQL | | Простои при деплое | Низкая | Среднее | Zero-downtime деплой (docker compose up -d) | | Проблемы с SSL | Низкая | Среднее | Certbot автообновление, мониторинг | | Yandex Disk API лимиты | Низкая | Низкое | Кэширование, retry logic | | Нехватка ресурсов VPS | Средняя | Высокое | Мониторинг CPU/RAM, масштабирование | | Ошибки в расчётах | Средняя | Высокое | Тесты, сравнение с PHP-версией | --- ## 9. Оценки времени | Этап | Описание | Оценка | |---|---|---| | Этап 1 | Docker-фундамент | 1-2 недели | | Этап 2 | Перенос AegisOne на VPS | 1 неделя | | Этап 3 | Python-админка | 3-5 недель | | Этап 4 | Интеграции | 2-3 недели | | Этап 5 | CI/CD | 1 неделя | | **Итого** | | **8-12 недель** | --- ## 10. Контрольные точки | Точка | Описание | Критерий успеха | |---|---|---| | КП1 | Docker-фундамент готов | Все сервисы запущены, домены работают по HTTPS | | КП2 | Публичная часть на VPS | aegisone.ru работает, хостинг отключён | | КП3 | Python-админка MVP | CRUD для пользователей, объектов, SLA работает | | КП4 | Python-админка полная | Все модули, расчёты, миграция данных завершены | | КП5 | Интеграции | Yandex Disk, отчёты, Celery работают | | КП6 | CI/CD | Автоматический деплой при push | --- ## 11. Открытые вопросы (уточнить перед реализацией) > **Важно:** Если при реализации возникнут вопросы — лучше уточнить, чем переделывать потом. 1. **Миграция данных:** Переносим все данные из MySQL в PostgreSQL или начинаем с чистой БД? (Рекомендация: мигрировать) 2. **Домен для админки:** `service.aegisone.ru` или `app.aegisone.ru`? (Рекомендация: `service.aegisone.ru`) 3. **Yandex Disk OAuth:** Использовать токен приложения или OAuth с авторизацией пользователя? (Рекомендация: токен приложения для простоты) 4. **Генерация отчётов:** Какие именно отчёты нужны в первую очередь? (Паспорт объекта, отчёт по аудиту, SLA-контракт?) 5. **Интеграция с 1С:** Какая версия 1С? Какой метод интеграции (REST API, OData, файловый обмен)? 6. **Мониторинг:** Нужен ли Uptime Kuma или другой мониторинг? (Рекомендация: да, для продакшена) 7. **Бэкапы:** Где хранить бэкапы PostgreSQL? (Отдельный диск на VPS, облако, другой сервер?) 8. **Тестирование:** Покрывать ли код тестами? (Рекомендация: да, хотя бы критические расчёты) 9. **Документация:** Вести ли документацию по API? (Рекомендация: да, через FastAPI автоматическую /docs) 10. **Логирование:** Какой уровень логирования? Куда писать логи? (Рекомендация: INFO в stdout, Docker logs) --- ## 12. Следующие шаги 1. **Утвердить план** — ответить на открытые вопросы (раздел 11) 2. **Начать Этап 1** — Docker-фундамент на VPS 3. **Параллельно** — создать репозитории в Gitea, настроить CI/CD skeleton 4. **После Этапа 1** — приступить к Этапу 2 (перенос PHP) 5. **После Этапа 2** — приступить к Этапу 3 (Python-админка) --- **Документ создан:** 2026-05-17 **Версия:** 1.0 **Автор:** AI-ассистент + Владелец **Статус:** Готов к реализации после утверждения