Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 90 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,93 @@
# SLS Management UI Environment Variables

# Base URL for the API server
# This will be used by the React app to make API calls
APP_URL=http://localhost:8080

# Ports
SRT_PLAYER_PORT=4000
SRT_SENDER_PORT=4001
SLS_STATS_PORT=8080
SRTLA_PORT=5000
# ============================================================================
# Backend API Configuration
# ============================================================================

# Base URL for the API server (Optional)
# Leave empty for automatic detection based on access method:
# - Direct IP access: Uses IP + SLS_STATS_PORT (e.g., http://192.168.1.100:8080)
# - FQDN access: Uses current domain (e.g., https://srt.example.com)
# Set explicitly only if you need to override automatic detection
# Examples:
# APP_URL=http://192.168.1.100:8080 # Direct IP access
# APP_URL=https://api.example.com # Custom API domain
# APP_URL= # Auto-detection (recommended)
APP_URL=

# ============================================================================
# Service Ports Configuration
# ============================================================================

# SRT Live Server ports
SLS_STATS_PORT=8080 # HTTP API and statistics (required)
SRT_PLAYER_PORT=4000 # SRT player/receiver port (UDP)
SRT_SENDER_PORT=4001 # SRT publisher/sender port (UDP)

# SRTLA (SRT Link Aggregation) port (optional)
# Leave empty if not using SRTLA
SRTLA_PORT=5000 # SRTLA aggregation port (UDP)

# Frontend UI port (for docker-compose)
SLS_MGNT_PORT=3000 # Management UI HTTP port

# ============================================================================
# Nginx Reverse Proxy Configuration Example
# ============================================================================
#
# For HTTPS domain access (e.g., https://srt.example.com), configure nginx:
#
# server {
# listen 443 ssl http2;
# server_name srt.example.com;
#
# # SSL certificates
# ssl_certificate /path/to/ssl/cert.pem;
# ssl_certificate_key /path/to/ssl/key.pem;
#
# # Frontend UI (Management Interface)
# location / {
# proxy_pass http://localhost:3000;
# 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;
# }
#
# # Backend API endpoints
# location /api/ {
# proxy_pass http://localhost:8080/api/;
# 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;
# }
#
# # Statistics endpoints
# location /stats/ {
# proxy_pass http://localhost:8080/stats/;
# 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;
# }
# }
#
# Note: SRT/SRTLA UDP ports (4000, 4001, 5000) cannot be proxied through HTTP.
# These ports must be directly accessible for SRT streaming protocols.
#
# ============================================================================
# Port Summary for Firewall Configuration
# ============================================================================
#
# TCP Ports (HTTP/HTTPS):
# 3000 - Management UI (or 443 if using nginx SSL)
# 8080 - SLS API & Statistics
#
# UDP Ports (SRT Streaming):
# 4000 - SRT Player/Receiver
# 4001 - SRT Publisher/Sender
# 5000 - SRTLA (optional)
#
# ============================================================================

10 changes: 5 additions & 5 deletions html/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ files.forEach(filepath => {

let newContent = content
.toString()
.replaceAll('{{BASE_URL}}', process.env.REACT_APP_BASE_URL)
.replaceAll('{{BASE_URL}}', process.env.REACT_APP_BASE_URL || '')
// Replace new port placeholders
.replaceAll('{{SRT_PLAYER_PORT}}', process.env.REACT_APP_SRT_PLAYER_PORT)
.replaceAll('{{SRT_SENDER_PORT}}', process.env.REACT_APP_SRT_SENDER_PORT)
.replaceAll('{{SLS_STATS_PORT}}', process.env.REACT_APP_SLS_STATS_PORT)
.replaceAll('{{SRTLA_PORT}}', process.env.REACT_APP_SRTLA_PORT);
.replaceAll('{{SRT_PLAYER_PORT}}', process.env.REACT_APP_SRT_PLAYER_PORT || '')
.replaceAll('{{SRT_SENDER_PORT}}', process.env.REACT_APP_SRT_SENDER_PORT || '')
.replaceAll('{{SLS_STATS_PORT}}', process.env.REACT_APP_SLS_STATS_PORT || '')
.replaceAll('{{SRTLA_PORT}}', process.env.REACT_APP_SRTLA_PORT || '');

fs.writeFileSync(filepath, newContent);
});
Expand Down
83 changes: 73 additions & 10 deletions html/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,86 @@
// Configuration with runtime replacement support
// Configuration with runtime replacement support and dynamic backend detection

// Environment variables mapping for compile-time access
const ENV_VARS = {
NODE_ENV: process.env.NODE_ENV || 'production',
REACT_APP_BASE_URL: process.env.REACT_APP_BASE_URL || '{{BASE_URL}}',
REACT_APP_SRT_PLAYER_PORT: process.env.REACT_APP_SRT_PLAYER_PORT || '{{SRT_PLAYER_PORT}}',
REACT_APP_SRT_SENDER_PORT: process.env.REACT_APP_SRT_SENDER_PORT || '{{SRT_SENDER_PORT}}',
REACT_APP_SLS_STATS_PORT: process.env.REACT_APP_SLS_STATS_PORT || '{{SLS_STATS_PORT}}',
REACT_APP_SRTLA_PORT: process.env.REACT_APP_SRTLA_PORT || '{{SRTLA_PORT}}'
};

// Safely access environment variables
const getEnvVar = (key: keyof typeof ENV_VARS, fallback: string = '') => {
return ENV_VARS[key] || fallback;
};

const config = (() => {
// In development: Use environment variable if available
// In production: Use placeholder that will be replaced by run.js
const API_ENDPOINT = process.env.REACT_APP_BASE_URL || '{{BASE_URL}}';
// Helper function to detect if current access is via IP or FQDN
const getCurrentHostInfo = () => {
const hostname = window.location.hostname;
const port = window.location.port;
const protocol = window.location.protocol;

// Check if hostname is an IP address (IPv4)
const isIP = /^(\d{1,3}\.){3}\d{1,3}$/.test(hostname);

return {
hostname,
port,
protocol,
isIP,
isLocalhost: hostname === 'localhost' || hostname === '127.0.0.1'
};
};

// Get dynamic API endpoint based on access method
const getDynamicApiEndpoint = () => {
const hostInfo = getCurrentHostInfo();

// In development, use environment variable if available
if (getEnvVar('NODE_ENV') === 'development') {
const devUrl = getEnvVar('REACT_APP_BASE_URL', 'http://localhost:8080');
return devUrl;
}

// Check for explicitly configured base URL first
const configuredUrl = getEnvVar('REACT_APP_BASE_URL');
if (configuredUrl && !configuredUrl.includes('{{')) {
return configuredUrl;
}

// Dynamic endpoint generation based on access method
let finalUrl;
if (hostInfo.isIP || hostInfo.isLocalhost) {
// Direct IP/localhost access: use same host with stats port
const statsPort = getEnvVar('REACT_APP_SLS_STATS_PORT') || '8080';
const resolvedStatsPort = statsPort.includes('{{') ? '8080' : statsPort;
finalUrl = `${hostInfo.protocol}//${hostInfo.hostname}:${resolvedStatsPort}`;
} else {
// FQDN access: assume nginx proxy setup with direct proxy
finalUrl = `${hostInfo.protocol}//${hostInfo.hostname}${hostInfo.port ? `:${hostInfo.port}` : ''}`;
}

return finalUrl;
};

// SRT/SRTLA ports configuration
const SRT_PLAYER_PORT = process.env.REACT_APP_SRT_PLAYER_PORT || '{{SRT_PLAYER_PORT}}';
const SRT_SENDER_PORT = process.env.REACT_APP_SRT_SENDER_PORT || '{{SRT_SENDER_PORT}}';
const SLS_STATS_PORT = process.env.REACT_APP_SLS_STATS_PORT || '{{SLS_STATS_PORT}}';
const SRT_PLAYER_PORT = getEnvVar('REACT_APP_SRT_PLAYER_PORT');
const SRT_SENDER_PORT = getEnvVar('REACT_APP_SRT_SENDER_PORT');
const SLS_STATS_PORT = getEnvVar('REACT_APP_SLS_STATS_PORT');
// SRTLA_PORT is optional - will be empty if not configured
const SRTLA_PORT = process.env.REACT_APP_SRTLA_PORT || '{{SRTLA_PORT}}';
const SRTLA_PORT = getEnvVar('REACT_APP_SRTLA_PORT');

return {
apiEndpoint: API_ENDPOINT,
apiEndpoint: getDynamicApiEndpoint(),
srtPlayerPort: SRT_PLAYER_PORT,
srtSenderPort: SRT_SENDER_PORT,
slsStatsPort: SLS_STATS_PORT,
srtlaPort: SRTLA_PORT
srtlaPort: SRTLA_PORT,
// Add helper methods for debugging
getHostInfo: getCurrentHostInfo,
getDynamicEndpoint: getDynamicApiEndpoint
};
})();

Expand Down
12 changes: 12 additions & 0 deletions html/src/react-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// <reference types="react-scripts" />

declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
REACT_APP_BASE_URL?: string;
REACT_APP_SRT_PLAYER_PORT?: string;
REACT_APP_SRT_SENDER_PORT?: string;
REACT_APP_SLS_STATS_PORT?: string;
REACT_APP_SRTLA_PORT?: string;
}
}
19 changes: 19 additions & 0 deletions html/src/utils/url-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ const getServerHostname = (): string => {
}
};

// Check if we're accessing via FQDN (through nginx proxy)
const isAccessingViaFqdn = (): boolean => {
const hostname = window.location.hostname;
// If hostname is an IP address or localhost, we're accessing directly
return !(
hostname === 'localhost' ||
hostname === '127.0.0.1' ||
/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname)
);
};

// Check if a port is configured (not empty and not a placeholder)
const isPortConfigured = (port: string): boolean => {
return !!(port && port !== '' && !port.includes('{{'));
Expand Down Expand Up @@ -40,6 +51,14 @@ export const generateSrtlaPublisherUrl = (publisherId: string): string => {
// Generate Stats URL
export const generateStatsUrl = (playerId: string): string => {
const hostname = getServerHostname();

// If accessing via FQDN (nginx proxy), use the standard HTTP port without explicit port number
if (isAccessingViaFqdn()) {
const protocol = window.location.protocol; // Use same protocol as current page
return `${protocol}//${hostname}/stats/${playerId}?legacy=1`;
}

// For direct IP access, include the port
const port = config.slsStatsPort;
return `http://${hostname}:${port}/stats/${playerId}?legacy=1`;
};
Expand Down