Skip to content

Latest commit

 

History

186 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

University Journal System

REST API бэкенд электронного журнала университета — учёт посещаемости, управление оценками, формы оценивания и асинхронная генерация отчётов.


Защита ВКР — ключевые слайды

Архитектура системы

6. Архитектура

Архитектура построена как два контура. Первый сервис принимает внешние запросы, проверяет авторизацию с вузовской Keycloak, применяет ролевой доступ и передаёт запросы дальше. Внутренний сервис содержит бизнес-логику, взаимодействует с базой данных и с внешним расписанием. Есть контур наблюдаемости: метрики и логи. Система находится под вузовским VPN.

Ещё слайды защиты — результат, UI, нагрузка
Проектный результат

8. Проектный результат

Разработана серверная часть электронного журнала. Реализованы все 4 роли. API — 94 маршрута. Созданы ключевые процессы и механизмы управления; система подготовлена к запуску через CI/CD. Веб и мобильное приложение уже используют API в вузовском тестовом контуре.

Пример визуальной части

9. Визуальная часть

На слайде — скриншоты участников команды: кабинет администратора, журнал преподавателя и формирование ведомости. Показана только малая часть функций, работающих поверх этой серверной части.

Нагрузочное тестирование

11. Нагрузочное тестирование

Обработано более полумиллиона запросов; сервер держит ~1000 req/s. 4 реплики Edge за балансировщиком и gzip снижают объём ответов. Под нагрузкой среднее время ответа ~5 ms, p95 ~13 ms.

Полная презентация защиты ВКР (Google Slides)
10 слайдов · проблематика · цель и задачи · архитектура · результат · тестирование


Сервисы и порты

Все сервисы поднимаются через docker compose up -d.

Сервис Порт URL Учётные данные Назначение
Nginx 80 http://localhost Главная точка входа. Rate limiting, security headers, reverse proxy → Edge
Edge 8080 http://localhost:8080 HTTP-шлюз. Валидация JWT, CORS, middleware-цепочка, маршрутизация в Domain
Domain 8081 http://localhost:8081 Бизнес-логика. Посещаемость, оценки, отчёты, аудит, фоновые задачи
PostgreSQL 5432 journal_user / journal_pass Единственный источник истины. Все данные журнала
Keycloak внешний SSO KEYCLOAK_URL управляется вне репозитория IAM / OIDC. Выдаёт JWT-токены, управляет пользователями и ролями
Prometheus 9090 http://localhost:9090 Сбор метрик с Edge, Domain, cAdvisor каждые 15 секунд
Grafana 3000 http://localhost:3000 admin / admin Дашборды метрик + просмотр логов из Loki
Loki 3100 http://localhost:3100 Агрегация и хранение логов (бэкенд для Grafana)
cAdvisor 8090 http://localhost:8090 CPU / RAM / сеть по каждому контейнеру → Prometheus
node_exporter 9100 http://localhost:9100 Метрики хоста: CPU, RAM, disk, filesystem
postgres_exporter 9187 http://localhost:9187 Метрики PostgreSQL
Tempo 3200, 4317, 4318 http://localhost:3200 Хранилище OpenTelemetry trace-ов для Grafana
Promtail Агент логов: читает stdout Docker-контейнеров, шлёт в Loki

Оглавление


Что это такое?

University Journal System — бэкенд-платформа для ведения академических записей университета. Решает задачи:

  • Посещаемость — отметка, массовая отметка и запрос посещаемости по занятию
  • Оценки — создание и изменение записей об оценках студентов
  • Формы оценивания — группировка оценок по занятию или семестру
  • Отчёты — асинхронная генерация PDF/Excel с опросом статуса

Система разделена на два независимо разворачиваемых Go-бинарника за Nginx. Аутентификация делегирована Keycloak (OIDC/JWT). Все данные хранятся в PostgreSQL; отдельный кэш-сервис сейчас не используется.


Быстрый старт (5 минут)

Требования

Инструмент Версия Установка
Go 1.26.4 go version
Docker + Compose 24+ docker compose version
sqlc 1.26+ go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.26.0
oapi-codegen 2.x go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
goose latest go install github.com/pressly/goose/v3/cmd/goose@latest
golangci-lint 2.12+ make install-golangci-lint или go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2

Подсказки для Linux

Утилиты из таблицы ставятся через go install; бинарники попадают в $(go env GOPATH)/bin (часто это ~/go/bin). Пока этот каталог не в PATH, команды sqlc, oapi-codegen, goose не находятся — make generate падает с make: sqlc: No such file or directory или Error 127.

Один раз в текущей сессии:

export PATH="$PATH:$(go env GOPATH)/bin"

Чтобы не повторять после каждого входа, добавьте ту же строку в ~/.bashrc или ~/.profile (для login-shell — смотрите, какой файл читает ваш дистрибутив).

Проверка:

command -v sqlc && command -v oapi-codegen && command -v goose
sqlc version
oapi-codegen -version

Если бинарника всё ещё нет — сначала выполните go install из таблицы «Требования», затем снова проверьте command -v.

Docker без sudo: пользователь должен входить в группу docker (sudo usermod -aG docker "$USER", затем перелогиниться). Иначе make docker-up завершится отказом в доступу к сокету Docker.

Только PostgreSQL (без остальных сервисов)

Иногда удобно сначала поднять только БД: например, применить миграции и сиды с хоста до полного docker-up, или работать против Postgres локально без Edge.

Compose поднимает только сервис postgres (тот же volume postgres_data, те же учётные данные journal_user / journal_pass, порт 5432 на хосте). Образ приложения не собирается.

Основной стек (docker-compose.yml, Keycloak внешний):

make docker-db-up
# эквивалент: docker compose up -d --wait postgres

Стек prod-local (файл docker-compose.prod-local.yml; нужен .env.prod-local):

make docker-db-up-ext
# эквивалент: docker compose -f docker-compose.prod-local.yml --env-file .env.prod-local up -d --wait postgres

Флаг --wait дождётся healthcheck Postgres (pg_isready), после чего с хоста обычно срабатывает make migrate-up с дефолтным DB_DSN из Makefile (localhost:5432/journal_db). Затем можно запускать остальной стек (make docker-up или make docker-up-ext) — Postgres уже будет работать; Compose подключится к существующему контейнеру и volume.

Установка

Перед локальной сборкой go build (без Docker) выполните make generate: каталоги internal/db/generated/ и api/generated/ в .gitignore, иначе компиляция на машине разработчика упадёт.

Сборка образов Dockerfile.edge и Dockerfile.domain сама генерирует недостающий код (Edge — oapi-codegen, Domain — sqlc, те же версии, что в Makefile) и выполняет go mod tidy, чтобы не хранить артефакты в git и подтянуть недостающие записи в go.sum. Для работы в IDE и тестов без контейнеров по-прежнему удобнее один раз выполнить make generate локально.

# 1. Клонировать и войти в проект
git clone <repo-url>
cd university-journal-system

# 2. Создать файл переменных окружения
cp .env.example .env.local
# Отредактировать .env.local при необходимости (дефолты работают с docker-compose)

# 3. Сгенерировать код из SQL-миграций (sqlc) и OpenAPI-спеки (oapi-codegen)
#    Нужны установленные sqlc и oapi-codegen (см. таблицу «Требования»).
make generate

# 4. Запустить все сервисы и дождаться готовности (Compose --wait).
#    Первый запуск Keycloak может занять 1–3 минуты — это нормально.
#    Если нужна только БД на первом шаге: make docker-db-up (раздел «Только PostgreSQL» выше),
#    затем шаг 5 и позже полный стек — make docker-up.
make docker-up
# или вручную: docker compose up -d --wait --wait-timeout 600

# 5. Применить миграции БД с хоста (DSN по умолчанию — localhost:5432, совпадает с compose)
make migrate-up

Если make migrate-up сразу после шага 4 всё же вернул «connection refused», подождите несколько секунд и повторите: реже на очень медленном диске порт PostgreSQL на хосте открывается с задержкой. Если вы меняли пароль БД в compose, передайте тот же DSN в goose: DB_DSN='postgres://...' make migrate-up.

Проверить что работает

# Health-check
curl http://localhost/health           # через nginx
curl http://localhost:8080/health      # edge напрямую

# Readiness на Edge (пока без опроса Domain — см. internal/edge/handler/health.go)
curl http://localhost:8080/ready

# Проверка Domain (здесь реально пингуется БД и проверяются миграции)
curl http://localhost:8081/ready

# Интерактивная документация API (только вне prod)
# Windows: start http://localhost/api/docs
# macOS:   open  http://localhost/api/docs
# Linux:   xdg-open http://localhost/api/docs

Ожидаемые ответы:

// GET /health → 200
{"status": "ok"}

// GET :8080/ready → 200 (заглушка на Edge; детальные проверки — на Domain)
{"status": "ready"}

// GET :8081/ready → 200 когда БД доступна и миграции применены
{"status": "ready"}

Развёртывание prod-local (внешний Keycloak)

Стек docker-compose.prod-local.yml поднимает Edge, Domain, Postgres, Nginx и observability-контур; Keycloak находится во внешнем серверном контуре, а подключение задаётся переменными KEYCLOAK_* в .env.prod-local. Краткая инструкция также в шапке compose-файла.

Полный цикл: новая версия кода + работающая БД

Сборка образов не запускает SQL-миграции. Данные Postgres лежат в именованном volume (postgres_data); goose выполняется отдельно с хоста (или из CI) против той же БД, к которой подключается контейнер domain.

Шаг Что сделать
1 Убедиться, что при необходимости выполнен make generate (менялись OpenAPI или SQL/sqlc).
2 Пересобрать и запустить контейнеры (см. ниже).
3 Применить миграции к БД на хосте: make migrate-up с корректным DB_DSN (см. ниже). Пропуск шага при новых файлах в internal/db/migrations/ приводит к тому, что Domain долго отвечает 503 на GET /ready с причиной вроде database migrations not up to date.
4 Проверить curl http://localhost:8081/ready и при необходимости Edge curl http://localhost:8080/ready.

Переменные окружения

Скопируйте шаблон и заполните значения для вашего Keycloak:

cp .env.example .env.prod-local
# Отредактировать KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID и при необходимости остальное

Запуск и пересборка образов

Базовая команда (пересобирает edge и domain из текущего репозитория и поднимает сервисы в фоне):

docker compose -f docker-compose.prod-local.yml --env-file .env.prod-local up -d --build

Эквивалент с дополнительными флагами уже зашит в Makefile: ожидание healthcheck-ов, удаление осиротевших контейнеров после правок compose:

make docker-up-ext

При подозрении на устаревший слой кэша Docker (образ «не подхватывает» изменения):

docker compose -f docker-compose.prod-local.yml --env-file .env.prod-local build --no-cache
docker compose -f docker-compose.prod-local.yml --env-file .env.prod-local up -d

После изменения списка сервисов в compose имеет смысл добавлять --remove-orphans, чтобы убрать старые контейнеры, которые больше не описаны в файле (в make docker-up-ext это уже есть).

Миграции базы данных

Строка подключения по умолчанию в Makefile совпадает с учётными данными Postgres из compose и пробросом порта 5432 на localhost:

# Дефолт: postgres://journal_user:journal_pass@localhost:5432/journal_db?sslmode=disable
make migrate-up

Явная форма (удобно при нестандартном порте или удалённом хосте):

DB_DSN='postgres://journal_user:journal_pass@127.0.0.1:5432/journal_db?sslmode=disable' make migrate-up

Проверить состояние миграций:

make migrate-status

Для локальных smoke-прогонов после миграций можно загрузить тестовые данные (идемпотентный сидер; на общей «боевой» БД используйте осознанно):

make prod-local-bootstrap   # migrate-up + seed-test-data

Остановка стека prod-local

make docker-down-ext
# или
docker compose -f docker-compose.prod-local.yml --env-file .env.prod-local down --remove-orphans

Логи

make docker-logs-ext

Архитектура

Схема системы

Клиент (браузер / мобайл / Postman)
         │
         ▼
    [Nginx :80]          ← rate limiting, security headers, reverse proxy
         │
         ▼
   [Edge :8080]          ← валидация JWT (Keycloak JWKS), роутинг, middleware
         │
         ├──→ [Keycloak :8180]   ← выдача OIDC-токенов и JWKS-endpoint
         │
         ▼
  [Domain :8081]         ← бизнес-логика: посещаемость, оценки, отчёты
         │
         └──→ [PostgreSQL :5432] ← все персистентные данные

Observability (отдельный пайплайн):
   Docker logs ──→ [Promtail] ──→ [Loki :3100]     ──→ [Grafana :3000]
   /metrics    ──→ [Prometheus :9090] ─────────────→ [Grafana :3000]
   контейнеры  ──→ [cAdvisor :8090]  ──→ [Prometheus]
   хост/БД     ──→ [node_exporter/postgres_exporter] ──→ [Prometheus]
   traces      ──→ [Tempo :4318] ─────────────────────→ [Grafana]

Разделение на два сервиса

Сервис Бинарник Зона ответственности
Edge cmd/edge HTTP-шлюз: auth middleware, CORS, request ID, Prometheus-метрики, проксирование в Domain
Domain cmd/domain Бизнес-логика: посещаемость, оценки, формы оценивания, асинхронные отчёты, аудит-лог, фоновые воркеры

Edge никогда не обращается к БД напрямую. Domain никогда не занимается аутентификацией.

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

Слой Технология Зачем
Язык Go 1.26.4 Один бинарник, быстрый старт, отличный конкурентный I/O
HTTP-фреймворк Gin Минимальные накладные расходы, middleware-первичность
Аутентификация Keycloak 24 + go-oidc/v3 Стандарт OIDC, поддержка университетского SSO
База данных PostgreSQL 16 Реляционная, ACID, драйвер pgx/v5
Кодогенерация БД sqlc 1.26 Type-safe SQL → Go, без ORM
Кодогенерация API oapi-codegen 2.x OpenAPI-спека → Go-стабы сервера + модели
Миграции goose SQL-based, транзакционные, идемпотентные
Observability Prometheus + Grafana + Loki + Tempo Метрики, логи и базовые OpenTelemetry traces
Прокси Nginx 1.25 Rate limiting, TLS-терминация, security headers

Справочник API

Интерактивная документация

Доступны два endpoint-а (по умолчанию при APP_ENV != prod; в prod — при API_DOCS_ENABLED=true):

URL Описание
http://localhost/api/docs Swagger UI — интерактивный просмотр и тестирование всех эндпоинтов
http://localhost/api/openapi.yaml OpenAPI 3.0 YAML — для импорта в Postman, кодогенерации клиентов, CI

В prod оба endpoint-а выключены, пока не задано API_DOCS_ENABLED=true.

Аутентификация

Все эндпоинты /api/v1/* требуют Bearer JWT-токен, выданный Keycloak:

Authorization: Bearer <access_token>

Получение токена:

Web и mobile логинятся во внешнем Keycloak через Authorization Code + PKCE (journal-web / journal-mobile) и отправляют в API только access_token. Backend client journal-backend используется как audience API, а не как пользовательский login-client.

Эндпоинты

Health

Метод Путь Авторизация Описание
GET /health Нет Liveness probe — сервис запущен
GET /ready Нет Readiness probe — PostgreSQL доступна, миграции применены
GET /metrics Нет Endpoint сбора Prometheus-метрик

Посещаемость

Метод Путь Авторизация Описание
GET /api/v1/lessons/{lesson_id}/attendance Bearer Получить все записи посещаемости занятия
POST /api/v1/lessons/{lesson_id}/attendance/mark Bearer (teacher/admin) Отметить посещаемость одного студента
POST /api/v1/lessons/{lesson_id}/attendance/bulk Bearer (teacher/admin) Массовая отметка посещаемости группы

Пример — отметить посещаемость:

curl -X POST http://localhost/api/v1/lessons/3fa85f64-5717-4562-b3fc-2c963f66afa6/attendance/mark \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "student_id": "3fa85f64-5717-4562-b3fc-2c963f66afa7",
    "status": "present",
    "note": ""
  }'

Оценки

Метод Путь Авторизация Описание
POST /api/v1/assessment-forms Bearer (teacher/admin) Создать форму оценивания для занятия
GET /api/v1/assessment-forms/{form_id}/grades Bearer Получить все оценки формы оценивания
POST /api/v1/grades Bearer (teacher/admin) Создать запись об оценке студента
PUT /api/v1/grades/{grade_id} Bearer (teacher/admin) Обновить существующую оценку

Отчёты

Метод Путь Авторизация Описание
POST /api/v1/reports Bearer Запустить асинхронную генерацию отчёта
GET /api/v1/reports/{report_id}/status Bearer Опросить статус генерации отчёта

Формат ошибок

Все ошибки возвращаются в единой обёртке:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "lesson_id must be a valid UUID",
    "request_id": "01HX..."
  }
}
HTTP-статус Код Значение
400 VALIDATION_ERROR Невалидное тело запроса или параметры пути
401 UNAUTHORIZED Отсутствует или недействителен Bearer-токен
403 FORBIDDEN Токен валидный, но недостаточно прав
404 NOT_FOUND Ресурс не найден
409 CONFLICT Дубликат записи
422 UNPROCESSABLE_ENTITY Нарушение бизнес-правила
500 INTERNAL_ERROR Неожиданная ошибка сервера
503 SERVICE_UNAVAILABLE Критичная зависимость недоступна

Ключевые файлы

university-journal-system/
├── cmd/
│   ├── edge/main.go              # Точка входа Edge-сервиса
│   └── domain/main.go            # Точка входа Domain-сервиса
├── api/
│   ├── openapi/journal.yaml      # OpenAPI 3.0 спека (единый источник истины)
│   ├── openapi/spec.go           # go:embed обёртка — спека встроена в бинарник
│   └── generated/journal.gen.go  # Вывод oapi-codegen (StrictServerInterface)
├── internal/
│   ├── edge/
│   │   ├── config/config.go      # Конфиг Edge (переменные окружения)
│   │   ├── handler/              # HTTP-хендлеры (посещаемость, оценки, health, docs)
│   │   ├── middleware/           # Auth (JWT), RequestID, Recovery
│   │   └── server/server.go      # Gin-роутер и его сборка
│   ├── domain/
│   │   ├── config/               # Конфиг Domain
│   │   ├── journal/
│   │   │   ├── domain/entities.go    # Доменные модели
│   │   │   ├── service/              # Бизнес-логика (AttendanceService, GradesService)
│   │   │   └── repository/           # Слой доступа к БД (использует sqlc Querier)
│   │   ├── shared/               # Аудит-лог, фоновые воркеры
│   │   └── server/server.go      # Gin-роутер Domain
│   ├── db/
│   │   ├── migrations/           # goose SQL-миграции
│   │   ├── queries/              # Сырые SQL-файлы (входные данные для sqlc)
│   │   └── generated/            # Вывод sqlc: Querier, models, *.sql.go
│   └── pkg/
│       ├── middleware/logging.go  # Общее структурное логирование + Prometheus-метрики
│       └── metrics/              # Определения Prometheus-метрик
├── config/
│   ├── nginx/nginx.conf          # Rate limiting, правила проксирования, security headers
│   ├── prometheus/prometheus.yml
│   ├── grafana/                  # Datasources + автопровизия дашбордов
│   ├── loki/
│   └── promtail/
├── docker-compose.yml            # Полный стек из 11 сервисов
├── Dockerfile.edge               # Многоэтапная сборка Edge
├── Dockerfile.domain             # Многоэтапная сборка Domain
├── Makefile                      # Задачи разработчика
├── oapi-codegen.yaml             # Конфиг oapi-codegen
├── sqlc.yaml                     # Конфиг sqlc
└── .env.example                  # Шаблон обязательных переменных окружения

Рабочий процесс разработчика

Частые задачи

# Показать все доступные make-таргеты
make help

# Собрать оба бинарника
make build

# Перегенерировать код после изменений journal.yaml или SQL-запросов
make generate          # запускает generate-sqlc + generate-api

# Миграции базы данных
make migrate-up        # применить все ожидающие
make migrate-down      # откатить последнюю миграцию
make migrate-status    # статус применённых/ожидающих
make migrate-create NAME=add_student_table  # создать новую миграцию

# Тесты
make test              # все тесты с флагом -race
make test-unit         # только юнит-тесты (без Docker)
make test-integration  # требует Docker

# Линтинг
make lint

# Горячая пересборка одного сервиса (не трогает остальные контейнеры)
docker compose up -d --build edge
docker compose restart nginx      # если edge пересобирался (DNS-кэш)

Добавление нового API-эндпоинта

  1. Добавить в api/openapi/journal.yaml — описать путь, параметры, схемы запроса/ответа
  2. Запустить make generate-api — регенерирует api/generated/journal.gen.go с новым методом
  3. Реализовать хендлер в internal/edge/handler/
  4. Зарегистрировать роут в internal/edge/server/server.go
  5. Добавить SQL-запрос в internal/db/queries/ при необходимости, затем make generate-sqlc

Схема кодогенерации

journal.yaml ──[oapi-codegen]──→ api/generated/journal.gen.go
                                     StrictServerInterface
                                     RegisterHandlers()
                                     Все модели запросов/ответов

internal/db/queries/*.sql ──[sqlc]──→ internal/db/generated/
                                           querier.go  (интерфейс Querier)
                                           models.go   (DB-структуры)
                                           *.sql.go    (типизированные функции)

Валидация OpenAPI-спеки

make docs-validate     # требует vacuum (https://quobix.com/vacuum)

# Или с помощью spectral:
npx @stoplight/spectral-cli lint api/openapi/journal.yaml

Observability

Метрики

Prometheus опрашивает три цели каждые 15 секунд:

Цель Endpoint Что измеряет
Edge :8080/metrics Частота HTTP-запросов, латентность, коды статусов по роутам
Domain :8081/metrics То же + пул соединений с БД, статистика фоновых воркеров
cAdvisor :8090/metrics CPU, RAM, сетевой I/O по каждому контейнеру

Просмотр дашбордов: http://localhost:3000 (Grafana, admin/admin)

На production Grafana слушает только 127.0.0.1:3000 — откройте через SSH-туннель, например ssh -L 3000:127.0.0.1:3000 user@server.

Services Overview: «No data» при зелёных плашках UP

Плашки edge/domain UP смотрят up{job=...} в Prometheus. Графики CPU / Memory / Network и Live Logs зависят от переменных дашборда и лейблов cAdvisor/Loki.

Причина Что сделать
Service не «All» / пустой multi-select Вверху дашборда выберите Service → All (иначе service=~"" → 0 точек)
Compose projectuniversity-journal-system На хосте могут быть другие стеки (project-monopoly, …); выберите university-journal-system
Нет compose_project / compose_service в cAdvisor (часто Docker Desktop) Графики CPU/RAM берут uj:service_* (process-метрики Edge/Domain). На Linux пересоздайте cadvisor prometheus grafana; cAdvisor нормализует labels из Docker или имени контейнера
Root filesystem «No data» на Host-дашборде Используется uj:node_primary_disk_avail_ratio (Linux /, Desktop /mnt/docker-desktop-disk); перезапустите Prometheus после git pull
Старый дашборд в БД Grafana git pull, docker compose restart grafana (provisioning с allowUiUpdates: false)
Другой compose project при деплое Скопируйте .env.example.env.production и не меняйте COMPOSE_PROJECT_NAME без правки Grafana/Promtail

Проверка на сервере:

bash scripts/check-observability.sh

Prometheus (должно быть > 0):

count(uj:service_cpu:rate5m{compose_project="university-journal-system"})

Предпровизированные дашборды:

  • HTTP Overview — частота запросов, частота ошибок, P99-латентность
  • DB Connections — загрузка пула, продолжительность запросов
  • Background Jobs — глубина очереди воркеров, успехи/ошибки задач
  • Container Resources — CPU/RAM на сервис (через cAdvisor)

Логи

Все сервисы пишут структурированный JSON в stdout. Promtail автоматически отправляет их в Loki.

Запросы логов в Grafana → Explore:

# Все ошибки Edge
{service="edge"} | json | level="error"

# Конкретный request_id сквозь все сервисы
{} | json | request_id="01HX..."

# Медленные запросы Domain (> 100ms)
{service="domain"} | json | duration > 100

Просмотр сырых логов через Docker:

docker compose logs -f edge
docker compose logs -f domain --tail=100

Health-эндпоинты

# Liveness — процесс запущен?
GET /health

# Readiness — критичные зависимости доступны?
GET /ready

Переменные окружения

Скопируйте .env.example в .env.local и заполните значения. Docker Compose читает .env.local.

Переменная Дефолт Описание
APP_ENV local Окружение: local, dev, prod
API_DOCS_ENABLED (пусто) true — включить /api/docs и /api/openapi.yaml; в prod нужно явно true, в local/dev docs включены по умолчанию
LOG_LEVEL debug Уровень логирования: debug, info, warn, error
EDGE_APP_PORT 8080 HTTP-порт Edge
DOMAIN_APP_PORT 8081 HTTP-порт Domain
DOMAIN_SERVICE_URL http://domain:8081 URL апстрима Edge → Domain
DB_DSN см. пример Строка подключения к PostgreSQL
DB_MAX_CONNS 20 Максимальный размер пула соединений с БД
DB_MIN_CONNS 5 Минимальный размер пула соединений с БД
KEYCLOAK_URL https://sso.example.edu Базовый URL внешнего Keycloak
KEYCLOAK_REALM Test Имя realm во внешнем Keycloak
KEYCLOAK_CLIENT_ID journal-backend Client ID Keycloak для валидации JWT
KEYCLOAK_ALLOWED_AZP пусто или journal-web,journal-mobile Опциональный allow-list azp для web/mobile access token
CORS_ALLOWED_ORIGINS http://localhost:4173,... Разрешённые CORS-источники через запятую

Безопасность: никогда не коммитьте .env.local и файлы с реальными секретами — они в .gitignore.


Заметки по ролям

Новые участники команды

  • Начать отсюда: прочитайте api/openapi/journal.yaml — это единственный источник истины для всех API-контрактов
  • Запустить make docker-up && make migrate-up, затем открыть http://localhost:3000 (Grafana) и http://localhost:8080/api/docs (Swagger)
  • Keycloak Realm не хранится в репозитории: используйте внешний тестовый/production Realm и значения KEYCLOAK_* из окружения
  • Придерживайтесь паттерна из internal/edge/handler/attendance.go при создании новых хендлеров
  • Весь SQL — в internal/db/queries/, никаких сырых запросов в Go-коде

Для клиентов API (фронтенд / мобайл)

  • Swagger UI: http://localhost/api/docs — просмотр всех эндпоинтов и тестирование прямо в браузере
  • Машиночитаемая спека: http://localhost/api/openapi.yaml — импорт в Postman или генерация клиентского кода
  • Все эндпоинты возвращают application/json
  • Аутентификация: получите Bearer-токен из Keycloak и передавайте его как Authorization: Bearer <token>
  • В ошибках всегда есть request_id — указывайте его при сообщениях об ошибках

DevOps / Деплой

  • CI: при каждом push в master workflow .github/workflows/main.yml по SSH заходит на сервер и выполняет scripts/deploy-server.sh: git pull, docker compose build --no-cache, up -d --wait, make migrate-up (нужен goose в PATH или Go), загрузка SEED_SQL через psql в контейнере Postgres. Секреты GitHub описаны в комментарии в начале workflow-файла (обязательные: SSH_*, WORK_DIR_DEV, BRANCH_DEV; опционально: DOCKER_COMPOSE_ARGS, DB_DSN_DEV, SEED_SQL_PATH).
  • Локально полный цикл как на сервере: DEPLOY_BRANCH=master make deploy-server (нужен Docker и совпадающий compose).
  • Пошаговый сценарий для docker-compose.prod-local.yml (миграции, пересборка, кэш, orphans): раздел Развёртывание prod-local
  • Два Docker-образа: Dockerfile.edge и Dockerfile.domain (многоэтапные; в builder — go mod tidy, затем oapi-codegen или sqlc с GitHub Releases и go build)
  • Если сборка domain падает с panic: unable to mmap memory / cannot allocate memory на шаге sqlc generate: контейнеру не хватает RAM (часто при жёстком лимите Docker, без swap или при docker build --platform=... через QEMU). Увеличьте память или swap для Docker; по возможности собирайте образ на той же архитектуре, что и рантайм. В Dockerfile.domain используется готовый бинарник sqlc с GitHub (не go run) и GOMAXPROCS=1 при генерации, чтобы снизить пиковое потребление.
  • Health-пробы: /health (liveness) и /ready (readiness) на обоих сервисах
  • Swagger в prod: оставьте APP_ENV=prod и не задавайте API_DOCS_ENABLED (или API_DOCS_ENABLED=false)
  • Конфиг Prometheus: config/prometheus/prometheus.yml
  • Datasources и дашборды Grafana автопровизируются из config/grafana/ (allowUiUpdates: false; после деплоя — restart grafana)
  • Диагностика на хосте: bash scripts/check-observability.sh
  • Пересборка одного сервиса без даунтайма: docker compose up -d --build edge && docker compose restart nginx

Код-ревьюеры

  • Каждый роут должен иметь соответствующую запись в OpenAPI-спеке
  • Весь доступ к БД — только через Querier-интерфейс, сгенерированный sqlc
  • Хендлеры не должны содержать бизнес-логику — она принадлежит internal/domain/journal/service/
  • Ошибки — только через handler.NewErrorResponse() для единообразной обёртки
  • Новые миграции — только forward-only (никаких деструктивных изменений в up)

About

REST API бэкенд электронного журнала университета — учёт посещаемости, управление оценками, формы оценивания и асинхронная генерация отчётов.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages