Files
SuperVPN/Docs/REWRITE_INSTRUCTION.md
2025-10-12 11:06:25 +09:00

16 KiB
Raw Blame History

🛠️ ИНСТРУКЦИЯ ПО ПЕРЕПИСЫВАНИЮ СИСТЕМЫ VIDEOREADER

Версия: 1.0
Дата: 9 октября 2025 г.
Цель: Создание собственной безопасной системы видеонаблюдения


📋 ПЛАН МИГРАЦИИ

Этап 1: Анализ и планирование (1-2 недели)

Этап 2: Инфраструктура (2-3 недели)

Етап 3: Backend разработка (3-4 недели)

Этап 4: Клиентские приложения (4-6 недель)

Этап 5: Тестирование и деплой (2 недели)


🏗️ СОВРЕМЕННАЯ АРХИТЕКТУРА

Рекомендуемый стек технологий:

🌐 Backend (Сигналинг сервер)

├── Node.js + TypeScript
├── Socket.IO / WebSocket
├── Redis (для сессий)
├── PostgreSQL (метаданные)
├── Docker + Docker Compose
└── Nginx (Reverse Proxy)

📱 Android приложение

├── Kotlin / Java
├── WebRTC Android API
├── Camera2 API
├── Retrofit (HTTP клиент)
├── Room (локальная БД)
└── Dagger/Hilt (DI)

💻 Desktop приложение

Вариант 1: Electron + React/Vue
├── TypeScript
├── WebRTC Web API
├── FFmpeg.js
└── Material-UI / Ant Design

Вариант 2: .NET MAUI
├── C# .NET 8
├── WebRTC.NET
├── FFMpegCore
└── Avalonia UI

Вариант 3: Flutter Desktop
├── Dart
├── WebRTC Flutter plugin
├── FFmpeg Flutter
└── Material Design

🚀 ЭТАП 1: НАСТРОЙКА ИНФРАСТРУКТУРЫ

1.1 Создание сигналинг сервера

# Создание проекта
mkdir videoreader-signaling
cd videoreader-signaling
npm init -y

# Установка зависимостей
npm install express socket.io redis ioredis
npm install @types/node typescript ts-node nodemon --save-dev

1.2 Базовая структура сервера

// src/server.ts
import express from 'express';
import { createServer } from 'http';
import { Server } from 'socket.io';
import Redis from 'ioredis';

const app = express();
const server = createServer(app);
const io = new Server(server, {
  cors: { origin: "*" }
});

const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');

interface Device {
  id: string;
  type: 'sender' | 'receiver';
  channel: string;
  socketId: string;
}

// Управление каналами и устройствами
class ChannelManager {
  async addDevice(device: Device) {
    await redis.hset(`channel:${device.channel}`, device.type, device.socketId);
    await redis.expire(`channel:${device.channel}`, 3600); // TTL 1 час
  }

  async getPartner(channel: string, deviceType: string) {
    const partnerType = deviceType === 'sender' ? 'receiver' : 'sender';
    return await redis.hget(`channel:${channel}`, partnerType);
  }
}

const channelManager = new ChannelManager();

io.on('connection', (socket) => {
  socket.on('join-channel', async (data: {channel: string, type: 'sender'|'receiver'}) => {
    await channelManager.addDevice({
      id: socket.id,
      type: data.type,
      channel: data.channel,
      socketId: socket.id
    });

    // Поиск партнера
    const partner = await channelManager.getPartner(data.channel, data.type);
    if (partner) {
      socket.emit('partner-found', { partnerId: partner });
      io.to(partner).emit('partner-found', { partnerId: socket.id });
    }
  });

  // WebRTC signaling
  socket.on('webrtc-offer', (data) => {
    io.to(data.to).emit('webrtc-offer', { from: socket.id, offer: data.offer });
  });

  socket.on('webrtc-answer', (data) => {
    io.to(data.to).emit('webrtc-answer', { from: socket.id, answer: data.answer });
  });

  socket.on('webrtc-ice-candidate', (data) => {
    io.to(data.to).emit('webrtc-ice-candidate', { from: socket.id, candidate: data.candidate });
  });
});

server.listen(3000, () => console.log('Signaling server running on port 3000'));

1.3 Docker конфигурация

# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
# docker-compose.yml
version: '3.8'
services:
  signaling:
    build: .
    ports:
      - "3000:3000"
    environment:
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl

📱 ЭТАП 2: ANDROID ПРИЛОЖЕНИЕ (KOTLIN)

2.1 Создание проекта

// build.gradle (Module: app)
dependencies {
    implementation 'org.webrtc:google-webrtc:1.0.32006'
    implementation 'io.socket:socket.io-client:2.0.0'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'androidx.camera:camera-camera2:1.3.0'
    implementation 'androidx.camera:camera-lifecycle:1.3.0'
    implementation 'androidx.camera:camera-view:1.3.0'
}

2.2 WebRTC интеграция

// WebRTCManager.kt
class WebRTCManager(private val context: Context) {
    private var peerConnection: PeerConnection? = null
    private var localVideoTrack: VideoTrack? = null
    private var socket: Socket? = null
    
    fun initialize() {
        // Инициализация PeerConnectionFactory
        PeerConnectionFactory.initializeAndroidGlobals(
            context, 
            true, 
            true, 
            true
        )
        
        val factory = PeerConnectionFactory.builder()
            .createPeerConnectionFactory()
            
        // Настройка ICE серверов
        val iceServers = listOf(
            PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer()
        )
        
        peerConnection = factory.createPeerConnection(
            PeerConnection.RTCConfiguration(iceServers),
            object : PeerConnection.Observer {
                override fun onIceCandidate(candidate: IceCandidate) {
                    sendIceCandidate(candidate)
                }
                // ... другие callbacks
            }
        )
    }
    
    fun startCapture() {
        val videoCapturer = Camera2Enumerator(context).run {
            deviceNames.firstOrNull()?.let { createCapturer(it, null) }
        }
        
        val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", null)
        val videoSource = factory.createVideoSource(false)
        videoCapturer?.initialize(surfaceTextureHelper, context, videoSource.capturerObserver)
        
        localVideoTrack = factory.createVideoTrack("local_video", videoSource)
        peerConnection?.addTrack(localVideoTrack, listOf("stream_id"))
    }
    
    private fun sendIceCandidate(candidate: IceCandidate) {
        socket?.emit("webrtc-ice-candidate", JSONObject().apply {
            put("to", partnerId)
            put("candidate", candidate.toJson())
        })
    }
}

2.3 Сигналинг клиент

// SignalingClient.kt
class SignalingClient(private val serverUrl: String) {
    private var socket: Socket? = null
    private var webRTCManager: WebRTCManager? = null
    
    fun connect(channel: String) {
        socket = IO.socket(serverUrl).apply {
            on(Socket.EVENT_CONNECT) {
                emit("join-channel", JSONObject().apply {
                    put("channel", channel)
                    put("type", "sender")
                })
            }
            
            on("partner-found") { args ->
                val data = args[0] as JSONObject
                val partnerId = data.getString("partnerId")
                createOffer(partnerId)
            }
            
            on("webrtc-offer") { args ->
                val data = args[0] as JSONObject
                handleOffer(data)
            }
            
            connect()
        }
    }
    
    private fun createOffer(partnerId: String) {
        webRTCManager?.createOffer { offer ->
            socket?.emit("webrtc-offer", JSONObject().apply {
                put("to", partnerId)
                put("offer", offer.toJson())
            })
        }
    }
}

💻 ЭТАП 3: DESKTOP ПРИЛОЖЕНИЕ (ELECTRON)

3.1 Инициализация проекта

mkdir videoreader-desktop
cd videoreader-desktop
npm init -y
npm install electron react react-dom typescript
npm install simple-peer socket.io-client --save

3.2 WebRTC компонент

// src/components/VideoReceiver.tsx
import React, { useEffect, useRef, useState } from 'react';
import io from 'socket.io-client';
import SimplePeer from 'simple-peer';

interface VideoReceiverProps {
  channel: string;
  serverUrl: string;
}

export const VideoReceiver: React.FC<VideoReceiverProps> = ({ channel, serverUrl }) => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [connected, setConnected] = useState(false);
  const [peer, setPeer] = useState<SimplePeer.Instance | null>(null);
  const socket = useRef(io(serverUrl));

  useEffect(() => {
    socket.current.emit('join-channel', {
      channel,
      type: 'receiver'
    });

    socket.current.on('partner-found', (data: { partnerId: string }) => {
      const newPeer = new SimplePeer({
        initiator: false,
        trickle: false
      });

      newPeer.on('signal', (signal) => {
        socket.current.emit('webrtc-answer', {
          to: data.partnerId,
          answer: signal
        });
      });

      newPeer.on('stream', (stream) => {
        if (videoRef.current) {
          videoRef.current.srcObject = stream;
          setConnected(true);
        }
      });

      setPeer(newPeer);
    });

    socket.current.on('webrtc-offer', (data: { from: string; offer: any }) => {
      if (peer) {
        peer.signal(data.offer);
      }
    });

    return () => {
      socket.current.disconnect();
      peer?.destroy();
    };
  }, [channel, serverUrl, peer]);

  return (
    <div className="video-receiver">
      <h2>Channel: {channel}</h2>
      <div className={`status ${connected ? 'connected' : 'disconnected'}`}>
        {connected ? 'Connected' : 'Waiting for connection...'}
      </div>
      <video
        ref={videoRef}
        autoPlay
        muted
        style={{ width: '100%', maxWidth: '800px' }}
      />
    </div>
  );
};

3.3 Главное окно Electron

// src/main.ts
import { app, BrowserWindow } from 'electron';
import * as path from 'path';

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
      webSecurity: false // Для разработки, в продакшене нужно настроить правильно
    }
  });

  if (process.env.NODE_ENV === 'development') {
    mainWindow.loadURL('http://localhost:3001');
    mainWindow.webContents.openDevTools();
  } else {
    mainWindow.loadFile(path.join(__dirname, '../build/index.html'));
  }
}

app.whenReady().then(createWindow);

🔐 ЭТАП 4: БЕЗОПАСНОСТЬ

4.1 Аутентификация пользователей

// auth/AuthService.ts
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';

export class AuthService {
  private static readonly JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
  
  static async hashPassword(password: string): Promise<string> {
    return bcrypt.hash(password, 12);
  }
  
  static async verifyPassword(password: string, hash: string): Promise<boolean> {
    return bcrypt.compare(password, hash);
  }
  
  static generateToken(userId: string): string {
    return jwt.sign({ userId }, this.JWT_SECRET, { expiresIn: '24h' });
  }
  
  static verifyToken(token: string): any {
    return jwt.verify(token, this.JWT_SECRET);
  }
}

4.2 HTTPS и WSS

# nginx.conf
server {
    listen 443 ssl http2;
    server_name your-domain.com;
    
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    
    location / {
        proxy_pass http://signaling:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

📊 ЭТАП 5: МОНИТОРИНГ И МАСШТАБИРОВАНИЕ

5.1 Логирование

// logger/Logger.ts
import winston from 'winston';

export const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'videoreader-signaling' },
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console({
      format: winston.format.simple()
    })
  ]
});

5.2 Метрики

// metrics/Metrics.ts
import prometheus from 'prom-client';

export const metrics = {
  connectionsTotal: new prometheus.Counter({
    name: 'connections_total',
    help: 'Total number of connections'
  }),
  
  activeConnections: new prometheus.Gauge({
    name: 'active_connections',
    help: 'Number of active connections'
  }),
  
  channelsActive: new prometheus.Gauge({
    name: 'channels_active',
    help: 'Number of active channels'
  })
};

prometheus.register.registerMetric(metrics.connectionsTotal);
prometheus.register.registerMetric(metrics.activeConnections);
prometheus.register.registerMetric(metrics.channelsActive);

🚀 ДЕПЛОЙMENT

Kubernetes манифесты

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: videoreader-signaling
spec:
  replicas: 3
  selector:
    matchLabels:
      app: videoreader-signaling
  template:
    metadata:
      labels:
        app: videoreader-signaling
    spec:
      containers:
      - name: signaling
        image: your-registry/videoreader-signaling:latest
        ports:
        - containerPort: 3000
        env:
        - name: REDIS_URL
          value: "redis://redis-service:6379"
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: jwt-secret

💡 РЕКОМЕНДАЦИИ ПО УЛУЧШЕНИЮ

1. Производительность

  • Использование WebRTC для P2P соединений
  • CDN для статических файлов
  • Load balancing для сигналинг серверов
  • Redis Cluster для масштабирования

2. Безопасность

  • End-to-end шифрование
  • Rate limiting
  • DDoS защита
  • Регулярные security аудиты

3. Пользовательский опыт

  • Progressive Web App (PWA) версия
  • Адаптивный дизайн
  • Офлайн режим
  • Уведомления

4. Мониторинг

  • Grafana дашборды
  • Alertmanager для уведомлений
  • Jaeger для трейсинга
  • ELK стек для логов

📈 ПЛАН РАЗВИТИЯ

Версия 2.0

  • Групповые видеозвонки
  • Запись и хранение видео
  • AI анализ контента
  • Мобильные push уведомления

Версия 3.0

  • Облачное хранилище
  • API для интеграций
  • Белый лейбл решение
  • Международная локализация

🎯 РЕЗУЛЬТАТ: Современная, безопасная и масштабируемая система видеонаблюдения с собственной инфраструктурой, готовая к продакшн использованию и дальнейшему развитию.