Skip to content

chottodev/mvidia

Repository files navigation

mvidia

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

Описание продукта

  1. Пользователь загружает видеофайл через интерфейс (или API).
  2. Сервис сохраняет файл, генерирует уникальный идентификатор и URL.
  3. По ссылке открывается страница с 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 APIHTTP 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-adminJavaScript.
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   │   │              │
                    └─────────────────┘   └─────────────┘

API user (packages/api-user)

  • Публичный сайт: загрузка MP4, метаданные, отдача файла с Range по publicId.
  • Скринкаст: POST /recordings, chunks по дорожкам, finalize, GET /recordings/{sessionId}; после merge — запись в Video (как при обычной загрузке).
  • CORS * (как зафиксировано).
  • Отдельный процесс и порт; своя OpenAPI-спека и express-openapi.

API admin (packages/api-admin)

  • Только операции администратора: список видео 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.

User UI

  • Форма загрузки: файл + название, прогресс, итоговая публичная ссылка.
  • Скринкаст (/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>, заголовок страницы из названия.

Admin UI

  • Базовый URL только api-admin; запросы с HTTP Basic (форма логина → заголовок Authorization — на усмотрение реализации).
  • Таблица с пагинацией offset/limit: название, publicId, дата, размер, ссылка, удаление.

Хранение и обработка видео

  • Файл: {internalId}.mp4 (или согласованный шаблон); publicId только в URL и в документе Mongo.
  • Обычная загрузка: транскодирование не используется. Скринкаст: merge на сервере (ffmpeg).

План работ (примерный)

Фаза 1 — каркас и API

  1. Корень: workspaces: [packages/*]; пакеты packages/db, packages/api-user, packages/api-admin, packages/web, packages/web-admin. Скрипты: npm run dev -w api-user и т.д.
  2. packages/db: схема «видео» (publicId, storageFileName, title, mimeType, sizeBytes, createdAt).
  3. packages/api-user: Express (JS) + express-openapi, CORS *, лимиты тела; POST /videos (multipart, title, MP4, ≤1 ГБ); GET метаданных и Range-отдача по publicId.
  4. packages/api-admin: отдельное приложение Express + express-openapi; глобально HTTP Basic; список с offset/limit, удаление (файл + документ).
  5. Согласовать порты по умолчанию (например user API 3001, admin API 3002) и задокументировать в .env.example.

Фаза 2 — User UI (packages/web, Vue 3 + TS)

  1. Переменная окружения базового URL api-user (например VITE_API_USER_BASE_URL или единое имя по вкусу команды).
  2. Загрузка с названием + ссылка /v/<publicId>.
  3. Страница плеера, русский UI.

Фаза 3 — Admin UI (packages/web-admin, Vue 3 + TS)

  1. Базовый URL api-admin + HTTP Basic на каждом запросе.
  2. Список с пагинацией, удаление.

Фаза 4 — качество и упаковка

  1. Таймауты на upload, логирование.
  2. 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

Локальный запуск (два процесса)

  1. MongoDB на localhost:27017 (или docker compose up -d mongo).
  2. cp .env.example .env и задайте ADMIN_PASSWORD.
  3. Для локального prod-режима без Docker — один раз соберите UI: npm run build:ui (в образах Docker UI уже внутри).
  4. Два терминала:
    • Клиент: 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 и т.д.

Docker

  • 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: один набор с корня для всех пакетов или раздельно — по удобству команды.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors