Перейти к содержанию

Архитектура ARMory

ARMory — веб-приложение для управления документацией проектов: проекты → разделы → группы → элементы (файлы, заметки, ссылки). Реализовано на Python с использованием FastAPI, SQLAlchemy и Jinja2.

Стек

  • Backend: FastAPI + SQLAlchemy 2.0 (async) + aiosqlite
  • Frontend: Jinja2 templates + Bootstrap 5 + vanilla JS + SortableJS
  • База данных: SQLite (data/armory.db)
  • Хранилище файлов: локальная файловая система (data/uploads/) или S3-совместимое хранилище
  • Подключаемые приложения: реестр расширений, Docker-развёртывание и SSE-журнал операций
  • Документация: MkDocs + Material

Структура проекта

ARMory/
├── app/                    # Исходный код приложения
│   ├── __init__.py
│   ├── main.py             # Точка входа FastAPI
│   ├── config.py           # Pydantic Settings
│   ├── database.py         # Подключение к БД и фабрика сессий
│   ├── models.py           # SQLAlchemy модели
│   ├── schemas.py          # Pydantic схемы
│   ├── storage.py          # Абстракция хранилища файлов
│   ├── templates/          # Jinja2 шаблоны
│   ├── static/             # CSS, JS, изображения
│   └── routers/            # Модули API и страниц
│       ├── projects.py
│       ├── documents.py
│       ├── backup.py
│       ├── scheduler.py
│       ├── calendar.py
│       ├── alexandrite.py
│       ├── wopi.py
│       ├── collabora.py
│       └── ...
├── data/                   # Данные приложения
│   ├── armory.db           # База данных ARMory
│   ├── extensions.json     # Состояние подключаемых приложений
│   ├── uploads/            # Загруженные файлы
│   ├── alexandrite/        # Хранилище Alexandrite (автосоздаётся)
│   └── backups/            # Локальные резервные копии
├── docs/                   # Markdown-документация для MkDocs
├── site/                   # Собранная статика MkDocs
├── scripts/                # Скрипты для планировщика задач
├── backups/                # Пользовательская документация и экспорт
├── lib/                    # Общие shell-скрипты
├── tests/                  # Тесты (pytest)
├── compose.yml             # Docker Compose production
├── compose.dev.yml         # Docker Compose development
├── compose.gateway.yml     # Compose с внешним gateway
├── Dockerfile
├── pyproject.toml
├── uv.lock
└── run.sh                  # Скрипт запуска

Модель данных

Project
├── Section
│   └── Document (group)
│       └── DocumentItem (file, note, link)
├── Document (group without section)
│   └── DocumentItem
├── Task (kanban / scheduler)
├── TaskStatus (kanban columns)
├── TaskAttachment
└── CalendarEvent

Основные сущности

  • Project — верхний уровень, соответствует реальному проекту или продукту.
  • Section — раздел внутри проекта.
  • Document — группа элементов (ранее называлась «документ»). Может находиться внутри раздела или без раздела. Поддерживает ручную сортировку перетаскиванием.
  • DocumentItem — элемент группы: файл, заметка или ссылка. Элементы тоже можно сортировать внутри группы. Файл можно заменять, сохраняя название и историю.
  • Task — задача. Используется в kanban и планировщике: приоритет, дедлайн, несколько ответственных (связь many-to-many через task_assignees), теги, вложения.
  • TaskStatus — колонка kanban-доски проекта.
  • TaskAttachment — вложение к задаче: ссылка, файл или git-репозиторий.
  • CalendarEvent — событие календаря.
  • TaskStatusHistory — история входа задачи в каждую стадию проекта.
  • ProjectComment / ProjectCommentRead — обсуждение проекта и состояние прочтения для пользователя.
  • Affair — личная или общая заметка с дедлайном, признаком выполнения и публикацией в ежедневной сводке.
  • DailyNewsRead — дата, когда пользователь скрыл ежедневную сводку.
  • SidebarBlock / SidebarLink — боковые панели с пользовательскими ссылками.

Хранилище файлов

Локальное хранилище

Файлы сохраняются в data/uploads/<project_id>_<slug>/.

При удалении проекта вся его папка удаляется автоматически.

S3

Поддерживается любое S3-совместимое хранилище. Префикс объектов: <project_id>_<slug>/.

Alexandrite

Отдельное хранилище знаний в формате Markdown с двухпанельным интерфейсом:

  • Локальный режим — полный доступ: создание, редактирование, переименование, удаление файлов и папок.
  • Режим Яндекс.Диска — read-only просмотр Markdown-файлов и папок, расположенных в YANDEX_DISK_ALEXANDRITE_PATH. Для ограничения корневой папки используется ALEXANDRITE_YANDEX_ROOT_PATH.

Интерфейс восстанавливает раскрытие дерева после файловых операций в пределах текущей страницы.

Подключаемые приложения

ARMory хранит реестр в data/extensions.json. Страница /applications запускает готовые Docker-образы с фиксированными безопасными аргументами и передаёт журнал установки через Server-Sent Events. В Docker-запуске операции выполняет внутренний extension-manager, которому одному подключён /var/run/docker.sock; его порт не публикуется. При локальном запуске используется Docker CLI пользователя. Встроенных подключений и специального кода для отдельных приложений нет.

Конфигурация

Конфигурация загружается из переменных окружения и файла .env через pydantic-settings.

Ключевые параметры:

app_name: str = "ARMory"
database_url: str = "sqlite+aiosqlite:///./armory.db"
storage_type: str = "local"          # local | s3
local_storage_path: str = "./data/uploads"
alexandrite_vault_path: str = "./data/alexandrite"
yandex_disk_path: str = "ARMory/data"
yandex_disk_backups_path: str = "ARMory/backups"
yandex_disk_alexandrite_path: str = "ARMory/alexandrite"
armory_public_url: str = "https://<your-domain>"
collabora_enabled: bool = False

Полный список переменных окружения см. в .env.example.

Жизненный цикл запроса

  1. FastAPI получает HTTP-запрос.
  2. Зависимости (Depends) предоставляют сессию БД (AsyncSession) и бэкенд хранилища (StorageBackend).
  3. Роутер выполняет бизнес-логику, обращается к БД и/или хранилищу.
  4. Jinja2-шаблон рендерит HTML или возвращается JSON.

Асинхронность

  • Все операции с БД выполняются через AsyncSession.
  • IO-bound операции (S3, Яндекс.Диск) выполняются асинхронно.
  • Долгие операции экспорта/архивирования и синхронизации с Яндекс.Диском запускаются в фоновых asyncio.create_task и отслеживаются по job_id.

Масштабируемость

Текущая архитектура рассчитана на один инстанс. SQLite и локальное хранилище не позволяют запускать несколько реплик без общего хранилища. Для горизонтального масштабирования потребуется:

  • PostgreSQL вместо SQLite
  • S3 вместо локальной ФС
  • Redis или аналог для фоновых задач