Небольшой видео-хостинг: загрузка файла, выдача публичной ссылки, просмотр в веб-плеере.
- Пользователь загружает видеофайл через интерфейс (или API).
- Сервис сохраняет файл, генерирует уникальный идентификатор и URL.
- По ссылке открывается страница с HTML5-плеером (и при необходимости метаданными: название, длительность).
Целевая аудитория на старте: внутренние сценарии или личные проекты с умеренным трафиком. Масштабирование под миллионы просмотров — отдельная итерация.
| Слой | Технология |
|---|---|
| Репозиторий | npm workspaces, каталог packages/ |
| Backend | Два отдельных HTTP-сервиса на JavaScript: api-user (публичное) и api-admin (только админ); в каждом — Express + express-openapi (своя OpenAPI-спека) |
| Метаданные | MongoDB, Mongoose |
| Файлы видео | Локальный диск (uploads/), имя файла по внутреннему id (см. ниже); отдача исходного MP4 |
| Публичный id в URL | nanoid, 20 символов |
| Пользовательский UI | Vue 3, Composition API, TypeScript |
| Админка | Vue 3, TypeScript; один админ из .env; доступ к admin API — HTTP Basic |
| HTTP | api-user: CORS *; api-admin: CORS под origin web-admin (в dev — URL Vite), не путать с политикой user API |
| Клиент к API с фронтов | Свой тонкий клиент (fetch + обёртки), без orval/openapi-typescript |
| Сборка | Docker-образ(а) для сборки/упаковки артефактов (детали — при появлении деплоя) |
| # | Тема | Решение |
|---|---|---|
| 1 | Публичность | Все ссылки публичные; любой, у кого есть URL, может смотреть видео. |
| 2 | Пользователи | Анонимная загрузка, регистрация не требуется; владелец в метаданных не хранится (или опционально только технический маркер без UI). |
| 3 | Формат и размер | Только MP4 с H.264 (AVC) для воспроизведения в браузере; HEVC (H.265) отклоняется при загрузке; максимум 1 ГБ. |
| 4 | Хранение файлов | Локальный диск на машине с API. |
| 5 | Обработка | Отдача исходника как есть, без ffmpeg / HLS на первом этапе. |
| 6 | Идентификатор в ссылке | nanoid, 20 символов. |
| 7 | Админка | Один администратор; учётные данные в .env; к защищённым маршрутам API — HTTP Basic (логин/пароль как пара для Basic). |
| 8 | Инфраструктура / деплой | Хостинг и оркестрация не проектируем сейчас; сборка — в Docker-контейнере. |
| 9 | Модерация / юридическое | Не требуется. |
| 10 | Язык UI | Русский; i18n не закладываем на старте. |
| 11 | Название видео | Поле «название» при загрузке нужно (отображение в UI, <title>, колонка в админке). |
| 12 | Разделение backend | Два независимых HTTP-приложения: пользовательское API и админское API (разные пакеты, порты и OpenAPI-спеки). |
| # | Тема | Решение |
|---|---|---|
| A | Workspaces | Только packages/. Пакеты: db (Mongoose), api-user, api-admin, web, web-admin. |
| B | TypeScript | Только фронты (web, web-admin); api-user и api-admin — JavaScript. |
| C | ODM | Mongoose в пакете db. |
| D | Админ | Весь сервис api-admin защищён HTTP Basic (глобальный middleware); api-user без Basic, только публичные маршруты. |
| E | Сборка | Docker: один образ antirek/mvidia; в compose два контейнера с разным command (api-user / api-admin). |
| F | Имя файла на диске | Внутренний id (например UUID или ObjectId), не {publicId}.mp4; связь publicId ↔ путь к файлу только в Mongo. |
| G | Локальная разработка | Три процесса на разных портах: api-user, api-admin, Vite-фронты. У web: VITE_API_USER_BASE_URL (или VITE_API_BASE_URL — единое имя для user API); у web-admin: VITE_API_ADMIN_BASE_URL. |
| H | CORS | api-user: Access-Control-Allow-Origin: *. api-admin: не дублировать * без нужды; в dev разрешить origin web-admin (URL Vite), иначе браузер заблокирует запросы с Basic; при желании на время разработки можно ослабить до * локально. |
| I | Злоупотребления | Без rate limit и CAPTCHA на старте; опора на лимит 1 ГБ и последующий харднинг при необходимости. |
| J | Клиент API | Свой ручной/минимальный клиент под OpenAPI-контракт (без генераторов). |
| K | Пагинация в админке | offset + limit (простой список, пагинация «страницами»). |
- Метаданные в MongoDB через Mongoose: как минимум
publicId, внутренний ключ файла на диске,title, mime, размер, даты. - Две OpenAPI-спеки: для
api-userи дляapi-admin(валидация и маршруты разделены по процессам). - Фронты держат свой клиент и типы вручную под соответствующую спеку.
┌─────────────────┐
│ User UI │ packages/web → только api-user
│ (public) │
└────────┬────────┘
│
┌────────▼────────┐
│ Admin UI │ packages/web-admin → только api-admin (HTTP Basic)
│ (protected) │
└────────┬────────┘
│
┌───────────────────┴───────────────────┐
│ │
┌────────▼─────────────┐ ┌──────────▼──────────────┐
│ packages/api-user │ │ packages/api-admin │
│ Express + openapi │ │ Express + openapi │
│ CORS * │ │ весь сервис за HTTP Basic│
│ upload, meta, stream │ │ list offset/limit, delete│
└────────┬─────────────┘ └──────────┬──────────────┘
│ │
└───────────────────┬───────────────────┘
│
┌────────▼────────┐ ┌──────▼──────┐
│ Локальный диск │ │ MongoDB │ общая БД; модели из packages/db (рекомендуется)
│ internalId.mp4 │ │ │
└─────────────────┘ └─────────────┘
- Публичный сайт: загрузка MP4, метаданные, отдача файла с Range по
publicId. - Скринкаст:
POST /recordings, chunks по дорожкам,finalize,GET /recordings/{sessionId}; после merge — запись вVideo(как при обычной загрузке). - CORS
*(как зафиксировано). - Отдельный процесс и порт; своя OpenAPI-спека и express-openapi.
- Только операции администратора: список видео offset/limit, удаление (файл на диске + документ в Mongo).
- HTTP Basic на всех маршрутах приложения.
- Отдельный процесс и порт; своя OpenAPI-спека и express-openapi.
- Пишет в тот же каталог
uploads/и ту же MongoDB, что и api-user (общийMONGODB_URI, общийUPLOAD_DIRв.env). - CORS: запросы идут из
web-adminс другого порта — настроитьAccess-Control-Allow-Origin(как минимум URL dev-сервера админки); глобальный*зафиксирован только для api-user.
- Форма загрузки: файл + название, прогресс, итоговая публичная ссылка.
- Скринкаст (
/screencast): потоковая запись дорожек (экран, вебкамера, микрофон, звук вкладки) в WebM на сервер (POST /recordings/...), сборка MP4 H.264 через ffmpeg. Chrome / Edge, HTTPS / localhost / 127.0.0.1. На сервере нужен ffmpeg (apt install ffmpegили Docker-образ проекта). План —docs/screencast-streaming-recording-plan.md. - Плеер:
<video controls>, заголовок страницы из названия.
- Базовый URL только
api-admin; запросы с HTTP Basic (форма логина → заголовокAuthorization— на усмотрение реализации). - Таблица с пагинацией offset/limit: название, publicId, дата, размер, ссылка, удаление.
- Файл:
{internalId}.mp4(или согласованный шаблон);publicIdтолько в URL и в документе Mongo. - Обычная загрузка: транскодирование не используется. Скринкаст: merge на сервере (ffmpeg).
- Корень:
workspaces: [packages/*]; пакетыpackages/db,packages/api-user,packages/api-admin,packages/web,packages/web-admin. Скрипты:npm run dev -w api-userи т.д. packages/db: схема «видео» (publicId,storageFileName,title,mimeType,sizeBytes,createdAt).packages/api-user: Express (JS) + express-openapi, CORS*, лимиты тела;POST /videos(multipart, title, MP4, ≤1 ГБ);GETметаданных и Range-отдача поpublicId.packages/api-admin: отдельное приложение Express + express-openapi; глобально HTTP Basic; список с offset/limit, удаление (файл + документ).- Согласовать порты по умолчанию (например user API
3001, admin API3002) и задокументировать в.env.example.
- Переменная окружения базового URL
api-user(напримерVITE_API_USER_BASE_URLили единое имя по вкусу команды). - Загрузка с названием + ссылка
/v/<publicId>. - Страница плеера, русский UI.
- Базовый URL
api-admin+ HTTP Basic на каждом запросе. - Список с пагинацией, удаление.
- Таймауты на upload, логирование.
- Dockerfile (или несколько) для сборки
api-user,api-adminи статики фронтов; полноценный деплой — по мере необходимости.
Контракты: две OpenAPI-спеки рядом с express-openapi в соответствующих пакетах.
| Пакет | Назначение |
|---|---|
packages/db |
Mongoose-модель Video, connect(uri) |
packages/api-user |
Публичный API + раздача packages/web/dist (один процесс, SERVE_UI=1) |
packages/api-admin |
Админ API + раздача packages/web-admin/dist (один процесс) |
packages/web |
Исходники Vue; сборка npm run build -w web |
packages/web-admin |
Исходники админки; сборка npm run build -w web-admin |
- MongoDB на
localhost:27017(илиdocker compose up -d mongo). cp .env.example .envи задайтеADMIN_PASSWORD.- Для локального prod-режима без Docker — один раз соберите UI:
npm run build:ui(в образах Docker UI уже внутри). - Два терминала:
- Клиент:
npm run dev:user(разработка) илиnpm run start:user(только запуск, нуженpackages/web/dist) - Админ:
npm run dev:adminилиnpm run start:admin(нуженpackages/web-admin/dist)
http://localhost:3001/ и http://localhost:3002/
- Клиент:
Альтернатива с hot-reload фронта (четыре процесса): npm run dev:web + SERVE_UI=0 npm run dev -w api-user и т.д.
Dockerfile: один образ; UI вpackages/web/distиpackages/web-admin/dist(каждый API отдаёт свой каталог).docker-compose.yml: сервисыuserиadmin, один образMVIDIA_IMAGE, разныйcommand.docker-build.sh:./docker-build.sh→ образantirek/mvidia:TAG(переменныеIMAGE,TAG).
- Зафиксировать версию Node (например актуальный LTS) в
.nvmrcилиenginesв корневомpackage.json. - ESLint + Prettier: один набор с корня для всех пакетов или раздельно — по удобству команды.