Files
ac-ms-sfu-signaling/src/bootstrap.php
Anton Afanasyeu 0658e26ae4 sfu-signaling: real Janus signaling service implementation
- JanusClient.php: HTTP REST client for Janus Gateway API
- SfuRoomRepository.php: room + participant + stats persistence
- bootstrap.php: PDO, auth, Janus client factory
- public/index.php: front-controller routing
- public/health.php: Janus online check
- public/api/rooms.php: list/create/destroy rooms
- public/api/join.php: SDP offer relay → Janus VideoRoom
- public/api/stats.php: bitrate/packet-loss analytics ingest
- public/dashboard.php: SFU rooms UI (common BE dark-theme styles)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 22:27:59 +02:00

120 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
/*
* SFU signaling bootstrap — minimal standalone setup.
* Reuses the composed BE database and session if available,
* otherwise falls back to own PDO from environment.
*/
define('SFU_ROOT', dirname(__DIR__));
/* ── autoloader ───────────────────────────────────────────────────── */
spl_autoload_register(static function (string $class): void {
$prefix = 'AndroidCast\\Sfu\\';
if (str_starts_with($class, $prefix)) {
$rel = str_replace('\\', '/', substr($class, strlen($prefix)));
$file = SFU_ROOT . '/src/' . $rel . '.php';
if (is_file($file)) {
require_once $file;
}
}
});
/* ── configuration ────────────────────────────────────────────────── */
function sfuConfig(string $key, mixed $default = null): mixed
{
static $cfg = null;
if ($cfg === null) {
$cfgFile = SFU_ROOT . '/config/config.php';
$cfg = is_file($cfgFile) ? (require $cfgFile) : [];
/* Env overrides */
if (getenv('JANUS_URL') !== false) {
$cfg['janus_url'] = getenv('JANUS_URL');
}
if (getenv('DB_DSN') !== false) {
$cfg['db_dsn'] = getenv('DB_DSN');
}
}
return $cfg[$key] ?? $default;
}
/* ── PDO ────────────────────────────────────────────────────────────── */
function sfuPdo(): \PDO
{
static $pdo = null;
if ($pdo === null) {
/* Prefer composed BE PDO if already bootstrapped */
$composedBe = '/var/www/ac/composed/backend/src/bootstrap.php';
if (is_file($composedBe)) {
require_once $composedBe;
if (function_exists('db')) {
$pdo = db();
return $pdo;
}
}
/* Fall back to own DSN */
$dsn = sfuConfig('db_dsn', 'mysql:host=cast01;dbname=androidcast;charset=utf8mb4');
$user = sfuConfig('db_user', 'androidcast');
$pass = sfuConfig('db_pass', '');
$pdo = new \PDO($dsn, $user, $pass, [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
]);
}
return $pdo;
}
/* ── Janus client ────────────────────────────────────────────────── */
function janusClient(): \AndroidCast\Sfu\JanusClient
{
static $client = null;
if ($client === null) {
$url = sfuConfig('janus_url', 'http://10.7.16.237:8088/janus');
$client = new \AndroidCast\Sfu\JanusClient($url);
}
return $client;
}
/* ── Auth helper ─────────────────────────────────────────────────── */
function sfuRequireAuth(): array
{
/* Try composed BE session */
$composedBe = '/var/www/ac/composed/backend/src/bootstrap.php';
if (is_file($composedBe)) {
require_once $composedBe;
if (function_exists('requireAuth')) {
return requireAuth();
}
}
/* Minimal cookie/bearer check */
session_start();
if (empty($_SESSION['user_id'])) {
http_response_code(401);
echo json_encode(['ok' => false, 'error' => 'unauthorized']);
exit;
}
return ['id' => (int) $_SESSION['user_id'], 'email' => $_SESSION['email'] ?? ''];
}
/* ── JSON helpers ────────────────────────────────────────────────── */
function sfuJsonOk(array $data = []): void
{
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => true] + $data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
exit;
}
function sfuJsonErr(string $message, int $code = 400): void
{
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'error' => $message], JSON_UNESCAPED_SLASHES);
exit;
}