Initial commit

This commit is contained in:
2026-05-17 05:22:06 +03:00
commit ca4d00c895
155 changed files with 45216 additions and 0 deletions
@@ -0,0 +1,836 @@
# 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=<password>
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: <root_password>
MYSQL_DATABASE: aegisone
MYSQL_USER: aegisone
MYSQL_PASSWORD: <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-ассистент + Владелец
**Статус:** Готов к реализации после утверждения