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

🛠️ Технологический стек

📋 Обзор технологий

Система охраны труда построена на современном технологическом стеке, обеспечивающем высокую производительность, масштабируемость и безопасность.

🖥️ Frontend

React Ecosystem

  • React 18.2+: современная библиотека для создания пользовательских интерфейсов
  • TypeScript 5.0+: типизированный JavaScript для повышения надежности кода
  • Vite 4.0+: быстрый инструмент сборки и разработки
{
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^5.0.0",
    "vite": "^4.0.0"
  }
}

UI Framework

  • shadcn/ui: современная библиотека компонентов
  • Tailwind CSS 3.0+: utility-first CSS фреймворк
  • Radix UI: низкоуровневые компоненты для доступности
// Пример использования shadcn/ui компонента
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"

const DocumentCard = ({ document }: { document: Document }) => (
  <Card>
    <CardHeader>
      <CardTitle>{document.name}</CardTitle>
    </CardHeader>
    <CardContent>
      <Button variant="outline">Скачать</Button>
    </CardContent>
  </Card>
)

State Management

  • TanStack Query: управление серверным состоянием
  • Zustand: легковесное управление клиентским состоянием
  • React Hook Form: управление формами с валидацией
// Пример использования TanStack Query
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'

const useDocuments = () => {
  return useQuery({
    queryKey: ['documents'],
    queryFn: fetchDocuments,
    staleTime: 5 * 60 * 1000, // 5 минут
  })
}

const useCreateDocument = () => {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: createDocument,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['documents'] })
    },
  })
}

Routing & Navigation

  • Wouter: легковесный роутер для React
  • React Router: альтернативный роутер (опционально)
import { Route, useLocation } from 'wouter'

const App = () => (
  <div>
    <Route path="/" component={Dashboard} />
    <Route path="/documents" component={Documents} />
    <Route path="/documents/:id" component={DocumentDetail} />
  </div>
)

🐍 Backend

Python Framework

  • Python 3.11+: современная версия Python с улучшенной производительностью
  • FastAPI 0.100+: высокопроизводительный веб-фреймворк
  • Pydantic 2.0+: валидация данных и сериализация
# Пример FastAPI эндпоинта
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
from typing import List

app = FastAPI(title="Safety API", version="1.0.0")

class DocumentCreate(BaseModel):
    name: str
    description: str | None = None
    category: str

@app.post("/documents/", response_model=DocumentResponse)
async def create_document(
    document: DocumentCreate,
    current_user: User = Depends(get_current_active_user),
    db: Session = Depends(get_db)
):
    """Создание нового документа"""
    db_document = Document(**document.dict(), uploaded_by=current_user.id)
    db.add(db_document)
    db.commit()
    db.refresh(db_document)
    return db_document

Database & ORM

  • PostgreSQL 14+: надежная реляционная база данных
  • SQLAlchemy 2.0+: современный Python ORM
  • Alembic: система миграций базы данных
# Пример модели SQLAlchemy
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = "safety_users"

    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(50), unique=True, index=True, nullable=False)
    email = Column(String(100), unique=True, index=True, nullable=False)
    full_name = Column(String(200), nullable=False)
    password_hash = Column(String(255), nullable=False)
    role = Column(String(20), default="user")
    is_active = Column(Boolean, default=True)
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

Authentication & Security

  • JWT: JSON Web Tokens для аутентификации
  • bcrypt: хеширование паролей
  • python-jose: работа с JWT токенами
  • passlib: библиотека для работы с паролями
# Пример аутентификации
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

🗄️ Database

PostgreSQL Features

  • JSON/JSONB: хранение неструктурированных данных
  • Full-text search: полнотекстовый поиск
  • Indexing: оптимизированные индексы
  • Partitioning: партиционирование больших таблиц
-- Пример использования JSONB
CREATE TABLE organizations (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    contacts JSONB,
    additional_data JSONB
);

-- Создание индекса для JSONB
CREATE INDEX idx_organizations_contacts ON organizations USING GIN (contacts);

-- Запрос с использованием JSONB
SELECT * FROM organizations 
WHERE contacts->>'email' = 'admin@example.com';

Database Optimization

  • Connection pooling: переиспользование соединений
  • Query optimization: оптимизация запросов
  • Caching: кэширование результатов
  • Monitoring: мониторинг производительности

📁 File Processing

Document Processing

  • openpyxl: работа с Excel файлами
  • python-docx: работа с Word документами
  • PyPDF2: обработка PDF файлов
  • python-magic: определение типов файлов
# Пример обработки Excel файла
import openpyxl
from openpyxl import load_workbook

def extract_excel_fields(file_path: str) -> dict:
    """Извлечение полей из Excel шаблона"""
    workbook = load_workbook(file_path)
    fields = {}

    for sheet_name in workbook.sheetnames:
        sheet = workbook[sheet_name]
        sheet_fields = []

        for row in sheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    if '{' in cell.value and '}' in cell.value:
                        # Извлекаем поля в фигурных скобках
                        import re
                        matches = re.findall(r'\{([^}]+)\}', cell.value)
                        sheet_fields.extend(matches)

        fields[sheet_name] = list(set(sheet_fields))

    return fields

File Storage

  • SFTP: безопасная передача файлов
  • Local storage: локальное хранение
  • File validation: проверка типов файлов
  • Virus scanning: сканирование на вирусы

🔗 External Integrations

API Integrations

  • httpx: асинхронные HTTP запросы
  • requests: синхронные HTTP запросы
  • FNS API: интеграция с налоговой службой
  • SMTP: отправка email уведомлений
# Пример интеграции с FNS API
import httpx
from typing import Optional

class FNSService:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api-fns.ru/api"

    async def get_organization_data(self, inn: str) -> Optional[dict]:
        """Получение данных организации из ФНС"""
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{self.base_url}/egr",
                params={"req": inn, "key": self.api_key}
            )
            if response.status_code == 200:
                return response.json()
        return None

Email Service

  • aiosmtplib: асинхронная отправка email
  • email-validator: валидация email адресов
  • Jinja2: шаблонизация email сообщений
# Пример отправки email
import aiosmtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

async def send_email(to: str, subject: str, body: str):
    """Отправка email уведомления"""
    message = MIMEMultipart()
    message["From"] = SMTP_USER
    message["To"] = to
    message["Subject"] = subject
    message.attach(MIMEText(body, "html"))

    await aiosmtplib.send(
        message,
        hostname=SMTP_HOST,
        port=SMTP_PORT,
        username=SMTP_USER,
        password=SMTP_PASSWORD,
        use_tls=True
    )

🚀 DevOps & Deployment

Containerization

  • Docker: контейнеризация приложений
  • Docker Compose: оркестрация для разработки
  • Multi-stage builds: оптимизация образов
# Multi-stage Dockerfile
FROM node:18-alpine AS frontend-build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM python:3.11-slim AS backend-build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

FROM python:3.11-slim AS production
WORKDIR /app
COPY --from=backend-build /app .
COPY --from=frontend-build /app/dist ./static
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Monitoring & Logging

  • Prometheus: сбор метрик
  • Grafana: визуализация метрик
  • ELK Stack: централизованное логирование
  • Sentry: отслеживание ошибок
# Пример настройки логирования
import logging
import structlog
from pythonjsonlogger import jsonlogger

# Настройка структурированного логирования
structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.processors.JSONRenderer()
    ],
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    wrapper_class=structlog.stdlib.BoundLogger,
    cache_logger_on_first_use=True,
)

CI/CD

  • GitHub Actions: автоматизация сборки и развертывания
  • Testing: pytest для backend, Jest для frontend
  • Code Quality: black, flake8, mypy для Python
  • Security: bandit для проверки безопасности
# GitHub Actions workflow
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
      - name: Run tests
        run: pytest tests/ --cov=app --cov-report=xml
      - name: Code quality
        run: |
          black --check app/
          flake8 app/
          mypy app/

🔧 Development Tools

Code Quality

  • Black: форматирование Python кода
  • Flake8: линтинг Python кода
  • MyPy: статическая типизация
  • Pre-commit: хуки для Git
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black
    rev: 23.3.0
    hooks:
      - id: black
        language_version: python3.11

  - repo: https://github.com/pycqa/flake8
    rev: 6.0.0
    hooks:
      - id: flake8

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.3.0
    hooks:
      - id: mypy
        additional_dependencies: [types-all]

Testing

  • pytest: тестирование Python кода
  • pytest-asyncio: тестирование асинхронного кода
  • pytest-cov: покрытие кода тестами
  • factory-boy: создание тестовых данных
# Пример теста
import pytest
from fastapi.testclient import TestClient
from app.main import app
from app.database import get_db
from tests.factories import UserFactory

client = TestClient(app)

@pytest.fixture
def test_user():
    return UserFactory()

def test_create_document(test_user):
    response = client.post(
        "/documents/",
        json={"name": "Test Document", "category": "safety"},
        headers={"Authorization": f"Bearer {test_user.token}"}
    )
    assert response.status_code == 201
    assert response.json()["name"] == "Test Document"

📊 Performance & Optimization

Caching

  • Redis: кэширование данных
  • Memcached: альтернативное кэширование
  • Application cache: кэширование на уровне приложения
  • CDN: кэширование статических ресурсов

Database Optimization

  • Connection pooling: пул соединений с БД
  • Query optimization: оптимизация SQL запросов
  • Indexing strategy: стратегия индексирования
  • Partitioning: партиционирование таблиц

Async Processing

  • Celery: асинхронная обработка задач
  • Redis: брокер сообщений
  • Background tasks: фоновые задачи
  • Queue management: управление очередями

Технологический стек выбран с учетом требований производительности, масштабируемости и современности разработки.