No description
  • Go 52.2%
  • TypeScript 28.3%
  • HTML 15%
  • SCSS 4.3%
  • Dockerfile 0.1%
Find a file
2026-07-15 10:40:42 +03:00
backend Удалить backend/main 2026-07-15 10:40:42 +03:00
docs feat: Добавление режима PWA 2026-07-08 22:20:46 +03:00
frontend feat: Добавление режима PWA 2026-07-08 22:20:46 +03:00
.gitignore fis: Правка отправки done 2026-07-08 14:25:38 +03:00
.woodpecker.yml fix: Добавление отладочной информации для chunker-upload 2026-07-02 10:17:34 +03:00
angie.conf.example fix: Увеличение таймаута на клиенте до 10 минут 2026-06-24 10:51:18 +03:00
Dockerfile fix: CI 2026-07-01 09:47:21 +03:00
Dockerfile.ci fix: Добавление отладочной информации для chunker-upload 2026-07-02 10:17:34 +03:00
LICENSE CI/CD 2026-06-22 21:08:13 +03:00
README.md feat: Транслитерация кирилических имен файлов 2026-07-06 09:55:00 +03:00
test.js fix: openUploadDialog 2026-07-01 13:57:27 +03:00

Система управления программой конференции

Мгновенная синхронизация, загрузка презентаций, управление докладчиками и порядком выступлений — всё в одном веб-приложении.


Особенности

  • Ролевая модель — глобальные роли (global_admin, security_admin, viewer) + локальные роли на площадку (venue_admin, operator, secretary, moderator). Полная матрица прав ниже.

  • JWT + Argon2id — безопасная аутентификация. Пароли не хранятся в открытом виде. Refresh token rotation, отзыв сессий.

  • Секции/залы — иерархическое дерево секций с поддержкой перетаскивания (CDK drag-and-drop), вложенности, сворачивания.

  • Статус speaking — трёхстатусный цикл (pending → speaking → done). Фиксация времени начала и окончания выступления. Автоматический расчёт таймлайна.

  • Файлы презентаций — загрузка через веб-интерфейс, автоматическое сохранение на сервере. Поддержка чанковой загрузки через chunker-upload с fallback на прямую загрузку. Автоматическая конвертация в PDF и ZIP-архив через conference-convert (опционально). Чипсы форматов прямо в таблице.

  • Массовый импорт — JSON или XLSX/ODS/CSV, авто-маппинг колонок, проверка дублей, авто-создание залов и секций.

  • Протокол conference-display — WebSocket-команды для внешнего сервиса отображения презентаций на экранах в зале. Предзагрузка следующего доклада (queue_next) для нулевого лага.

  • Drag-and-drop — интуитивное перетаскивание докладов и секций (CDK). Ограничено по правам: venue_admin — всё, остальные — только reorder внутри своей секции.

  • Тёмная тема — переключение между светлой, тёмной и системной. Выбор сохраняется в localStorage.

  • Pull-to-refresh — на мобильных устройствах (<768px) жест смахивания вниз запрашивает свежие данные.

  • Мгновенные обновления для организаторов через WebSocket; зрители получают обновления с накоплением (30-секундный debounce).

  • OS-уведомления — через Notification API + Service Worker.

  • Service Worker на TypeScript — кэширование статики, нотификации.

  • Легкое развёртывание — одиночный бинарник Go + SQLite, готовый Docker-образ. Фронтенд вшит в бинарник через go:embed.

  • Гибкий эндпоинт — флаг --prefix позволяет повесить приложение на подпуть.

  • Аудит действий — логирование всех изменений участников, секций, залов, ролей с привязкой к пользователю.


Быстрый запуск в Docker

docker run -p 5000:3000 \
  -e CONFERENCE_NAME="Моя конференция" \
  -v ${PWD}/data/:/data \
  -v ${PWD}/uploads:/uploads \
  git.ymnuktech.ru/ymnuk/reception-conference:latest

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

Параметры Docker-контейнера

Переменная окружения Описание По умолчанию
CONFERENCE_NAME Название конференции Конференция
DB_PATH Путь к файлу SQLite conference.db
PORT Порт HTTP 3000
PREFIX Префикс эндпоинта (пусто)
BASE_PATH Адрес для прослушивания (:port или host:port) :3000
CHUNKER_UPLOAD_URL URL сервиса chunker-upload (пусто — прямой upload)
CHUNKER_APP_ID App ID для chunker-upload conference
CHUNKER_TOKEN Токен доступа к chunker-upload (пусто)
CHUNKER_WEBHOOK_TOKEN Токен для проверки webhook от chunker (пусто — без проверки)
CHUNKER_BASE_PATH Базовый путь в chunker-upload conference
CONVERTER_URL URL сервиса conference-convert (пусто — отключён)
CONVERTER_TOKEN Токен для push-уведомлений в конвертер (пусто)
DISPLAY_BASE_URL Базовый URL сервиса conference-display (пусто — отключён)
LOG_LEVEL Уровень логирования: debug, info, off info

Важно: пробросьте тома:

  • -v /host/path/data:/data — база данных.
  • -v /host/path/uploads:/uploads — загруженные презентации.

Самостоятельная сборка и запуск

Требования

  • Go 1.26.4+
  • Node.js 24+ и npm

Шаги

  1. Клонировать репозиторий

    git clone <repository-url>
    cd reception-conference
    
  2. Собрать фронтенд

    cd frontend
    npm install
    npx ng build --prod --output-path=../backend/cmd/server/static
    cd ..
    
  3. Собрать бэкенд

    cd backend
    go mod download
    go build -o conference-server ./cmd/server
    
  4. Запустить сервер

    ./conference-server --db=./data.db --port=8080
    
  5. Открыть в браузереhttp://localhost:8080. Логин: admin, пароль: admin.

Для разработки фронтенд запускается отдельно (ng serve), бэкенд с флагом --dev для CORS. Прокси настроен в frontend/proxy.conf.json.

Параметры командной строки

Аргумент Переменная окружения По умолчанию Описание
--db DB_PATH conference.db Путь к SQLite
--port PORT 3000 Порт HTTP
--base-path BASE_PATH :3000 Адрес прослушивания
--prefix PREFIX (пусто) Префикс эндпоинта
--name CONFERENCE_NAME Конференция Название конференции
--dev false Режим разработки (CORS)
--chunker-url CHUNKER_UPLOAD_URL (пусто) URL chunker-upload
--chunker-app-id CHUNKER_APP_ID conference App ID для chunker
--chunker-token CHUNKER_TOKEN (пусто) Токен доступа к chunker
--chunker-webhook-token CHUNKER_WEBHOOK_TOKEN (пусто) Токен проверки webhook
--chunker-base-path CHUNKER_BASE_PATH conference Базовый путь в chunker
--converter-url CONVERTER_URL (пусто) URL conference-convert
--converter-token CONVERTER_TOKEN (пусто) Токен для push в конвертер
--display-base-url DISPLAY_BASE_URL (пусто) URL conference-display
--log-level LOG_LEVEL info Уровень логирования: debug, info, off

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

.
├── .woodpecker.yml              # CI/CD (Woodpecker)
├── frontend/                    # Angular 22
│   ├── src/
│   │   ├── app/
│   │   │   ├── app.ts/html      # Корневой компонент
│   │   │   ├── services/        # WebSocket, File, Theme
│   │   │   ├── components/      # ProgramTable, SectionGroup
│   │   │   ├── dialogs/         # Login, Add, Edit, Import, Upload, Admin, etc.
│   │   │   └── directives/      # PullToRefresh
│   │   ├── service-worker.ts    # Service Worker (TS)
│   │   └── index.html
│   ├── angular.json
│   └── package.json
├── backend/                     # Go 1.26
│   ├── cmd/server/main.go       # Точка входа (Echo)
│   ├── internal/
│   │   ├── api/                 # External REST API (/api/v1)
│   │   ├── auth/                # JWT + Argon2id
│   │   ├── config/              # Парсинг флагов
│   │   ├── db/                  # SQLite + AutoMigrate
│   │   ├── handlers/            # HTTP upload/download
│   │   ├── models/              # GORM-модели
│   │   ├── ws/                  # WebSocket хаб (12 файлов)
│   │   ├── webhook/             # Приём webhook от chunker
│   │   └── integration/         # Интеграционные тесты
│   ├── go.mod
│   └── go.sum
├── docs/
│   ├── ROADMAP.md               # Трекер задач
│   ├── ARCHITECTURE.md          # Архитектура
│   ├── DISPLAY_PROTOCOL.md      # Протокол conference-display
│   └── IMPORT.md                # Формат импорта
├── Dockerfile
└── README.md

Как это работает

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

  • При открытии страницы устанавливается WebSocket-соединение (гость = viewer).
  • Пользователь вводит логин/пароль → сервер возвращает JWT (15 мин) + refresh token (24 ч).
  • JWT отправляется через WS auth, сервер поднимает роль.
  • Refresh token rotation: каждый refresh выдаёт новую пару, старый refresh отзывается.
  • Пароли хэшируются Argon2id. При первом запуске seed: admin/admin (global_admin + security_admin).

Управление

  • Площадки (venues) — создание/редактирование/удаление (только global_admin). Каждая площадка имеет дерево секций.
  • Секции — иерархические, с временем начала, длительностью, перерывами. Drag-and-drop между собой.
  • Участники — имя, тема, статус (pending/speaking/done), секция, файл, длительность.
  • Статус speaking — при переключении фиксируется StartReadingAt; при завершении — EndReadingAt. Это позволяет построить точный таймлайн.
  • Файлы — загрузка, конвертация (PDF/ZIP), скачивание. Форматы показываются иконками в таблице.
  • Импорт — массовое добавление участников через JSON или XLSX/ODS/CSV.
  • Сервис-токены — для внешних сервисов (conference-display). Привязаны к одному залу.

Интеграции

Сервис Назначение Протокол
chunker-upload Чанковая загрузка больших файлов REST API + webhook
conference-convert Конвертация в PDF/ZIP Push-уведомление + webhook
conference-display Отображение слайдов на экранах зала WebSocket (ServiceToken)

Все интеграции опциональны. Без них приложение работает штатно.


Ролевая модель и матрица прав

Глобальные роли

Роль Описание
global_admin Полный доступ ко всем площадкам, управление залами, токенами. Создаётся при seed.
security_admin Управление пользователями (создание, блокировка, сброс пароля, назначение глобальных ролей).
viewer Начальная роль гостя. Только просмотр активных участников.

Локальные роли (назначаются на площадку)

Каждый пользователь может иметь разные роли на разных площадках.

Роль Полномочия
venue_admin Полный CRUD участников и секций, перемещение между секциями, drag-and-drop секций.
operator Смена статуса, перестановка порядка, загрузка/замена файлов.
secretary Смена статуса, перестановка порядка, редактирование имени/темы.
moderator Смена статуса, перестановка порядка.

Матрица действий

Действие global_admin venue_admin operator secretary moderator viewer
Управление площадками
CRUD секций (свои)
Drag-and-drop секций (свои)
Добавление/удаление участника (свои)
Редактирование имени/темы (свои)
Смена статуса (свои)
Перестановка внутри секции (свои)
Перемещение между секциями (свои)
Загрузка/замена файла (свои)
Скачивание файла
Сервис-токены
Управление пользователями
Просмотр участников (свои) (свои) (свои) (свои) (активные)

Статусы участников

Статус Описание
pending Начальный статус. Файл не загружен.
ready Файл загружен, готов к выступлению.
speaking Выступает сейчас. Фиксируется StartReadingAt.
done Выступление завершено. Фиксируется EndReadingAt.

Цикл: pending → ready → speaking → done. Из done можно вернуть в ready или pending.

Исключённые участники

Скрыты от зрителей, не участвуют в сортировке, не редактируются и не удаляются. Возвращаются кнопкой «Восстановить» (venue_admin/global_admin).

Массовый импорт

Доступен из двух табов:

JSON:

[
  {"name": "Иван Петров", "topic": "Квантовые вычисления"},
  {"name": "Мария Сидорова", "topic": "Нейросети", "duration": 20, "venueName": "Зал A"}
]

Файл (XLSX/ODS/CSV с авто-маппингом колонок):

Столбец Обязательный Описание
Докладчик да Имя
Тема да Название доклада
Зал нет Название зала (пусто → «Остальное»)
Длительность нет Минуты (пусто → 15)

Дубли (name + topic) пропускаются. При импорте авто-создаются залы и секция «Импортировано …».


Вебхуки

Система принимает два вебхука от внешних сервисов:

POST /webhook/file — от chunker-upload

Приходит после завершения чанковой загрузки файла.

{
  "upload_id": "uuid",
  "app_id": "conference",
  "base_path": "conference",
  "file_name": "presentation.pptx",
  "file_size": 4194304,
  "status": "completed"
}

Что делает бэкенд:

  1. Проверяет Authorization: Bearer <chunker_webhook_token> (опционально, если токен задан).
  2. Находит FileUpload по upload_id в БД.
  3. Скачивает готовый файл из chunker-upload через GET /{base_path}/file/{upload_id}.
  4. Сохраняет локально в /uploads/{participantID}_{filename}.
  5. Обновляет Participant.FilePath и статус → ready.
  6. Если настроен converter — отправляет файл на конвертацию в PDF/ZIP.

Push POST /api/convert → conference-convert

При загрузке файла (через прямой upload или chunker) reception отправляет файл на конвертацию:

POST {converter_url}/api/convert
Content-Type: multipart/form-data
Authorization: Bearer {CONVERTER_TOKEN}

Поля формы:

  • file — загруженный файл (оригинальное имя)
  • talk_id — UUID участника
  • callback_url — URL для ответа: {PUBLIC_URL}/api/v1/webhook/converted[?secret={CONVERTER_TOKEN}]

Retry: 3 попытки с интервалом 1, 3, 5 сек. При timeout или не 201/202 — повтор.

POST /api/v1/webhook/converted — callback от conference-convert

Принимает результат конвертации. Аутентификация: если задан CONVERTER_TOKEN, проверяется ?secret=<токен> query-параметр.

Успех (event: "conversion.completed")

{
  "event": "conversion.completed",
  "task_id": "conv_abc123",
  "talk_id": "019f1c99-...",
  "status": "success",
  "result": {
    "origin_url": "https://...",
    "pdf_url": "/api/result/conv_abc123/pdf",
    "zip_url": "/api/result/conv_abc123/zip",
    "format": "mixed",
    "total_slides": 15,
    "size_bytes": 2048576,
    "converted_at": "2026-06-30T10:00:00Z"
  }
}

Что делает бэкенд:

  1. Retry guard: если task_id уже существует (sentinel или готовые файлы) — 200 OK, пропуск.
  2. Создаёт sentinel-запись ConvertedFile{Format: "_processing_", TaskID: task_id} — блокирует retry.
  3. Возвращает 200 OK немедленно.
  4. В горутине: скачивает pdf_url и zip_url, сохраняет как {participantID}_converted_pdf.pdf / {participantID}_converted_zip.zip, удаляет sentinel, создаёт полноценные записи ConvertedFile.
  5. Вызывает OnChange → WS broadcast update с актуальным AvailableFormats.

Ошибка (event: "conversion.failed")

{
  "event": "conversion.failed",
  "task_id": "conv_abc123",
  "talk_id": "019f1c99-...",
  "status": "error",
  "error": {
    "code": "LIBREOFFICE_TIMEOUT",
    "message": "Conversion timeout",
    "details": ""
  }
}

Логируется и возвращается 200 OK.

Оба вебхука опциональны — без них приложение работает штатно, просто без конвертации форматов.

Тестирование

Backend (Go) — 115 тестов

cd backend
go test ./... -v

Пакеты:

  • ws — 40 тестов (роли, секции CRUD, перемещение, таймлайн, аудит, venue, display, permissions)
  • api — 15 тестов (ServiceToken, participants, PATCH статус, file GET, converter webhook)
  • auth — 15 тестов (логин, refresh, JWT, блокировка, argon2id)
  • handlers — 17 тестов (upload, download, uploadInit, валидация, замена файла, лимит)
  • models — 14 тестов (CRUD всех моделей)
  • webhook — 9 тестов (приём webhook, проверка токена, скачивание)
  • integration — 3 теста (полный цикл через chunker-upload + unauthorized + healthcheck)
  • config — 2 теста (парсинг флагов)

Frontend (Angular / Vitest) — 77 тестов, 9 файлов

cd frontend
npm test

Разработка

# Фронтенд (порт 4200)
cd frontend && ng serve

# Бэкенд (порт 8080, с CORS для dev)
cd backend && go run ./cmd/server --port=8080 --dev

Прокси настроен в frontend/proxy.conf.json.


CI/CD

.woodpecker.yml — Woodpecker CI. При создании тега:

  1. Сборка фронтенда.
  2. Сборка Go-бинарника (со встроенной статикой).
  3. Публикация релиза с бинарниками для Linux и Windows.
  4. Сборка Docker-образа.

Известные ограничения

  • SQLite не рассчитана на высокую конкурентную запись (>10 одновременных редакторов).
  • Нет поддержки нескольких конференций одновременно.
  • Загрузка файлов ограничена 100MB.
  • Notification API требует HTTPS (кроме localhost), на iOS Safari не поддерживается.
  • Конвертация файлов требует внешнего сервиса conference-convert.

Лицензия

MIT. Подробнее в файле LICENSE.