1
0
Fork 1
mirror of https://github.com/pixelfed/pixelfed.git synced 2025-03-11 22:53:07 +00:00

Merge pull request #3562 from pixelfed/staging

Staging
This commit is contained in:
daniel 2022-07-01 02:36:51 -06:00 committed by GitHub
commit 04caa30e9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 3552 additions and 460 deletions

View file

@ -1,35 +1,62 @@
APP_NAME="Pixelfed Prod"
APP_ENV=production
APP_NAME="Pixelfed"
APP_ENV="production"
APP_KEY=
APP_DEBUG=false
APP_DEBUG="false"
APP_URL=http://localhost
# Instance Configuration
OPEN_REGISTRATION="false"
ENFORCE_EMAIL_VERIFICATION="false"
PF_MAX_USERS="1000"
OAUTH_ENABLED="true"
# Media Configuration
PF_OPTIMIZE_IMAGES="true"
IMAGE_QUALITY="80"
MAX_PHOTO_SIZE="15000"
MAX_CAPTION_LENGTH="500"
MAX_ALBUM_LENGTH="4"
# Instance URL Configuration
APP_URL="http://localhost"
APP_DOMAIN="localhost"
ADMIN_DOMAIN="localhost"
SESSION_DOMAIN="localhost"
TRUST_PROXIES="*"
LOG_CHANNEL=stack
# Database Configuration
DB_CONNECTION="mysql"
DB_HOST="127.0.0.1"
DB_PORT="3306"
DB_DATABASE="pixelfed"
DB_USERNAME="pixelfed"
DB_PASSWORD="pixelfed"
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=pixelfed
DB_USERNAME=pixelfed
DB_PASSWORD=pixelfed
BROADCAST_DRIVER=log
CACHE_DRIVER=redis
SESSION_DRIVER=database
QUEUE_DRIVER=redis
# Redis Configuration
REDIS_CLIENT="predis"
REDIS_SCHEME="tcp"
REDIS_HOST="127.0.0.1"
REDIS_PASSWORD="null"
REDIS_PORT="6379"
# Laravel Configuration
SESSION_DRIVER="database"
CACHE_DRIVER="redis"
QUEUE_DRIVER="redis"
BROADCAST_DRIVER="log"
LOG_CHANNEL="stack"
HORIZON_PREFIX="horizon-"
REDIS_SCHEME=tcp
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# ActivityPub Configuration
ACTIVITY_PUB="false"
AP_REMOTE_FOLLOW="false"
AP_INBOX="false"
AP_OUTBOX="false"
AP_SHAREDINBOX="false"
# Experimental Configuration
EXP_EMC="true"
## Mail Configuration (Post-Installer)
MAIL_DRIVER=log
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
@ -39,15 +66,14 @@ MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="pixelfed@example.com"
MAIL_FROM_NAME="Pixelfed"
OPEN_REGISTRATION=true
ENFORCE_EMAIL_VERIFICATION=true
PF_MAX_USERS=1000
MAX_PHOTO_SIZE=15000
MAX_CAPTION_LENGTH=150
MAX_ALBUM_LENGTH=4
ACTIVITY_PUB=false
AP_REMOTE_FOLLOW=false
AP_INBOX=false
PF_COSTAR_ENABLED=false
## S3 Configuration (Post-Installer)
PF_ENABLE_CLOUD=false
FILESYSTEM_DRIVER=local
FILESYSTEM_CLOUD=s3
#AWS_ACCESS_KEY_ID=
#AWS_SECRET_ACCESS_KEY=
#AWS_DEFAULT_REGION=
#AWS_BUCKET=<BucketName>
#AWS_URL=
#AWS_ENDPOINT=
#AWS_USE_PATH_STYLE_ENDPOINT=false

View file

@ -57,52 +57,81 @@ class Installer extends Command
$this->info(' ');
$this->info('Pixelfed version: ' . config('pixelfed.version'));
$this->line(' ');
$this->installerSteps();
}
protected function installerSteps()
{
$this->envCheck();
$this->envCreate();
$this->installType();
if ($this->installType === 'Advanced') {
$this->info('Installer: Advanced...');
$this->checkPHPRequiredDependencies();
$this->checkFFmpegDependencies();
$this->checkOptimiseDependencies();
$this->checkDiskPermissions();
$this->envProd();
$this->instanceDB();
$this->instanceRedis();
$this->instanceURL();
$this->activityPubSettings();
$this->laravelSettings();
$this->instanceSettings();
$this->mediaSettings();
$this->dbMigrations();
$this->validateEnv();
$this->resetArtisanCache();
} else {
$this->info('Installer: Simple...');
$this->checkDiskPermissions();
$this->envProd();
$this->instanceDB();
$this->instanceRedis();
$this->instanceURL();
$this->activityPubSettings();
$this->instanceSettings();
$this->dbMigrations();
$this->validateEnv();
$this->resetArtisanCache();
}
}
protected function envCheck()
{
if( file_exists(base_path('.env')) &&
filesize(base_path('.env')) !== 0 &&
!$this->option('dangerously-overwrite-env')
if (file_exists(base_path('.env')) &&
filesize(base_path('.env')) !== 0 &&
!$this->option('dangerously-overwrite-env')
) {
$this->line('');
$this->error('Installation aborted, found existing .env file');
$this->line('Run the following command to re-run the installer:');
$this->line('');
$this->info('php artisan install --dangerously-overwrite-env');
$this->error('Existing .env File Found - Installation Aborted');
$this->line('Run the following command to re-run the installer: php artisan install --dangerously-overwrite-env');
$this->line('');
exit;
}
$this->installType();
}
protected function envCreate()
{
$this->line('');
$this->info('Creating .env if required');
if (!file_exists(app()->environmentFilePath())) {
exec('cp .env.example .env');
}
}
protected function installType()
{
$type = $this->choice('Select installation type', ['Simple', 'Advanced'], 0);
$this->installType = $type;
$this->preflightCheck();
$type = $this->choice('Select installation type', ['Simple', 'Advanced'], 1);
$this->installType = $type;
}
protected function preflightCheck()
protected function checkPHPRequiredDependencies()
{
if($this->installType === 'Advanced') {
$this->info('Scanning system...');
$this->line(' ');
$this->info('Checking for installed dependencies...');
$redis = Redis::connection();
if($redis->ping()) {
$this->info('- Found redis!');
} else {
$this->error('- Redis not found, aborting installation');
exit;
}
}
$this->checkPhpDependencies();
}
$this->line(' ');
$this->info('Checking for Required PHP Extensions...');
protected function checkPhpDependencies()
{
$extensions = [
'bcmath',
'ctype',
@ -110,197 +139,326 @@ class Installer extends Command
'json',
'mbstring',
'openssl',
'gd',
'intl',
'xml',
'zip',
'redis',
];
if($this->installType === 'Advanced') {
$ffmpeg = exec('which ffmpeg');
if(empty($ffmpeg)) {
$this->error('FFmpeg not found, please install it.');
$this->error('Cancelling installation.');
exit;
} else {
$this->info('- Found FFmpeg!');
}
$this->line('');
$this->info('Checking for required php extensions...');
}
foreach($extensions as $ext) {
if(extension_loaded($ext) == false) {
$this->error("\"{$ext}\" PHP extension not found, aborting installation");
exit;
foreach ($extensions as $ext) {
if (extension_loaded($ext) == false) {
$this->error("- \"{$ext}\" not found");
} else {
$this->info("- \"{$ext}\" found");
}
}
if($this->installType === 'Advanced') {
$this->info("- Required PHP extensions found!");
}
$this->checkPermissions();
$continue = $this->choice('Do you wish to continue?', ['yes', 'no'], 0);
$this->continue = $continue;
if ($this->continue === 'no') {
$this->info('Exiting Installer.');
exit;
}
}
protected function checkPermissions()
protected function checkFFmpegDependencies()
{
if($this->installType === 'Advanced') {
$this->line('');
$this->info('Checking for proper filesystem permissions...');
}
$this->line(' ');
$this->info('Checking for Required FFmpeg dependencies...');
$ffmpeg = exec('which ffmpeg');
if (empty($ffmpeg)) {
$this->error("- \"{$ext}\" FFmpeg not found, aborting installation");
exit;
} else {
$this->info('- Found FFmpeg!');
}
}
protected function checkOptimiseDependencies()
{
$this->line(' ');
$this->info('Checking for Optional Media Optimisation dependencies...');
$dependencies = [
'jpegoptim',
'optipng',
'pngquant',
'gifsicle',
];
foreach ($dependencies as $dep) {
$which = exec("which $dep");
if (empty($which)) {
$this->error("- \"{$dep}\" not found");
} else {
$this->info("- \"{$dep}\" found");
}
}
}
protected function checkDiskPermissions()
{
$this->line('');
$this->info('Checking for proper filesystem permissions...');
$this->callSilently('storage:link');
$paths = [
base_path('bootstrap'),
base_path('storage')
base_path('storage'),
];
foreach($paths as $path) {
if(is_writeable($path) == false) {
foreach ($paths as $path) {
if (is_writeable($path) == false) {
$this->error("- Invalid permission found! Aborting installation.");
$this->error(" Please make the following path writeable by the web server:");
$this->error(" $path");
exit;
} else {
if($this->installType === 'Advanced') {
$this->info("- Found valid permissions for {$path}");
}
$this->info("- Found valid permissions for {$path}");
}
}
$this->createEnv();
}
protected function createEnv()
protected function envProd()
{
$this->line('');
if(!file_exists(app()->environmentFilePath())) {
exec('cp .env.example .env');
$this->updateEnvFile('APP_ENV', 'setup');
$this->call('key:generate');
}
$name = $this->ask('Site name [ex: Pixelfed]');
$this->updateEnvFile('APP_NAME', $name ?? 'pixelfed');
$domain = $this->ask('Site Domain [ex: pixelfed.com]');
if(empty($domain)) {
$this->error('You must set the site domain');
exit;
}
if(starts_with($domain, 'http')) {
$this->error('The site domain cannot start with https://, you must use the FQDN (eg: example.org)');
exit;
}
if(strpos($domain, '.') == false) {
$this->error('You must enter a valid site domain');
exit;
}
$this->updateEnvFile('APP_DOMAIN', $domain ?? 'example.org');
$this->updateEnvFile('ADMIN_DOMAIN', $domain ?? 'example.org');
$this->updateEnvFile('SESSION_DOMAIN', $domain ?? 'example.org');
$this->updateEnvFile('APP_URL', 'https://' . $domain);
$database = $this->choice('Select database driver', ['mysql', 'pgsql'], 0);
$this->updateEnvFile('DB_CONNECTION', $database ?? 'mysql');
$database_host = $this->ask('Select database host', '127.0.0.1');
$this->updateEnvFile('DB_HOST', $database_host ?? 'mysql');
$database_port_default = $database === 'mysql' ? 3306 : 5432;
$database_port = $this->ask('Select database port', $database_port_default);
$this->updateEnvFile('DB_PORT', $database_port ?? $database_port_default);
$database_db = $this->ask('Select database', 'pixelfed');
$this->updateEnvFile('DB_DATABASE', $database_db ?? 'pixelfed');
$database_username = $this->ask('Select database username', 'pixelfed');
$this->updateEnvFile('DB_USERNAME', $database_username ?? 'pixelfed');
$db_pass = str_random(64);
$database_password = $this->secret('Select database password', $db_pass);
$this->updateEnvFile('DB_PASSWORD', $database_password);
$dsn = "{$database}:dbname={$database_db};host={$database_host};port={$database_port};";
try {
$dbh = new PDO($dsn, $database_username, $database_password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (\PDOException $e) {
$this->error('Cannot connect to database, check your credentials and try again');
exit;
}
if($this->installType === 'Advanced') {
$cache = $this->choice('Select cache driver', ["redis", "apc", "array", "database", "file", "memcached"], 0);
$this->updateEnvFile('CACHE_DRIVER', $cache ?? 'redis');
$session = $this->choice('Select session driver', ["redis", "file", "cookie", "database", "apc", "memcached", "array"], 0);
$this->updateEnvFile('SESSION_DRIVER', $session ?? 'redis');
$redis_host = $this->ask('Set redis host', 'localhost');
$this->updateEnvFile('REDIS_HOST', $redis_host);
$redis_password = $this->ask('Set redis password', 'null');
$this->updateEnvFile('REDIS_PASSWORD', $redis_password);
$redis_port = $this->ask('Set redis port', 6379);
$this->updateEnvFile('REDIS_PORT', $redis_port);
}
$open_registration = $this->choice('Allow new registrations?', ['false', 'true'], 0);
$this->updateEnvFile('OPEN_REGISTRATION', $open_registration);
$activitypub_federation = $this->choice('Enable ActivityPub federation?', ['false', 'true'], 1);
$this->updateEnvFile('ACTIVITY_PUB', $activitypub_federation);
$this->updateEnvFile('AP_INBOX', $activitypub_federation);
$this->updateEnvFile('AP_SHAREDINBOX', $activitypub_federation);
$this->updateEnvFile('AP_REMOTE_FOLLOW', $activitypub_federation);
$enforce_email_verification = $this->choice('Enforce email verification?', ['false', 'true'], 1);
$this->updateEnvFile('ENFORCE_EMAIL_VERIFICATION', $enforce_email_verification);
$enable_mobile_apis = $this->choice('Enable mobile app/apis support?', ['false', 'true'], 1);
$this->updateEnvFile('OAUTH_ENABLED', $enable_mobile_apis);
$this->updateEnvFile('EXP_EMC', $enable_mobile_apis);
$optimize_media = $this->choice('Optimize media uploads? Requires jpegoptim and other dependencies!', ['false', 'true'], 0);
$this->updateEnvFile('PF_OPTIMIZE_IMAGES', $optimize_media);
if($this->installType === 'Advanced') {
if($optimize_media === 'true') {
$image_quality = $this->ask('Set image optimization quality between 1-100. Default is 80%, lower values use less disk space at the expense of image quality.', '80');
if($image_quality < 1) {
$this->error('Min image quality is 1. You should avoid such a low value, 60 at minimum is recommended.');
exit;
}
if($image_quality > 100) {
$this->error('Max image quality is 100');
exit;
}
$this->updateEnvFile('IMAGE_QUALITY', $image_quality);
}
$max_photo_size = $this->ask('Max photo upload size in kilobytes. Default 15000 which is equal to 15MB', '15000');
if($max_photo_size * 1024 > $this->parseSize(ini_get('post_max_size'))) {
$this->error('Max photo size (' . (round($max_photo_size / 1000)) . 'M) cannot exceed php.ini `post_max_size` of ' . ini_get('post_max_size'));
exit;
}
$this->updateEnvFile('MAX_PHOTO_SIZE', $max_photo_size);
$max_caption_length = $this->ask('Max caption limit. Default to 500, max 5000.', '500');
if($max_caption_length > 5000) {
$this->error('Max caption length is 5000 characters.');
exit;
}
$this->updateEnvFile('MAX_CAPTION_LENGTH', $max_caption_length);
$max_album_length = $this->ask('Max photos allowed per album. Choose a value between 1 and 10.', '4');
if($max_album_length < 1) {
$this->error('Min album length is 1 photos per album.');
exit;
}
if($max_album_length > 10) {
$this->error('Max album length is 10 photos per album.');
exit;
}
$this->updateEnvFile('MAX_ALBUM_LENGTH', $max_album_length);
}
$this->info('Enabling production');
$this->updateEnvFile('APP_ENV', 'production');
$this->postInstall();
$this->updateEnvFile('APP_DEBUG', 'false');
$this->call('key:generate', ['--force' => true]);
}
protected function instanceDB()
{
$this->line('');
$this->info('Database Settings:');
$database = $this->choice('Select database driver', ['mysql', 'pgsql'], 0);
$database_host = $this->ask('Select database host', '127.0.0.1');
$database_port_default = $database === 'mysql' ? 3306 : 5432;
$database_port = $this->ask('Select database port', $database_port_default);
$database_db = $this->ask('Select database', 'pixelfed');
$database_username = $this->ask('Select database username', 'pixelfed');
$database_password = $this->secret('Select database password');
$this->updateEnvFile('DB_CONNECTION', $database);
$this->updateEnvFile('DB_HOST', $database_host);
$this->updateEnvFile('DB_PORT', $database_port);
$this->updateEnvFile('DB_DATABASE', $database_db);
$this->updateEnvFile('DB_USERNAME', $database_username);
$this->updateEnvFile('DB_PASSWORD', $database_password);
$this->info('Testing Database...');
$dsn = "{$database}:dbname={$database_db};host={$database_host};port={$database_port};";
try {
$dbh = new PDO($dsn, $database_username, $database_password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (\PDOException $e) {
$this->error('Cannot connect to database, check your details and try again');
exit;
}
$this->info('- Connected to DB Successfully');
}
protected function instanceRedis()
{
$this->line('');
$this->info('Redis Settings:');
$redis_client = $this->choice('Set redis client (PHP extension)', ['phpredis', 'predis'], 0);
$redis_host = $this->ask('Set redis host', 'localhost');
$redis_password = $this->ask('Set redis password', 'null');
$redis_port = $this->ask('Set redis port', 6379);
$this->updateEnvFile('REDIS_CLIENT', $redis_client);
$this->updateEnvFile('REDIS_SCHEME', 'tcp');
$this->updateEnvFile('REDIS_HOST', $redis_host);
$this->updateEnvFile('REDIS_PASSWORD', $redis_password);
$this->updateEnvFile('REDIS_PORT', $redis_port);
$this->info('Testing Redis...');
$redis = Redis::connection();
if ($redis->ping()) {
$this->info('- Connected to Redis Successfully!');
} else {
$this->error('Cannot connect to Redis, check your details and try again');
exit;
}
}
protected function instanceURL()
{
$this->line('');
$this->info('Instance URL Settings:');
$name = $this->ask('Site name [ex: Pixelfed]', 'Pixelfed');
$domain = $this->ask('Site Domain [ex: pixelfed.com]');
$domain = strtolower($domain);
if (empty($domain)) {
$this->error('You must set the site domain');
exit;
}
if (starts_with($domain, 'http')) {
$this->error('The site domain cannot start with https://, you must use the FQDN (eg: example.org)');
exit;
}
if (strpos($domain, '.') == false) {
$this->error('You must enter a valid site domain');
exit;
}
$this->updateEnvFile('APP_NAME', $name);
$this->updateEnvFile('APP_URL', 'https://' . $domain);
$this->updateEnvFile('APP_DOMAIN', $domain);
$this->updateEnvFile('ADMIN_DOMAIN', $domain);
$this->updateEnvFile('SESSION_DOMAIN', $domain);
}
protected function laravelSettings()
{
$this->line('');
$this->info('Laravel Settings (Defaults are recommended):');
$session = $this->choice('Select session driver', ["database", "file", "cookie", "redis", "memcached", "array"], 0);
$cache = $this->choice('Select cache driver', ["redis", "apc", "array", "database", "file", "memcached"], 0);
$queue = $this->choice('Select queue driver', ["redis", "database", "sync", "beanstalkd", "sqs", "null"], 0);
$broadcast = $this->choice('Select broadcast driver', ["log", "redis", "pusher", "null"], 0);
$log = $this->choice('Select Log Channel', ["stack", "single", "daily", "stderr", "syslog", "null"], 0);
$horizon = $this->ask('Set Horizon Prefix [ex: horizon-]', 'horizon-');
$this->updateEnvFile('SESSION_DRIVER', $session);
$this->updateEnvFile('CACHE_DRIVER', $cache);
$this->updateEnvFile('QUEUE_DRIVER', $queue);
$this->updateEnvFile('BROADCAST_DRIVER', $broadcast);
$this->updateEnvFile('LOG_CHANNEL', $log);
$this->updateEnvFile('HORIZON_PREFIX', $horizon);
}
protected function instanceSettings()
{
$this->line('');
$this->info('Instance Settings:');
$max_registration = $this->ask('Set Maximum users on this instance.', '1000');
$open_registration = $this->choice('Allow new registrations?', ['false', 'true'], 0);
$enforce_email_verification = $this->choice('Enforce email verification?', ['false', 'true'], 0);
$enable_mobile_apis = $this->choice('Enable mobile app/apis support?', ['false', 'true'], 1);
$this->updateEnvFile('PF_MAX_USERS', $max_registration);
$this->updateEnvFile('OPEN_REGISTRATION', $open_registration);
$this->updateEnvFile('ENFORCE_EMAIL_VERIFICATION', $enforce_email_verification);
$this->updateEnvFile('OAUTH_ENABLED', $enable_mobile_apis);
$this->updateEnvFile('EXP_EMC', $enable_mobile_apis);
}
protected function activityPubSettings()
{
$this->line('');
$this->info('Federation Settings:');
$activitypub_federation = $this->choice('Enable ActivityPub federation?', ['false', 'true'], 1);
$this->updateEnvFile('ACTIVITY_PUB', $activitypub_federation);
$this->updateEnvFile('AP_REMOTE_FOLLOW', $activitypub_federation);
$this->updateEnvFile('AP_INBOX', $activitypub_federation);
$this->updateEnvFile('AP_OUTBOX', $activitypub_federation);
$this->updateEnvFile('AP_SHAREDINBOX', $activitypub_federation);
}
protected function mediaSettings()
{
$this->line('');
$this->info('Media Settings:');
$optimize_media = $this->choice('Optimize media uploads? Requires jpegoptim and other dependencies!', ['false', 'true'], 1);
$image_quality = $this->ask('Set image optimization quality between 1-100. Default is 80%, lower values use less disk space at the expense of image quality.', '80');
if ($image_quality < 1) {
$this->error('Min image quality is 1. You should avoid such a low value, 60 at minimum is recommended.');
exit;
}
if ($image_quality > 100) {
$this->error('Max image quality is 100');
exit;
}
$this->info('Note: Max photo size cannot exceed `post_max_size` in php.ini.');
$max_photo_size = $this->ask('Max photo upload size in kilobytes. Default 15000 which is equal to 15MB', '15000');
$max_caption_length = $this->ask('Max caption limit. Default to 500, max 5000.', '500');
if ($max_caption_length > 5000) {
$this->error('Max caption length is 5000 characters.');
exit;
}
$max_album_length = $this->ask('Max photos allowed per album. Choose a value between 1 and 10.', '4');
if ($max_album_length < 1) {
$this->error('Min album length is 1 photos per album.');
exit;
}
if ($max_album_length > 10) {
$this->error('Max album length is 10 photos per album.');
exit;
}
$this->updateEnvFile('PF_OPTIMIZE_IMAGES', $optimize_media);
$this->updateEnvFile('IMAGE_QUALITY', $image_quality);
$this->updateEnvFile('MAX_PHOTO_SIZE', $max_photo_size);
$this->updateEnvFile('MAX_CAPTION_LENGTH', $max_caption_length);
$this->updateEnvFile('MAX_ALBUM_LENGTH', $max_album_length);
}
protected function dbMigrations()
{
$this->line('');
$this->info('Note: We recommend running database migrations now!');
$confirm = $this->choice('Do you want to run the database migrations?', ['Yes', 'No'], 0);
if ($confirm === 'Yes') {
sleep(3);
$this->line('');
$this->info('Migrating DB:');
$this->call('migrate', ['--force' => true]);
$this->line('');
$this->info('Importing Cities:');
$this->call('import:cities');
$this->line('');
$this->info('Creating Federation Instance Actor:');
$this->call('instance:actor');
$this->line('');
$this->info('Creating Password Keys for API:');
$this->call('passport:keys', ['--force' => true]);
$confirm = $this->choice('Do you want to create an admin account?', ['Yes', 'No'], 0);
if ($confirm === 'Yes') {
$this->call('user:create');
}
}
}
protected function resetArtisanCache()
{
$this->call('config:cache');
$this->call('route:cache');
$this->call('view:cache');
}
protected function validateEnv()
{
$this->checkEnvKeys('APP_KEY', "key:generate failed?");
$this->checkEnvKeys('APP_ENV', "APP_ENV value should be production");
$this->checkEnvKeys('APP_DEBUG', "APP_DEBUG value should be false");
}
#####
# Installer Functions
#####
protected function checkEnvKeys($key, $error)
{
$envPath = app()->environmentFilePath();
$payload = file_get_contents($envPath);
if ($existing = $this->existingEnv($key, $payload)) {
} else {
$this->error("$key empty - $error");
}
}
protected function updateEnvFile($key, $value)
@ -333,37 +491,14 @@ class Installer extends Command
fclose($file);
}
protected function postInstall()
protected function parseSize($size)
{
$this->line('');
$this->info('We recommend running database migrations now, or you can do it manually later.');
$confirm = $this->choice('Do you want to run the database migrations?', ['No', 'Yes'], 0);
if($confirm === 'Yes') {
$this->callSilently('config:clear');
sleep(3);
$this->call('migrate', ['--force' => true]);
$this->callSilently('instance:actor');
$this->callSilently('passport:install');
$confirm = $this->choice('Do you want to create an admin account?', ['No', 'Yes'], 0);
if($confirm === 'Yes') {
$this->call('user:create');
}
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
$size = preg_replace('/[^0-9\.]/', '', $size);
if ($unit) {
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
} else {
$this->callSilently('config:cache');
return round($size);
}
$this->info('Pixelfed has been successfully installed!');
}
protected function parseSize($size) {
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
$size = preg_replace('/[^0-9\.]/', '', $size);
if ($unit) {
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
}
else {
return round($size);
}
}
}

View file

@ -84,6 +84,11 @@ class UserCreate extends Command
exit;
}
if (strlen($password) < 6) {
$this->error('Must be 6 or more characters, please try again...');
exit;
}
$is_admin = $this->confirm('Make this user an admin?');
$confirm_email = $this->confirm('Manually verify email address?');

View file

@ -0,0 +1,51 @@
<?php
namespace App\Events\LiveStream;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\LiveStream;
class BanUser implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $livestream;
public $profileId;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(LiveStream $livestream, $profileId)
{
$this->livestream = $livestream;
$this->profileId = $profileId;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('live.chat.' . $this->livestream->profile_id);
}
public function broadcastAs()
{
return 'chat.ban-user';
}
public function broadcastWith()
{
return ['id' => $this->profileId];
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Events\LiveStream;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\LiveStream;
class DeleteChatComment implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $livestream;
public $chatmsg;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(LiveStream $livestream, $chatmsg)
{
$this->livestream = $livestream;
$this->chatmsg = $chatmsg;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('live.chat.' . $this->livestream->profile_id);
}
public function broadcastAs()
{
return 'chat.delete-message';
}
public function broadcastWith()
{
return ['id' => $this->chatmsg['id']];
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Events\LiveStream;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\LiveStream;
class NewChatComment implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $livestream;
public $chatmsg;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(LiveStream $livestream, $chatmsg)
{
$this->livestream = $livestream;
$this->chatmsg = $chatmsg;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('live.chat.' . $this->livestream->profile_id);
}
public function broadcastAs()
{
return 'chat.new-message';
}
public function broadcastWith()
{
return ['msg' => $this->chatmsg];
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Events\LiveStream;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\LiveStream;
class PinChatMessage implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $livestream;
public $chatmsg;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(LiveStream $livestream, $chatmsg)
{
$this->livestream = $livestream;
$this->chatmsg = $chatmsg;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('live.chat.' . $this->livestream->profile_id);
}
public function broadcastAs()
{
return 'chat.pin-message';
}
public function broadcastWith()
{
return $this->chatmsg;
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Events\LiveStream;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class StreamEnd implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $id;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($id)
{
$this->id = $id;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('live.chat.' . $this->id);
}
public function broadcastAs()
{
return 'stream.end';
}
public function broadcastWith()
{
return ['ts' => time() ];
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Events\LiveStream;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class StreamStart implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $id;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($id)
{
$this->id = $id;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('live.chat.' . $this->id);
}
public function broadcastAs()
{
return 'stream.start';
}
public function broadcastWith()
{
return ['ts' => time() ];
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Events\LiveStream;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\LiveStream;
class UnpinChatMessage implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $livestream;
public $chatmsg;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(LiveStream $livestream, $chatmsg)
{
$this->livestream = $livestream;
$this->chatmsg = $chatmsg;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('live.chat.' . $this->livestream->profile_id);
}
public function broadcastAs()
{
return 'chat.unpin-message';
}
public function broadcastWith()
{
return $this->chatmsg;
}
}

View file

@ -101,6 +101,16 @@ class ApiV1Controller extends Controller
return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
}
public function getWebsocketConfig()
{
return config('broadcasting.default') === 'pusher' ? [
'host' => config('broadcasting.connections.pusher.options.host'),
'port' => config('broadcasting.connections.pusher.options.port'),
'key' => config('broadcasting.connections.pusher.key'),
'cluster' => config('broadcasting.connections.pusher.options.cluster')
] : [];
}
public function getApp(Request $request)
{
if(!$request->user()) {

View file

@ -9,6 +9,14 @@ use Illuminate\Support\Facades\Storage;
use App\Services\AccountService;
use App\Services\FollowerService;
use App\Services\LiveStreamService;
use App\User;
use App\Events\LiveStream\NewChatComment;
use App\Events\LiveStream\DeleteChatComment;
use App\Events\LiveStream\BanUser;
use App\Events\LiveStream\PinChatMessage;
use App\Events\LiveStream\UnpinChatMessage;
use App\Events\LiveStream\StreamStart;
use App\Events\LiveStream\StreamEnd;
class LiveStreamController extends Controller
{
@ -63,32 +71,22 @@ class LiveStreamController extends Controller
abort_if(!config('livestreaming.enabled'), 400);
abort_if(!$request->user(), 403);
$stream = LiveStream::whereProfileId($request->input('profile_id'))->first();
$stream = LiveStream::whereProfileId($request->input('profile_id'))
->whereNotNull('live_at')
->orderByDesc('live_at')
->first();
if(!$stream) {
return [];
}
$res = [];
$owner = $stream->profile_id == $request->user()->profile_id;
$owner = $request->user() ? $stream->profile_id == $request->user()->profile_id : false;
if($stream->visibility === 'private') {
abort_if(!$owner && !FollowerService::follows($request->user()->profile_id, $stream->profile_id), 403, 'LSE:011');
}
if($owner) {
$res['stream_key'] = $stream->stream_key;
$res['stream_id'] = $stream->stream_id;
$res['stream_url'] = $stream->getStreamKeyUrl();
}
if($stream->live_at == null) {
$res['hls_url'] = null;
$res['name'] = $stream->name;
$res['description'] = $stream->description;
return $res;
}
$res = [
'hls_url' => $stream->getHlsUrl(),
'name' => $stream->name,
@ -98,6 +96,47 @@ class LiveStreamController extends Controller
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
}
public function getUserStreamAsGuest(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
$stream = LiveStream::whereProfileId($request->input('profile_id'))
->whereVisibility('public')
->whereNotNull('live_at')
->orderByDesc('live_at')
->first();
if(!$stream) {
return [];
}
$res = [];
$res = [
'hls_url' => $stream->getHlsUrl(),
'name' => $stream->name,
'description' => $stream->description
];
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
}
public function showProfilePlayer(Request $request, $username)
{
abort_if(!config('livestreaming.enabled'), 400);
$user = User::whereUsername($username)->firstOrFail();
$id = (string) $user->profile_id;
$stream = LiveStream::whereProfileId($id)
->whereNotNull('live_at')
->first();
abort_if(!$request->user() && $stream && $stream->visibility !== 'public', 404);
return view('live.player', compact('id'));
}
public function deleteStream(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
@ -107,6 +146,8 @@ class LiveStreamController extends Controller
->get()
->each(function($stream) {
Storage::deleteDirectory("public/live-hls/{$stream->stream_id}");
LiveStreamService::clearChat($stream->profile_id);
StreamEnd::dispatch($stream->profile_id);
$stream->delete();
});
@ -118,7 +159,7 @@ class LiveStreamController extends Controller
abort_if(!config('livestreaming.enabled'), 400);
abort_if(!$request->user(), 403);
return LiveStream::whereVisibility('local')->whereNotNull('live_at')->get()->map(function($stream) {
return LiveStream::whereIn('visibility', ['local', 'public'])->whereNotNull('live_at')->get()->map(function($stream) {
return [
'account' => AccountService::get($stream->profile_id),
'stream_id' => $stream->stream_id
@ -162,22 +203,30 @@ class LiveStreamController extends Controller
'message' => 'required|max:140'
]);
$stream = LiveStream::whereProfileId($request->input('profile_id'))->firstOrFail();
$stream = LiveStream::whereProfileId($request->input('profile_id'))
->whereNotNull('live_at')
->firstOrFail();
$owner = $stream->profile_id == $request->user()->profile_id;
if($stream->visibility === 'private') {
abort_if(!$owner && !FollowerService::follows($request->user()->profile_id, $stream->profile_id), 403, 'LSE:022');
abort_if(!$owner && !FollowerService::follows($request->user()->profile_id, $stream->profile_id), 403);
}
$user = AccountService::get($request->user()->profile_id);
abort_if(!$user, 422);
$res = [
'id' => (string) Str::uuid(),
'pid' => (string) $request->user()->profile_id,
'username' => $request->user()->username,
'avatar' => $user['avatar'],
'username' => $user['username'],
'text' => $request->input('message'),
'ts' => now()->timestamp
];
LiveStreamService::addComment($stream->profile_id, json_encode($res, JSON_UNESCAPED_SLASHES));
NewChatComment::dispatch($stream, $res);
return $res;
}
@ -209,24 +258,86 @@ class LiveStreamController extends Controller
'message' => 'required'
]);
abort_if($request->user()->profile_id != $request->input('profile_id'), 403);
$uid = $request->user()->profile_id;
$pid = $request->input('profile_id');
$msg = $request->input('message');
$admin = $uid == $request->input('profile_id');
$owner = $uid == $msg['pid'];
abort_if(!$admin && !$owner, 403);
$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
$stream = LiveStream::whereProfileId($pid)->firstOrFail();
$payload = $request->input('message');
DeleteChatComment::dispatch($stream, $payload);
$payload = json_encode($payload, JSON_UNESCAPED_SLASHES);
LiveStreamService::deleteComment($stream->profile_id, $payload);
return;
}
public function banChatUser(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
abort_if(!$request->user(), 403);
$this->validate($request, [
'profile_id' => 'required|exists:profiles,id',
]);
abort_if($request->user()->profile_id == $request->input('profile_id'), 403);
$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
$pid = $request->input('profile_id');
BanUser::dispatch($stream, $pid);
return;
}
public function pinChatComment(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
abort_if(!$request->user(), 403);
$this->validate($request, [
'profile_id' => 'required|exists:profiles,id',
'message' => 'required'
]);
$uid = $request->user()->profile_id;
$pid = $request->input('profile_id');
$msg = $request->input('message');
abort_if($uid != $pid, 403);
$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
PinChatMessage::dispatch($stream, $msg);
return;
}
public function unpinChatComment(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
abort_if(!$request->user(), 403);
$this->validate($request, [
'profile_id' => 'required|exists:profiles,id',
'message' => 'required'
]);
$uid = $request->user()->profile_id;
$pid = $request->input('profile_id');
$msg = $request->input('message');
abort_if($uid != $pid, 403);
$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
UnpinChatMessage::dispatch($stream, $msg);
return;
}
public function getConfig(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
abort_if(!$request->user(), 403);
$res = [
'enabled' => config('livestreaming.enabled'),
'enabled' => (bool) config('livestreaming.enabled'),
'broadcast' => [
'sources' => config('livestreaming.broadcast.sources'),
'limits' => config('livestreaming.broadcast.limits')
@ -239,6 +350,7 @@ class LiveStreamController extends Controller
public function clientBroadcastPublish(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
abort_if($request->ip() != '127.0.0.1', 400);
$key = $request->input('name');
$name = $request->input('name');
@ -259,9 +371,12 @@ class LiveStreamController extends Controller
$stream = LiveStream::whereStreamId($key)->firstOrFail();
}
StreamStart::dispatch($stream->profile_id);
if($request->filled('name') && $token == false) {
$stream->live_at = now();
$stream->save();
return [];
} else {
abort(400);
@ -273,11 +388,11 @@ class LiveStreamController extends Controller
public function clientBroadcastFinish(Request $request)
{
abort_if(!config('livestreaming.enabled'), 400);
abort_if(!$request->filled('tcurl'), 400);
$url = $this->parseStreamUrl($request->input('tcurl'));
$name = $url['name'] ?? $request->input('name');
$stream = LiveStream::whereStreamId($name)->whereStreamKey($url['key'])->firstOrFail();
abort_if($request->ip() != '127.0.0.1', 400);
$name = $request->input('name');
$stream = LiveStream::whereStreamId($name)->firstOrFail();
StreamEnd::dispatch($stream->profile_id);
LiveStreamService::clearChat($stream->profile_id);
if(config('livestreaming.broadcast.delete_token_after_finished')) {
$stream->delete();

View file

@ -0,0 +1,75 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeApplicationServiceProvider;
class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// Telescope::night();
$this->hideSensitiveRequestDetails();
Telescope::filter(function (IncomingEntry $entry) {
if ($this->app->environment('local')) {
return true;
}
return $entry->isReportableException() ||
$entry->isFailedRequest() ||
$entry->isFailedJob() ||
$entry->isScheduledTask() ||
$entry->hasMonitoredTag();
});
}
/**
* Prevent sensitive request details from being logged by Telescope.
*
* @return void
*/
protected function hideSensitiveRequestDetails()
{
if ($this->app->environment('local')) {
return;
}
Telescope::hideRequestParameters(['_token']);
Telescope::hideRequestHeaders([
'cookie',
'x-csrf-token',
'x-xsrf-token',
]);
}
/**
* Register the Telescope gate.
*
* This gate determines who can access Telescope in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewTelescope', function ($user) {
if(!config('telescope.enabled')) {
return false;
}
return in_array($user->email, [
'danielsupernault@gmail.com',
'me@dansup.com'
]);
});
}
}

View file

@ -39,7 +39,7 @@ class LiveStreamService
return Redis::lrem($key, 0, $val);
}
public static function clearChat($id, $val)
public static function clearChat($id)
{
$key = self::CACHE_KEY . 'chat:' . $id;
return Redis::del($key);

View file

@ -53,7 +53,7 @@ class StatusStatelessTransformer extends Fractal\TransformerAbstract
'mentions' => StatusMentionService::get($status->id),
'pf_type' => $status->type ?? $status->setType(),
'reply_count' => (int) $status->reply_count,
'comments_disabled' => $status->comments_disabled ? true : false,
'comments_disabled' => (bool) $status->comments_disabled,
'thread' => false,
'replies' => [],
'parent' => [],

View file

@ -56,7 +56,7 @@ class StatusTransformer extends Fractal\TransformerAbstract
'mentions' => StatusMentionService::get($status->id),
'pf_type' => $status->type ?? $status->setType(),
'reply_count' => (int) $status->reply_count,
'comments_disabled' => $status->comments_disabled ? true : false,
'comments_disabled' => (bool) $status->comments_disabled,
'thread' => false,
'replies' => [],
'parent' => [],

View file

@ -14,6 +14,7 @@
"ext-mbstring": "*",
"ext-openssl": "*",
"bacon/bacon-qr-code": "^2.0.3",
"beyondcode/laravel-websockets": "^1.13",
"brick/math": "^0.9.3",
"buzz/laravel-h-captcha": "1.0.3",
"doctrine/dbal": "^2.7",
@ -45,6 +46,7 @@
"require-dev": {
"brianium/paratest": "^6.1",
"facade/ignition": "^2.3.6",
"laravel/telescope": "^4.9",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.0"

1380
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -154,10 +154,11 @@ return [
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\HorizonServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\TelescopeServiceProvider::class,
App\Providers\PassportServiceProvider::class,
],

View file

@ -37,14 +37,10 @@ return [
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'encrypted' => true,
'host' => env('APP_DOMAIN'),
'port' => 6001,
'scheme' => 'https',
'curl_options' => [
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
]
'encrypted' => env('PUSHER_APP_ENCRYPTED', false),
'host' => env('PUSHER_HOST', env('APP_DOMAIN')),
'port' => env('PUSHER_PORT', 443),
'scheme' => env('PUSHER_SCHEME', 'https')
],
],

133
config/telescope.php Normal file
View file

@ -0,0 +1,133 @@
<?php
use Laravel\Telescope\Watchers;
use Laravel\Telescope\Http\Middleware\Authorize;
return [
'path' => 'telescope',
/*
|--------------------------------------------------------------------------
| Telescope Storage Driver
|--------------------------------------------------------------------------
|
| This configuration options determines the storage driver that will
| be used to store Telescope's data. In addition, you may set any
| custom options as needed by the particular driver you choose.
|
*/
'driver' => env('TELESCOPE_DRIVER', 'database'),
'storage' => [
'database' => [
'connection' => env('DB_CONNECTION', 'mysql'),
],
],
/*
|--------------------------------------------------------------------------
| Telescope Master Switch
|--------------------------------------------------------------------------
|
| This option may be used to disable all Telescope watchers regardless
| of their individual configuration, which simply provides a single
| and convenient way to enable or disable Telescope data storage.
|
*/
'enabled' => env('TELESCOPE_ENABLED', false),
/*
|--------------------------------------------------------------------------
| Telescope Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will be assigned to every Telescope route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => [
'web',
Authorize::class,
],
/*
|--------------------------------------------------------------------------
| Ignored Paths & Commands
|--------------------------------------------------------------------------
|
| The following array lists the URI paths and Artisan commands that will
| not be watched by Telescope. In addition to this list, some Laravel
| commands, like migrations and queue commands, are always ignored.
|
*/
'ignore_paths' => [
'js*',
'i*'
],
'ignore_commands' => [
//
],
/*
|--------------------------------------------------------------------------
| Telescope Watchers
|--------------------------------------------------------------------------
|
| The following array lists the "watchers" that will be registered with
| Telescope. The watchers gather the application's profile data when
| a request or task is executed. Feel free to customize this list.
|
*/
'watchers' => [
Watchers\CacheWatcher::class => env('TELESCOPE_CACHE_WATCHER', true),
Watchers\CommandWatcher::class => [
'enabled' => env('TELESCOPE_COMMAND_WATCHER', true),
'ignore' => [],
],
Watchers\DumpWatcher::class => env('TELESCOPE_DUMP_WATCHER', true),
Watchers\EventWatcher::class => env('TELESCOPE_EVENT_WATCHER', true),
Watchers\ExceptionWatcher::class => env('TELESCOPE_EXCEPTION_WATCHER', true),
Watchers\JobWatcher::class => env('TELESCOPE_JOB_WATCHER', true),
Watchers\LogWatcher::class => env('TELESCOPE_LOG_WATCHER', true),
Watchers\MailWatcher::class => env('TELESCOPE_MAIL_WATCHER', true),
Watchers\ClientRequestWatcher::class =>true,
Watchers\ModelWatcher::class => [
'enabled' => env('TELESCOPE_MODEL_WATCHER', true),
'events' => ['eloquent.*'],
],
Watchers\NotificationWatcher::class => env('TELESCOPE_NOTIFICATION_WATCHER', true),
Watchers\QueryWatcher::class => [
'enabled' => env('TELESCOPE_QUERY_WATCHER', true),
'ignore_packages' => true,
'slow' => 100,
],
Watchers\RedisWatcher::class => env('TELESCOPE_REDIS_WATCHER', true),
Watchers\RequestWatcher::class => [
'enabled' => env('TELESCOPE_REQUEST_WATCHER', true),
'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64),
],
Watchers\GateWatcher::class => [
'enabled' => env('TELESCOPE_GATE_WATCHER', true),
'ignore_abilities' => [],
'ignore_packages' => true,
],
Watchers\ScheduleWatcher::class => env('TELESCOPE_SCHEDULE_WATCHER', true),
],
];

View file

@ -1,125 +1,299 @@
<?php
use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize;
return [
/*
* This package comes with multi tenancy out of the box. Here you can
* configure the different apps that can use the webSockets server.
*
* Optionally you can disable client events so clients cannot send
* messages to each other via the webSockets.
*/
|--------------------------------------------------------------------------
| Dashboard Settings
|--------------------------------------------------------------------------
|
| You can configure the dashboard settings from here.
|
*/
'dashboard' => [
'port' => env('LARAVEL_WEBSOCKETS_PORT', 6001),
'domain' => env('LARAVEL_WEBSOCKETS_DOMAIN'),
'path' => env('LARAVEL_WEBSOCKETS_PATH', 'laravel-websockets'),
'middleware' => [
'web',
\BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize::class,
],
],
'managers' => [
/*
|--------------------------------------------------------------------------
| Application Manager
|--------------------------------------------------------------------------
|
| An Application manager determines how your websocket server allows
| the use of the TCP protocol based on, for example, a list of allowed
| applications.
| By default, it uses the defined array in the config file, but you can
| anytime implement the same interface as the class and add your own
| custom method to retrieve the apps.
|
*/
'app' => \BeyondCode\LaravelWebSockets\Apps\ConfigAppManager::class,
],
/*
|--------------------------------------------------------------------------
| Applications Repository
|--------------------------------------------------------------------------
|
| By default, the only allowed app is the one you define with
| your PUSHER_* variables from .env.
| You can configure to use multiple apps if you need to, or use
| a custom App Manager that will handle the apps from a database, per se.
|
| You can apply multiple settings, like the maximum capacity, enable
| client-to-client messages or statistics.
|
*/
'apps' => [
[
'id' => env('PUSHER_APP_ID'),
'name' => env('APP_NAME'),
'host' => env('PUSHER_APP_HOST'),
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'enable_client_messages' => env('WSS_CM', false),
'enable_statistics' => env('WSS_STATS', false),
'path' => env('PUSHER_APP_PATH'),
'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => false,
'allowed_origins' => [
// env('LARAVEL_WEBSOCKETS_DOMAIN'),
],
],
],
/*
* This class is responsible for finding the apps. The default provider
* will use the apps defined in this config file.
*
* You can create a custom provider by implementing the
* `AppProvider` interface.
*/
'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,
|--------------------------------------------------------------------------
| Broadcasting Replication PubSub
|--------------------------------------------------------------------------
|
| You can enable replication to publish and subscribe to
| messages across the driver.
|
| By default, it is set to 'local', but you can configure it to use drivers
| like Redis to ensure connection between multiple instances of
| WebSocket servers. Just set the driver to 'redis' to enable the PubSub using Redis.
|
*/
/*
* This array contains the hosts of which you want to allow incoming requests.
* Leave this empty if you want to accept requests from all hosts.
*/
'allowed_origins' => [
//
],
'replication' => [
/*
* The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
*/
'max_request_size_in_kb' => 250,
'mode' => env('WEBSOCKETS_REPLICATION_MODE', 'local'),
/*
* This path will be used to register the necessary routes for the package.
*/
'path' => 'pxws',
'modes' => [
/*
|--------------------------------------------------------------------------
| Local Replication
|--------------------------------------------------------------------------
|
| Local replication is actually a null replicator, meaning that it
| is the default behaviour of storing the connections into an array.
|
*/
'local' => [
/*
|--------------------------------------------------------------------------
| Channel Manager
|--------------------------------------------------------------------------
|
| The channel manager is responsible for storing, tracking and retrieving
| the channels as long as their members and connections.
|
*/
'channel_manager' => \BeyondCode\LaravelWebSockets\ChannelManagers\LocalChannelManager::class,
/*
|--------------------------------------------------------------------------
| Statistics Collector
|--------------------------------------------------------------------------
|
| The Statistics Collector will, by default, handle the incoming statistics,
| storing them until they will become dumped into another database, usually
| a MySQL database or a time-series database.
|
*/
'collector' => \BeyondCode\LaravelWebSockets\Statistics\Collectors\MemoryCollector::class,
],
'redis' => [
'connection' => env('WEBSOCKETS_REDIS_REPLICATION_CONNECTION', 'default'),
/*
|--------------------------------------------------------------------------
| Channel Manager
|--------------------------------------------------------------------------
|
| The channel manager is responsible for storing, tracking and retrieving
| the channels as long as their members and connections.
|
*/
'channel_manager' => \BeyondCode\LaravelWebSockets\ChannelManagers\RedisChannelManager::class,
/*
|--------------------------------------------------------------------------
| Statistics Collector
|--------------------------------------------------------------------------
|
| The Statistics Collector will, by default, handle the incoming statistics,
| storing them until they will become dumped into another database, usually
| a MySQL database or a time-series database.
|
*/
'collector' => \BeyondCode\LaravelWebSockets\Statistics\Collectors\RedisCollector::class,
],
],
/*
* Dashboard Routes Middleware
*
* These middleware will be assigned to every dashboard route, giving you
* the chance to add your own middleware to this list or change any of
* the existing middleware. Or, you can simply stick with this list.
*/
'middleware' => [
'web',
Authorize::class,
],
'statistics' => [
/*
* This model will be used to store the statistics of the WebSocketsServer.
* The only requirement is that the model should extend
* `WebSocketsStatisticsEntry` provided by this package.
*/
'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,
/*
* Here you can specify the interval in seconds at which statistics should be logged.
*/
|--------------------------------------------------------------------------
| Statistics Store
|--------------------------------------------------------------------------
|
| The Statistics Store is the place where all the temporary stats will
| be dumped. This is a much reliable store and will be used to display
| graphs or handle it later on your app.
|
*/
'store' => \BeyondCode\LaravelWebSockets\Statistics\Stores\DatabaseStore::class,
/*
|--------------------------------------------------------------------------
| Statistics Interval Period
|--------------------------------------------------------------------------
|
| Here you can specify the interval in seconds at which
| statistics should be logged.
|
*/
'interval_in_seconds' => 60,
/*
* When the clean-command is executed, all recorded statistics older than
* the number of days specified here will be deleted.
*/
|--------------------------------------------------------------------------
| Statistics Deletion Period
|--------------------------------------------------------------------------
|
| When the clean-command is executed, all recorded statistics older than
| the number of days specified here will be deleted.
|
*/
'delete_statistics_older_than_days' => 60,
/*
* Use an DNS resolver to make the requests to the statistics logger
* default is to resolve everything to 127.0.0.1.
*/
'perform_dns_lookup' => false,
],
/*
* Define the optional SSL context for your WebSocket connections.
* You can see all available options at: http://php.net/manual/en/context.ssl.php
*/
|--------------------------------------------------------------------------
| Maximum Request Size
|--------------------------------------------------------------------------
|
| The maximum request size in kilobytes that is allowed for
| an incoming WebSocket request.
|
*/
'max_request_size_in_kb' => 250,
/*
|--------------------------------------------------------------------------
| SSL Configuration
|--------------------------------------------------------------------------
|
| By default, the configuration allows only on HTTP. For SSL, you need
| to set up the the certificate, the key, and optionally, the passphrase
| for the private key.
| You will need to restart the server for the settings to take place.
|
*/
'ssl' => [
/*
* Path to local certificate file on filesystem. It must be a PEM encoded file which
* contains your certificate and private key. It can optionally contain the
* certificate chain of issuers. The private key also may be contained
* in a separate file specified by local_pk.
*/
'local_cert' => env('WSS_LOCAL_CERT', null),
/*
* Path to local private key file on filesystem in case of separate files for
* certificate (local_cert) and private key.
*/
'local_pk' => env('WSS_LOCAL_PK', null),
'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),
/*
* Passphrase for your local_cert file.
*/
'passphrase' => env('WSS_PASSPHRASE', null),
'capath' => env('LARAVEL_WEBSOCKETS_SSL_CA', null),
'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),
'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),
'verify_peer' => env('APP_ENV') === 'production',
'allow_self_signed' => env('APP_ENV') !== 'production',
'verify_peer' => env('WSS_VERIFY_PEER', false),
],
/*
* Channel Manager
* This class handles how channel persistence is handled.
* By default, persistence is stored in an array by the running webserver.
* The only requirement is that the class should implement
* `ChannelManager` interface provided by this package.
*/
'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,
|--------------------------------------------------------------------------
| Route Handlers
|--------------------------------------------------------------------------
|
| Here you can specify the route handlers that will take over
| the incoming/outgoing websocket connections. You can extend the
| original class and implement your own logic, alongside
| with the existing logic.
|
*/
'handlers' => [
'websocket' => \BeyondCode\LaravelWebSockets\Server\WebSocketHandler::class,
'health' => \BeyondCode\LaravelWebSockets\Server\HealthHandler::class,
'trigger_event' => \BeyondCode\LaravelWebSockets\API\TriggerEvent::class,
'fetch_channels' => \BeyondCode\LaravelWebSockets\API\FetchChannels::class,
'fetch_channel' => \BeyondCode\LaravelWebSockets\API\FetchChannel::class,
'fetch_users' => \BeyondCode\LaravelWebSockets\API\FetchUsers::class,
],
/*
|--------------------------------------------------------------------------
| Promise Resolver
|--------------------------------------------------------------------------
|
| The promise resolver is a class that takes a input value and is
| able to make sure the PHP code runs async by using ->then(). You can
| use your own Promise Resolver. This is usually changed when you want to
| intercept values by the promises throughout the app, like in testing
| to switch from async to sync.
|
*/
'promise_resolver' => \React\Promise\FulfilledPromise::class,
];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/installer.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
/*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */

1
public/js/live-player.js vendored Normal file

File diff suppressed because one or more lines are too long

2
public/js/spa.js vendored

File diff suppressed because one or more lines are too long

2
public/js/vendor.js vendored

File diff suppressed because one or more lines are too long

View file

@ -48,6 +48,14 @@
* Released under the MIT license
*/
/*!
* Pusher JavaScript Library v7.1.1-beta
* https://pusher.com/
*
* Copyright 2020, Pusher
* Released under the MIT licence.
*/
/*!
* Scroll Lock v3.1.3
* https://github.com/MohammadYounes/jquery-scrollLock
@ -155,8 +163,686 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/*! ../controller/level-helper */
/*! ../crypt/decrypter */
/*! ../demux/aacdemuxer */
/*! ../demux/chunk-cache */
/*! ../demux/id3 */
/*! ../demux/mp3demuxer */
/*! ../demux/mp4demuxer */
/*! ../demux/transmuxer */
/*! ../demux/transmuxer-interface */
/*! ../demux/transmuxer-worker.ts */
/*! ../demux/tsdemuxer */
/*! ../errors */
/*! ../events */
/*! ../is-supported */
/*! ../loader/fragment */
/*! ../loader/fragment-loader */
/*! ../loader/load-stats */
/*! ../remux/mp4-remuxer */
/*! ../remux/passthrough-remuxer */
/*! ../task-loop */
/*! ../types/cmcd */
/*! ../types/level */
/*! ../types/loader */
/*! ../types/transmuxer */
/*! ../utils/attr-list */
/*! ../utils/binary-search */
/*! ../utils/buffer-helper */
/*! ../utils/cea-608-parser */
/*! ../utils/codecs */
/*! ../utils/discontinuities */
/*! ../utils/ewma */
/*! ../utils/ewma-bandwidth-estimator */
/*! ../utils/imsc1-ttml-parser */
/*! ../utils/logger */
/*! ../utils/mediakeys-helper */
/*! ../utils/mediasource-helper */
/*! ../utils/mp4-tools */
/*! ../utils/output-filter */
/*! ../utils/texttrack-utils */
/*! ../utils/time-ranges */
/*! ../utils/timescale-conversion */
/*! ../utils/typed-array */
/*! ../utils/webvtt-parser */
/*! ./aac-helper */
/*! ./adts */
/*! ./aes-crypto */
/*! ./aes-decryptor */
/*! ./base-audio-demuxer */
/*! ./base-playlist-controller */
/*! ./base-stream-controller */
/*! ./buffer-operation-queue */
/*! ./chunk-cache */
/*! ./config */
/*! ./controller/abr-controller */
/*! ./controller/audio-stream-controller */
/*! ./controller/audio-track-controller */
/*! ./controller/buffer-controller */
/*! ./controller/cap-level-controller */
/*! ./controller/cmcd-controller */
/*! ./controller/eme-controller */
/*! ./controller/fps-controller */
/*! ./controller/fragment-tracker */
/*! ./controller/id3-track-controller */
/*! ./controller/latency-controller */
/*! ./controller/level-controller */
/*! ./controller/stream-controller */
/*! ./controller/subtitle-stream-controller */
/*! ./controller/subtitle-track-controller */
/*! ./controller/timeline-controller */
/*! ./dummy-demuxed-track */
/*! ./errors */
/*! ./events */
/*! ./exp-golomb */
/*! ./fast-aes-key */
/*! ./fragment */
/*! ./fragment-finders */
/*! ./fragment-tracker */
/*! ./gap-controller */
/*! ./id3 */
/*! ./is-supported */
/*! ./level-details */
/*! ./level-helper */
/*! ./level-key */
/*! ./load-stats */
/*! ./loader/key-loader */
/*! ./loader/playlist-loader */
/*! ./logger */
/*! ./m3u8-parser */
/*! ./mp4-generator */
/*! ./mp4-tools */
/*! ./mpegaudio */
/*! ./sample-aes */
/*! ./src/polyfills/number */
/*! ./texttrack-utils */
/*! ./timescale-conversion */
/*! ./tsdemuxer */
/*! ./typed-array */
/*! ./utils/cues */
/*! ./utils/fetch-loader */
/*! ./utils/logger */
/*! ./utils/mediakeys-helper */
/*! ./utils/mediasource-helper */
/*! ./utils/xhr-loader */
/*! ./vttcue */
/*! ./vttparser */
/*! ./webvtt-parser */
/*! eventemitter3 */
/*! exports provided: AttrList */
/*! exports provided: BufferHelper */
/*! exports provided: CMCDVersion, CMCDObjectType, CMCDStreamingFormat, CMCDStreamType */
/*! exports provided: ChunkMetadata */
/*! exports provided: ElementaryStreamTypes, BaseSegment, Fragment, Part */
/*! exports provided: ErrorTypes, ErrorDetails */
/*! exports provided: Events */
/*! exports provided: FragmentState, FragmentTracker */
/*! exports provided: HlsSkip, getSkipValue, HlsUrlParameters, Level */
/*! exports provided: IMSC1_CODEC, parseIMSC1 */
/*! exports provided: KeySystems, requestMediaKeySystemAccess */
/*! exports provided: LevelDetails */
/*! exports provided: LevelKey */
/*! exports provided: LoadStats */
/*! exports provided: PlaylistContextType, PlaylistLevelType */
/*! exports provided: Row, CaptionScreen, default */
/*! exports provided: STALL_MINIMUM_DURATION_MS, MAX_START_GAP_JUMP, SKIP_BUFFER_HOLE_STEP_SECONDS, SKIP_BUFFER_RANGE_START, default */
/*! exports provided: State, default */
/*! exports provided: SubtitleStreamController */
/*! exports provided: TimelineController */
/*! exports provided: addGroupId, assignTrackIdsByGroup, updatePTS, updateFragPTSDTS, mergeDetails, mapPartIntersection, mapFragmentIntersection, adjustSliding, addSliding, computeReloadInterval, getFragmentWithSN, getPartWith */
/*! exports provided: appendFrame, parseHeader, isHeaderPattern, isHeader, canParse, probe */
/*! exports provided: bin2str, readUint16, readUint32, writeUint32, findBox, parseSegmentIndex, parseInitSegment, getStartDTS, getDuration, computeRawDurationFromSamples, offsetStartDTS, segmentValidRange, appendUint8Array */
/*! exports provided: default */
/*! exports provided: default, LoadError */
/*! exports provided: default, isPromise, TransmuxConfig, TransmuxState */
/*! exports provided: default, normalizePts */
/*! exports provided: discardEPB, default */
/*! exports provided: dummyTrack */
/*! exports provided: enableLogs, logger */
/*! exports provided: fetchSupported, default */
/*! exports provided: findFirstFragWithCC, shouldAlignOnDiscontinuities, findDiscontinuousReferenceFrag, adjustSlidingStart, alignStream, alignPDT, alignFragmentByPDTDelta, alignMediaPlaylistByPDT */
/*! exports provided: findFragmentByPDT, findFragmentByPTS, fragmentWithinToleranceTest, pdtWithinToleranceTest, findFragWithCC */
/*! exports provided: generateCueId, parseWebVTT */
/*! exports provided: getAudioConfig, isHeaderPattern, getHeaderLength, getFullFrameLength, canGetFrameLength, isHeader, canParse, probe, initTrackConfig, getFrameDuration, parseFrameHeader, appendFrame */
/*! exports provided: getMediaSource */
/*! exports provided: hlsDefaultConfig, mergeConfig, enableStreamingMode */
/*! exports provided: initPTSFn, default */
/*! exports provided: isCodecType, isCodecSupportedInMp4 */
/*! exports provided: isFiniteNumber, MAX_SAFE_INTEGER */
/*! exports provided: isHeader, isFooter, getID3Data, canParse, getTimeStamp, isTimeStampFrame, getID3Frames, decodeFrame, utf8ArrayToStr, testables */
/*! exports provided: isSupported, changeTypeSupported */
/*! exports provided: parseTimeStamp, fixLineBreaks, VTTParser */
/*! exports provided: removePadding, default */
/*! exports provided: sendAddTrackEvent, addCueToTrack, clearCurrentCues, removeCuesInRange, getCuesInRange */
/*! exports provided: sliceUint8 */
/*! exports provided: toTimescaleFromBase, toTimescaleFromScale, toMsFromMpegTsClock, toMpegTsClockFromTimescale */
/*! https://mths.be/punycode v1.4.1 by @mathias */
/*! no static exports found */
/*! url-toolkit */
/*! webworkify-webpack */
/*!********************!*\
!*** ./src/hls.ts ***!
\********************/
/*!***********************!*\
!*** ./src/config.ts ***!
\***********************/
/*!***********************!*\
!*** ./src/errors.ts ***!
\***********************/
/*!***********************!*\
!*** ./src/events.ts ***!
\***********************/
/*!**************************!*\
!*** ./src/demux/id3.ts ***!
\**************************/
/*!**************************!*\
!*** ./src/task-loop.ts ***!
\**************************/
/*!***************************!*\
!*** ./src/demux/adts.ts ***!
\***************************/
/*!***************************!*\
!*** ./src/types/cmcd.ts ***!
\***************************/
/*!***************************!*\
!*** ./src/utils/cues.ts ***!
\***************************/
/*!***************************!*\
!*** ./src/utils/ewma.ts ***!
\***************************/
/*!****************************!*\
!*** ./src/types/level.ts ***!
\****************************/
/*!*****************************!*\
!*** ./src/is-supported.ts ***!
\*****************************/
/*!*****************************!*\
!*** ./src/types/loader.ts ***!
\*****************************/
/*!*****************************!*\
!*** ./src/utils/codecs.ts ***!
\*****************************/
/*!*****************************!*\
!*** ./src/utils/logger.ts ***!
\*****************************/
/*!*****************************!*\
!*** ./src/utils/vttcue.ts ***!
\*****************************/
/*!********************************!*\
!*** ./src/crypt/decrypter.ts ***!
\********************************/
/*!********************************!*\
!*** ./src/demux/mpegaudio.ts ***!
\********************************/
/*!********************************!*\
!*** ./src/demux/tsdemuxer.ts ***!
\********************************/
/*!********************************!*\
!*** ./src/loader/fragment.ts ***!
\********************************/
/*!********************************!*\
!*** ./src/utils/attr-list.ts ***!
\********************************/
/*!********************************!*\
!*** ./src/utils/mp4-tools.ts ***!
\********************************/
/*!********************************!*\
!*** ./src/utils/vttparser.ts ***!
\********************************/
/*!*********************************!*\
!*** ./src/crypt/aes-crypto.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/demux/aacdemuxer.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/demux/exp-golomb.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/demux/mp3demuxer.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/demux/mp4demuxer.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/demux/sample-aes.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/demux/transmuxer.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/loader/level-key.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/polyfills/number.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/remux/aac-helper.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/types/transmuxer.ts ***!
\*********************************/
/*!*********************************!*\
!*** ./src/utils/xhr-loader.ts ***!
\*********************************/
/*!**********************************!*\
!*** ./src/demux/chunk-cache.ts ***!
\**********************************/
/*!**********************************!*\
!*** ./src/loader/key-loader.ts ***!
\**********************************/
/*!**********************************!*\
!*** ./src/loader/load-stats.ts ***!
\**********************************/
/*!**********************************!*\
!*** ./src/remux/mp4-remuxer.ts ***!
\**********************************/
/*!**********************************!*\
!*** ./src/utils/time-ranges.ts ***!
\**********************************/
/*!**********************************!*\
!*** ./src/utils/typed-array.ts ***!
\**********************************/
/*!***********************************!*\
!*** ./src/crypt/fast-aes-key.ts ***!
\***********************************/
/*!***********************************!*\
!*** ./src/loader/m3u8-parser.ts ***!
\***********************************/
/*!***********************************!*\
!*** ./src/utils/fetch-loader.ts ***!
\***********************************/
/*!************************************!*\
!*** ./src/crypt/aes-decryptor.ts ***!
\************************************/
/*!************************************!*\
!*** ./src/remux/mp4-generator.ts ***!
\************************************/
/*!************************************!*\
!*** ./src/utils/binary-search.ts ***!
\************************************/
/*!************************************!*\
!*** ./src/utils/buffer-helper.ts ***!
\************************************/
/*!************************************!*\
!*** ./src/utils/output-filter.ts ***!
\************************************/
/*!************************************!*\
!*** ./src/utils/webvtt-parser.ts ***!
\************************************/
/*!*************************************!*\
!*** ./src/loader/level-details.ts ***!
\*************************************/
/*!*************************************!*\
!*** ./src/utils/cea-608-parser.ts ***!
\*************************************/
/*!**************************************!*\
!*** ./src/utils/discontinuities.ts ***!
\**************************************/
/*!**************************************!*\
!*** ./src/utils/texttrack-utils.ts ***!
\**************************************/
/*!***************************************!*\
!*** ./src/loader/fragment-loader.ts ***!
\***************************************/
/*!***************************************!*\
!*** ./src/loader/playlist-loader.ts ***!
\***************************************/
/*!***************************************!*\
!*** ./src/utils/mediakeys-helper.ts ***!
\***************************************/
/*!****************************************!*\
!*** ./src/controller/level-helper.ts ***!
\****************************************/
/*!****************************************!*\
!*** ./src/demux/transmuxer-worker.ts ***!
\****************************************/
/*!****************************************!*\
!*** ./src/utils/imsc1-ttml-parser.ts ***!
\****************************************/
/*!*****************************************!*\
!*** ./src/demux/base-audio-demuxer.ts ***!
\*****************************************/
/*!*****************************************!*\
!*** ./src/utils/mediasource-helper.ts ***!
\*****************************************/
/*!******************************************!*\
!*** ./src/controller/abr-controller.ts ***!
\******************************************/
/*!******************************************!*\
!*** ./src/controller/eme-controller.ts ***!
\******************************************/
/*!******************************************!*\
!*** ./src/controller/fps-controller.ts ***!
\******************************************/
/*!******************************************!*\
!*** ./src/controller/gap-controller.ts ***!
\******************************************/
/*!******************************************!*\
!*** ./src/demux/dummy-demuxed-track.ts ***!
\******************************************/
/*!******************************************!*\
!*** ./src/remux/passthrough-remuxer.ts ***!
\******************************************/
/*!*******************************************!*\
!*** ./src/controller/cmcd-controller.ts ***!
\*******************************************/
/*!*******************************************!*\
!*** ./src/demux/transmuxer-interface.ts ***!
\*******************************************/
/*!*******************************************!*\
!*** ./src/utils/timescale-conversion.ts ***!
\*******************************************/
/*!********************************************!*\
!*** ./src/controller/fragment-finders.ts ***!
\********************************************/
/*!********************************************!*\
!*** ./src/controller/fragment-tracker.ts ***!
\********************************************/
/*!********************************************!*\
!*** ./src/controller/level-controller.ts ***!
\********************************************/
/*!*********************************************!*\
!*** ./node_modules/eventemitter3/index.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/controller/buffer-controller.ts ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/controller/stream-controller.ts ***!
\*********************************************/
/*!**********************************************!*\
!*** ./src/controller/latency-controller.ts ***!
\**********************************************/
/*!***********************************************!*\
!*** ./src/controller/timeline-controller.ts ***!
\***********************************************/
/*!***********************************************!*\
!*** ./src/utils/ewma-bandwidth-estimator.ts ***!
\***********************************************/
/*!************************************************!*\
!*** ./src/controller/cap-level-controller.ts ***!
\************************************************/
/*!************************************************!*\
!*** ./src/controller/id3-track-controller.ts ***!
\************************************************/
/*!**************************************************!*\
!*** ./node_modules/webworkify-webpack/index.js ***!
\**************************************************/
/*!**************************************************!*\
!*** ./src/controller/audio-track-controller.ts ***!
\**************************************************/
/*!**************************************************!*\
!*** ./src/controller/base-stream-controller.ts ***!
\**************************************************/
/*!**************************************************!*\
!*** ./src/controller/buffer-operation-queue.ts ***!
\**************************************************/
/*!***************************************************!*\
!*** ./src/controller/audio-stream-controller.ts ***!
\***************************************************/
/*!****************************************************!*\
!*** ./src/controller/base-playlist-controller.ts ***!
\****************************************************/
/*!*****************************************************!*\
!*** ./node_modules/url-toolkit/src/url-toolkit.js ***!
\*****************************************************/
/*!*****************************************************!*\
!*** ./src/controller/subtitle-track-controller.ts ***!
\*****************************************************/
/*!******************************************************!*\
!*** ./src/controller/subtitle-stream-controller.ts ***!
\******************************************************/
/**
* vue-class-component v7.2.3
* (c) 2015-present Evan You

View file

@ -11,18 +11,20 @@
"/js/search.js": "/js/search.js?id=4bb81cba317cf1ad35f2c98dce78fd9d",
"/js/developers.js": "/js/developers.js?id=dd22facb8cf2746992404468a9373ac5",
"/js/hashtag.js": "/js/hashtag.js?id=5fe9d15d07a227f91eabd874cbb9fea2",
"/js/collectioncompose.js": "/js/collectioncompose.js?id=d7acc36deab8f6a1f67361f05f88e2d8",
"/js/collections.js": "/js/collections.js?id=12533dbfbe7a0bf7ef20fc57fb34e19a",
"/js/collectioncompose.js": "/js/collectioncompose.js?id=63476570639632c50306e8562f10e7c2",
"/js/collections.js": "/js/collections.js?id=638d13634a5f9b6501afcc93b5af08b7",
"/js/profile-directory.js": "/js/profile-directory.js?id=04ec970031e6bf15de5ade019147d53e",
"/js/story-compose.js": "/js/story-compose.js?id=afe8f35cf52d92ac48ee68a9916d218d",
"/js/direct.js": "/js/direct.js?id=29127c125979e275afa50b47d692c892",
"/js/admin.js": "/js/admin.js?id=fd88b96423314b41cc763a0714554a04",
"/js/rempro.js": "/js/rempro.js?id=03810f85fc35fb2238c518a076fe6b34",
"/js/rempos.js": "/js/rempos.js?id=390eecb73b0e650f058df325985db938",
"/js/spa.js": "/js/spa.js?id=b5ca9478a06aa0d2261f9777c481756b",
"/js/live-player.js": "/js/live-player.js?id=dd54a729ca39d7eebff1bdb6ef53e236",
"/js/spa.js": "/js/spa.js?id=c6c3766979c5b2c2aa6d872cf5ae8bda",
"/js/stories.js": "/js/stories.js?id=814a25875cac8987d85c801dcb453114",
"/js/installer.js": "/js/installer.js?id=d7b03f6c0bb707bec8ff9f81d328ac4a",
"/js/manifest.js": "/js/manifest.js?id=4e6dd9cb251d9698bfccb781db000cca",
"/js/home-ojtjadoml.js": "/js/home-ojtjadoml.js?id=17d512a55e9a924b1aaf7c600ef7d3a5",
"/js/home-ojtjadoml.js": "/js/home-ojtjadoml.js?id=aee71e08d2bee651799f655b006bea7b",
"/js/compose-ojtjadoml.js": "/js/compose-ojtjadoml.js?id=8c94338835b536bb064bc14247e16a2f",
"/js/post-ojtjadoml.js": "/js/post-ojtjadoml.js?id=0d2c0781c29fc6e344e915fb15a11ccf",
"/js/profile-ojtjadoml.js": "/js/profile-ojtjadoml.js?id=63e32708677900b9582d6617b1903d8a",
@ -41,5 +43,5 @@
"/css/spa.css": "/css/spa.css?id=4c78f163c6ad4e0f25ced75c7dd624b6",
"/css/admin.css": "/css/admin.css?id=c370da65565066b3fbcf2808bd0a4468",
"/css/landing.css": "/css/landing.css?id=1481d8b409a3e114d32a857db0bef4fd",
"/js/vendor.js": "/js/vendor.js?id=467b36b2c099089a7639448443158ceb"
"/js/vendor.js": "/js/vendor.js?id=4b8baeff156b0f280596a985b98255bc"
}

4
resources/assets/js/live-player.js vendored Normal file
View file

@ -0,0 +1,4 @@
Vue.component(
'live-player',
require('./../components/LivePlayer.vue').default
);

View file

@ -58,15 +58,15 @@
</li>
<li>
<strong><span class="badge badge-primary">ACTIVITYPUB</span> instance actor created: </strong>
<span>{{ \App\Models\InstanceActor::count() ? '✅' : '❌' }}</span>
<span>{{ \App\Models\InstanceActor::count() ? '✅ true' : '❌ false' }}</span>
</li>
<li>
<strong><span class="badge badge-primary">ACTIVITYPUB</span> instance actor cached: </strong>
<span>{{ Cache::get(\App\Models\InstanceActor::PROFILE_KEY) ? '✅' : '❌' }}</span>
<span>{{ Cache::get(\App\Models\InstanceActor::PROFILE_KEY) ? '✅ true' : '❌ false' }}</span>
</li>
<li>
<strong><span class="badge badge-primary">OAUTH</span> enabled: </strong>
<span>{{ config_cache('pixelfed.oauth_enabled') ? '✅' : '❌' }}</span>
<span>{{ config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }}</span>
</li>
<li>
<strong><span class="badge badge-primary">OAUTH</span> token_expiration</strong>
@ -74,11 +74,11 @@
</li>
<li>
<strong><span class="badge badge-primary">OAUTH</span> public key exists: </strong>
<span>{{ file_exists(storage_path('oauth-public.key')) ? '✅' : '❌' }}</span>
<span>{{ file_exists(storage_path('oauth-public.key')) ? '✅ true' : '❌ false' }}</span>
</li>
<li>
<strong><span class="badge badge-primary">OAUTH</span> private key exists: </strong>
<span>{{ file_exists(storage_path('oauth-private.key')) ? '✅' : '❌' }}</span>
<span>{{ file_exists(storage_path('oauth-private.key')) ? '✅ true' : '❌ false' }}</span>
</li>
<hr>
@ -153,11 +153,43 @@
<strong><span class="badge badge-primary">PHP INI</span> max_input_time:</strong>
<span>{{ ini_get('max_input_time') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI</span> file_uploads:</strong>
<span>{{ ini_get('file_uploads') ? '✅' : '❌' }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI</span> file_uploads (On):</strong>
<span>{{ ini_get('file_uploads') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> allow_url_fopen (true):</strong>
<span>{{ ini_get('allow_url_fopen') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> allow_url_include (false):</strong>
<span>{{ ini_get('allow_url_include') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> expose_php (false):</strong>
<span>{{ ini_get('expose_php') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> display_errors (false):</strong>
<span>{{ ini_get('display_errors') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> display_startup_errors (false):</strong>
<span>{{ ini_get('display_startup_errors') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> log_errors (true):</strong>
<span>{{ ini_get('log_errors') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> ignore_repeated_errors (false):</strong>
<span>{{ ini_get('ignore_repeated_errors') }}</span>
</li>
<li>
<strong><span class="badge badge-primary">PHP INI - Security</span> disable_functions:</strong>
<span>{{ ini_get('disable_functions') }}</span>
</li>
<hr>
<p class="font-weight-bold text-muted">
@ -328,6 +360,11 @@
<td><strong>PF_NETWORK_TIMELINE</strong></td>
<td><span>{{config_cache('federation.network_timeline') ? '✅ true' : '❌ false' }}</span></td>
</tr>
<tr>
<td><span class="badge badge-primary">FEDERATION</span></td>
<td><strong>PF_NETWORK_TIMELINE_DAYS_FALLOFF</strong></td>
<td><span>{{config('federation.network_timeline_days_falloff') }}</span></td>
</tr>
<tr>
<td><span class="badge badge-primary">FEDERATION</span></td>
<td><strong>CUSTOM_EMOJI</strong></td>
@ -453,6 +490,23 @@
<td><strong>INSTANCE_PUBLIC_LOCAL_TIMELINE</strong></td>
<td><span>{{config_cache('instance.timeline.local.is_public') ? '✅ true' : '❌ false' }}</span></td>
</tr>
<tr>
<td><span class="badge badge-primary">INSTANCE</span></td>
<td><strong>INSTANCE_NETWORK_TIMELINE_CACHED</strong></td>
<td><span>{{config('instance.timeline.network.cached') }}</span></td>
</tr>
<tr>
<td><span class="badge badge-primary">INSTANCE</span></td>
<td><strong>INSTANCE_NETWORK_TIMELINE_CACHE_DROPOFF</strong></td>
<td><span>{{config('instance.timeline.network.cache_dropoff') }}</span></td>
</tr>
<tr>
<td><span class="badge badge-primary">INSTANCE</span></td>
<td><strong>INSTANCE_NETWORK_TIMELINE_CACHE_MAX_HOUR_INGEST</strong></td>
<td><span>{{config('instance.timeline.network.max_hours_old') }}</span></td>
</tr>
<tr>
<td><span class="badge badge-primary">INSTANCE</span></td>
<td><strong>PAGE_404_HEADER</strong></td>

View file

@ -0,0 +1,33 @@
@extends('layouts.blank')
@section('content')
<div class="force-dark-mode">
<live-player id="{{ $id }}"></live-player>
</div>
@endsection
@push('scripts')
<script type="text/javascript" src="/js/live-player.js?v={{ time() }}"></script>
<script type="text/javascript">App.boot();</script>
@endpush
@push('meta')
<script type="text/javascript">
window._pushr = {
host: "{{ config('broadcasting.connections.pusher.options.host')}}",
port: "{{ config('broadcasting.connections.pusher.options.port')}}",
key: "{{ config('broadcasting.connections.pusher.key')}}",
cluster: "{{ config('broadcasting.connections.pusher.options.cluster')}}"
};
</script>
@endpush
@push('styles')
<link rel="stylesheet" type="text/css" href="{{ mix('css/spa.css') }}">
<style type="text/css">
body {
background-color: #000000;
background-image: radial-gradient(circle, #0f172a 0%, #000000 74%);
}
</style>
@endpush

View file

@ -6,9 +6,9 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<title>{{ config('app.name', 'Pixelfed') }}</title>
<meta property="og:site_name" content="{{ config_cache('app.name', 'pixelfed') }}">
<meta property="og:title" content="{{ config_cache('app.name', 'pixelfed') }}">
<title>{{ config_cache('app.name') ?? 'pixelfed' }}</title>
<meta property="og:site_name" content="{{ config_cache('app.name') ?? 'pixelfed' }}">
<meta property="og:title" content="{{ config_cache('app.name') ?? 'pixelfed' }}">
<meta property="og:type" content="article">
<meta property="og:url" content="{{route('site.about')}}">
<meta property="og:description" content="{{config_cache('app.short_description')}}">

View file

@ -94,6 +94,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
Route::group(['prefix' => 'v2'], function() use($middleware) {
Route::get('search', 'Api\ApiV1Controller@searchV2')->middleware($middleware);
Route::post('media', 'Api\ApiV1Controller@mediaUploadV2')->middleware($middleware);
Route::get('streaming/config', 'Api\ApiV1Controller@getWebsocketConfig');
});
Route::group(['prefix' => 'live'], function() use($middleware) {
@ -101,11 +102,15 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
Route::post('stream/edit', 'LiveStreamController@editStream')->middleware($middleware);
Route::get('active/list', 'LiveStreamController@getActiveStreams')->middleware($middleware);
Route::get('accounts/stream', 'LiveStreamController@getUserStream')->middleware($middleware);
Route::get('accounts/stream/guest', 'LiveStreamController@getUserStreamAsGuest');
Route::delete('accounts/stream', 'LiveStreamController@deleteStream')->middleware($middleware);
Route::get('chat/latest', 'LiveStreamController@getLatestChat')->middleware($middleware);
Route::post('chat/message', 'LiveStreamController@addChatComment')->middleware($middleware);
Route::post('chat/delete', 'LiveStreamController@deleteChatComment')->middleware($middleware);
Route::get('config', 'LiveStreamController@getConfig')->middleware($middleware);
Route::post('chat/ban-user', 'LiveStreamController@banChatUser')->middleware($middleware);
Route::post('chat/pin', 'LiveStreamController@pinChatComment')->middleware($middleware);
Route::post('chat/unpin', 'LiveStreamController@unpinChatComment')->middleware($middleware);
Route::get('config', 'LiveStreamController@getConfig');
Route::post('broadcast/publish', 'LiveStreamController@clientBroadcastPublish');
Route::post('broadcast/finish', 'LiveStreamController@clientBroadcastFinish');
});

View file

@ -14,3 +14,11 @@
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Broadcast::channel('live.chat.{id}', function ($user, $id) {
return true;
}, ['guards' => ['web', 'api']]);
Broadcast::channel('live.presence.{id}', function ($user, $id) {
return [ $user->profile_id ];
}, ['guards' => ['web', 'api']]);

View file

@ -540,6 +540,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::get('p/{username}/{id}.json', 'StatusController@showObject');
Route::get('p/{username}/{id}', 'StatusController@show');
Route::get('{username}/embed', 'ProfileController@embed');
Route::get('{username}/live', 'LiveStreamController@showProfilePlayer');
Route::get('@{username}@{domain}', 'SiteController@legacyWebfingerRedirect');
Route::get('@{username}', 'SiteController@legacyProfileRedirect');
Route::get('{username}', 'ProfileController@show');