mirror of https://github.com/pixelfed/pixelfed.git
commit
255c41fb83
|
@ -26,6 +26,7 @@
|
|||
- Add ffmpeg config, disable logging by default ([108e3803](https://github.com/pixelfed/pixelfed/commit/108e3803))
|
||||
- Refactor AP profileFetch logic to fix race conditions and improve updating fields and avatars ([505261da](https://github.com/pixelfed/pixelfed/commit/505261da))
|
||||
- Update network timeline api, limit falloff to 2 days ([13a66303](https://github.com/pixelfed/pixelfed/commit/13a66303))
|
||||
- Update Inbox, store follow request activity ([c82f2085](https://github.com/pixelfed/pixelfed/commit/c82f2085))
|
||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||
|
||||
## [v0.11.3 (2022-05-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.2...v0.11.3)
|
||||
|
|
|
@ -29,9 +29,6 @@ class GenerateInstanceActor extends Command
|
|||
}
|
||||
|
||||
if(InstanceActor::exists()) {
|
||||
$this->line(' ');
|
||||
$this->error('Instance actor already exists!');
|
||||
$this->line(' ');
|
||||
$actor = InstanceActor::whereNotNull('public_key')
|
||||
->whereNotNull('private_key')
|
||||
->firstOrFail();
|
||||
|
@ -42,7 +39,8 @@ class GenerateInstanceActor extends Command
|
|||
Cache::rememberForever(InstanceActor::PKI_PRIVATE, function() use($actor) {
|
||||
return $actor->private_key;
|
||||
});
|
||||
exit;
|
||||
$this->info('Instance actor succesfully generated. You do not need to run this command again.');
|
||||
return;
|
||||
}
|
||||
|
||||
$pkiConfig = [
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
|||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use \PDO;
|
||||
|
||||
class Installer extends Command
|
||||
{
|
||||
|
@ -12,7 +13,7 @@ class Installer extends Command
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'install';
|
||||
protected $signature = 'install {--dangerously-overwrite-env : Re-run installation and overwrite current .env }';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
|
@ -21,6 +22,8 @@ class Installer extends Command
|
|||
*/
|
||||
protected $description = 'CLI Installer';
|
||||
|
||||
public $installType = 'Simple';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
|
@ -54,23 +57,48 @@ class Installer extends Command
|
|||
$this->info(' ');
|
||||
$this->info('Pixelfed version: ' . config('pixelfed.version'));
|
||||
$this->line(' ');
|
||||
$this->info('Scanning system...');
|
||||
$this->preflightCheck();
|
||||
$this->envCheck();
|
||||
}
|
||||
protected function preflightCheck()
|
||||
|
||||
protected function envCheck()
|
||||
{
|
||||
$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');
|
||||
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->line('');
|
||||
exit;
|
||||
}
|
||||
$this->installType();
|
||||
}
|
||||
|
||||
protected function installType()
|
||||
{
|
||||
$type = $this->choice('Select installation type', ['Simple', 'Advanced'], 0);
|
||||
$this->installType = $type;
|
||||
$this->preflightCheck();
|
||||
}
|
||||
|
||||
protected function preflightCheck()
|
||||
{
|
||||
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->checkPermissions();
|
||||
$this->envCheck();
|
||||
}
|
||||
|
||||
protected function checkPhpDependencies()
|
||||
|
@ -81,31 +109,39 @@ class Installer extends Command
|
|||
'curl',
|
||||
'json',
|
||||
'mbstring',
|
||||
'openssl'
|
||||
'openssl',
|
||||
];
|
||||
$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...');
|
||||
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} extension not found, aborting installation");
|
||||
$this->error("\"{$ext}\" PHP extension not found, aborting installation");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$this->info("- Required PHP extensions found!");
|
||||
if($this->installType === 'Advanced') {
|
||||
$this->info("- Required PHP extensions found!");
|
||||
}
|
||||
|
||||
$this->checkPermissions();
|
||||
}
|
||||
|
||||
protected function checkPermissions()
|
||||
{
|
||||
$this->line('');
|
||||
$this->info('Checking for proper filesystem permissions...');
|
||||
if($this->installType === 'Advanced') {
|
||||
$this->line('');
|
||||
$this->info('Checking for proper filesystem permissions...');
|
||||
}
|
||||
|
||||
$paths = [
|
||||
base_path('bootstrap'),
|
||||
|
@ -119,100 +155,152 @@ class Installer extends Command
|
|||
$this->error(" $path");
|
||||
exit;
|
||||
} else {
|
||||
$this->info("- Found valid permissions for {$path}");
|
||||
if($this->installType === 'Advanced') {
|
||||
$this->info("- Found valid permissions for {$path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function envCheck()
|
||||
{
|
||||
if(!file_exists(base_path('.env')) || filesize(base_path('.env')) == 0) {
|
||||
$this->line('');
|
||||
$this->info('No .env configuration file found. We will create one now!');
|
||||
$this->createEnv();
|
||||
} else {
|
||||
$confirm = $this->confirm('Found .env file, do you want to overwrite it?');
|
||||
if(!$confirm) {
|
||||
$this->info('Cancelling installation.');
|
||||
exit;
|
||||
}
|
||||
$confirm = $this->confirm('Are you really sure you want to overwrite it?');
|
||||
if(!$confirm) {
|
||||
$this->info('Cancelling installation.');
|
||||
exit;
|
||||
}
|
||||
$this->error('Warning ... if you did not backup your .env before its overwritten it will be permanently deleted.');
|
||||
$confirm = $this->confirm('The application may be installed already, are you really sure you want to overwrite it?');
|
||||
if(!$confirm) {
|
||||
$this->info('Cancelling installation.');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$this->postInstall();
|
||||
$this->createEnv();
|
||||
}
|
||||
|
||||
protected function createEnv()
|
||||
{
|
||||
$this->line('');
|
||||
// copy env
|
||||
if(!file_exists(app()->environmentFilePath())) {
|
||||
exec('cp .env.example .env');
|
||||
$this->call('key:generate');
|
||||
$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 ?? 'https://example.org');
|
||||
$this->updateEnvFile('APP_URL', 'https://' . $domain);
|
||||
|
||||
$database = $this->choice('Select database driver', ['mysql', 'pgsql'], 0);
|
||||
$this->updateEnvFile('DB_CONNECTION', $database ?? 'mysql');
|
||||
switch ($database) {
|
||||
case 'mysql':
|
||||
$database_host = $this->ask('Select database host', '127.0.0.1');
|
||||
$this->updateEnvFile('DB_HOST', $database_host ?? 'mysql');
|
||||
|
||||
$database_port = $this->ask('Select database port', 3306);
|
||||
$this->updateEnvFile('DB_PORT', $database_port ?? 3306);
|
||||
$database_host = $this->ask('Select database host', '127.0.0.1');
|
||||
$this->updateEnvFile('DB_HOST', $database_host ?? 'mysql');
|
||||
|
||||
$database_db = $this->ask('Select database', 'pixelfed');
|
||||
$this->updateEnvFile('DB_DATABASE', $database_db ?? 'pixelfed');
|
||||
$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_username = $this->ask('Select database username', 'pixelfed');
|
||||
$this->updateEnvFile('DB_USERNAME', $database_username ?? 'pixelfed');
|
||||
$database_db = $this->ask('Select database', 'pixelfed');
|
||||
$this->updateEnvFile('DB_DATABASE', $database_db ?? 'pixelfed');
|
||||
|
||||
$db_pass = str_random(64);
|
||||
$database_password = $this->secret('Select database password', $db_pass);
|
||||
$this->updateEnvFile('DB_PASSWORD', $database_password);
|
||||
break;
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$cache = $this->choice('Select cache driver', ["redis", "apc", "array", "database", "file", "memcached"], 0);
|
||||
$this->updateEnvFile('CACHE_DRIVER', $cache ?? 'redis');
|
||||
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');
|
||||
$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_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_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);
|
||||
$redis_port = $this->ask('Set redis port', 6379);
|
||||
$this->updateEnvFile('REDIS_PORT', $redis_port);
|
||||
}
|
||||
|
||||
$open_registration = $this->choice('Allow new registrations?', ['true', 'false'], 1);
|
||||
$open_registration = $this->choice('Allow new registrations?', ['false', 'true'], 0);
|
||||
$this->updateEnvFile('OPEN_REGISTRATION', $open_registration);
|
||||
|
||||
$enforce_email_verification = $this->choice('Enforce email verification?', ['true', 'false'], 0);
|
||||
$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->updateEnvFile('APP_ENV', 'production');
|
||||
$this->postInstall();
|
||||
}
|
||||
|
||||
protected function updateEnvFile($key, $value)
|
||||
|
@ -247,8 +335,35 @@ class Installer extends Command
|
|||
|
||||
protected function postInstall()
|
||||
{
|
||||
$this->callSilent('config:cache');
|
||||
//$this->callSilent('route:cache');
|
||||
$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');
|
||||
}
|
||||
} else {
|
||||
$this->callSilently('config:cache');
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,16 @@ use Illuminate\Database\Eloquent\Model;
|
|||
|
||||
class FollowRequest extends Model
|
||||
{
|
||||
protected $fillable = ['follower_id', 'following_id'];
|
||||
protected $fillable = ['follower_id', 'following_id', 'activity', 'handled_at'];
|
||||
|
||||
protected $casts = [
|
||||
'activity' => 'array',
|
||||
];
|
||||
|
||||
public function actor()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'follower_id', 'id');
|
||||
}
|
||||
|
||||
public function follower()
|
||||
{
|
||||
|
@ -18,13 +27,14 @@ class FollowRequest extends Model
|
|||
return $this->belongsTo(Profile::class, 'following_id', 'id');
|
||||
}
|
||||
|
||||
public function actor()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'follower_id', 'id');
|
||||
}
|
||||
|
||||
public function target()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'following_id', 'id');
|
||||
}
|
||||
|
||||
public function permalink($append = null, $namespace = '#accepts')
|
||||
{
|
||||
$path = $this->target->permalink("{$namespace}/follows/{$this->id}{$append}");
|
||||
return url($path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ use App\Transformer\Api\Mastodon\v1\AccountTransformer;
|
|||
use App\Services\AccountService;
|
||||
use App\Services\UserFilterService;
|
||||
use App\Services\RelationshipService;
|
||||
use App\Jobs\FollowPipeline\FollowAcceptPipeline;
|
||||
use App\Jobs\FollowPipeline\FollowRejectPipeline;
|
||||
|
||||
class AccountController extends Controller
|
||||
{
|
||||
|
@ -363,12 +365,13 @@ class AccountController extends Controller
|
|||
'accounts' => $followers->take(10)->map(function($a) {
|
||||
$actor = $a->actor;
|
||||
return [
|
||||
'id' => $actor->id,
|
||||
'rid' => (string) $a->id,
|
||||
'id' => (string) $actor->id,
|
||||
'username' => $actor->username,
|
||||
'avatar' => $actor->avatarUrl(),
|
||||
'url' => $actor->url(),
|
||||
'local' => $actor->domain == null,
|
||||
'following' => $actor->followedBy(Auth::user()->profile)
|
||||
'account' => AccountService::get($actor->id)
|
||||
];
|
||||
})
|
||||
];
|
||||
|
@ -390,17 +393,35 @@ class AccountController extends Controller
|
|||
|
||||
switch ($action) {
|
||||
case 'accept':
|
||||
$follow = new Follower();
|
||||
$follow->profile_id = $follower->id;
|
||||
$follow->following_id = $pid;
|
||||
$follow->save();
|
||||
FollowPipeline::dispatch($follow);
|
||||
$followRequest->delete();
|
||||
$follow = new Follower();
|
||||
$follow->profile_id = $follower->id;
|
||||
$follow->following_id = $pid;
|
||||
$follow->save();
|
||||
|
||||
$profile = Profile::findOrFail($pid);
|
||||
$profile->followers_count++;
|
||||
$profile->save();
|
||||
AccountService::del($profile->id);
|
||||
|
||||
$profile = Profile::findOrFail($follower->id);
|
||||
$profile->following_count++;
|
||||
$profile->save();
|
||||
AccountService::del($profile->id);
|
||||
|
||||
if($follower->domain != null && $follower->private_key === null) {
|
||||
FollowAcceptPipeline::dispatch($followRequest);
|
||||
} else {
|
||||
FollowPipeline::dispatch($follow);
|
||||
$followRequest->delete();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'reject':
|
||||
$followRequest->is_rejected = true;
|
||||
$followRequest->save();
|
||||
if($follower->domain != null && $follower->private_key === null) {
|
||||
FollowRejectPipeline::dispatch($followRequest);
|
||||
} else {
|
||||
$followRequest->delete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ use App\Services\{
|
|||
FollowerService,
|
||||
InstanceService,
|
||||
LikeService,
|
||||
NetworkTimelineService,
|
||||
NotificationService,
|
||||
MediaPathService,
|
||||
PublicTimelineService,
|
||||
|
@ -82,6 +83,8 @@ use App\Services\DiscoverService;
|
|||
use App\Services\CustomEmojiService;
|
||||
use App\Services\MarkerService;
|
||||
use App\Models\Conversation;
|
||||
use App\Jobs\FollowPipeline\FollowAcceptPipeline;
|
||||
use App\Jobs\FollowPipeline\FollowRejectPipeline;
|
||||
|
||||
class ApiV1Controller extends Controller
|
||||
{
|
||||
|
@ -441,7 +444,7 @@ class ApiV1Controller extends Controller
|
|||
|
||||
if($pid != $account['id']) {
|
||||
if($account['locked']) {
|
||||
if(FollowerService::follows($pid, $account['id'])) {
|
||||
if(!FollowerService::follows($pid, $account['id'])) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +491,7 @@ class ApiV1Controller extends Controller
|
|||
|
||||
if($pid != $account['id']) {
|
||||
if($account['locked']) {
|
||||
if(FollowerService::follows($pid, $account['id'])) {
|
||||
if(!FollowerService::follows($pid, $account['id'])) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -722,6 +725,13 @@ class ApiV1Controller extends Controller
|
|||
->exists();
|
||||
|
||||
if($isFollowing == false) {
|
||||
$followRequest = FollowRequest::whereFollowerId($user->profile_id)
|
||||
->whereFollowingId($target->id)
|
||||
->first();
|
||||
if($followRequest) {
|
||||
$followRequest->delete();
|
||||
RelationshipService::refresh($target->id, $user->profile_id);
|
||||
}
|
||||
$resource = new Fractal\Resource\Item($target, new RelationshipTransformer());
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
|
||||
|
@ -1149,15 +1159,22 @@ class ApiV1Controller extends Controller
|
|||
public function accountFollowRequests(Request $request)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'sometimes|integer|min:1|max:100'
|
||||
]);
|
||||
$user = $request->user();
|
||||
|
||||
$followRequests = FollowRequest::whereFollowingId($user->profile->id)->pluck('follower_id');
|
||||
$res = FollowRequest::whereFollowingId($user->profile->id)
|
||||
->limit($request->input('limit', 40))
|
||||
->pluck('follower_id')
|
||||
->map(function($id) {
|
||||
return AccountService::getMastodon($id, true);
|
||||
})
|
||||
->filter(function($acct) {
|
||||
return $acct && isset($acct['id']);
|
||||
})
|
||||
->values();
|
||||
|
||||
$profiles = Profile::find($followRequests);
|
||||
|
||||
$resource = new Fractal\Resource\Collection($profiles, new AccountTransformer());
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return $this->json($res);
|
||||
}
|
||||
|
||||
|
@ -1171,10 +1188,46 @@ class ApiV1Controller extends Controller
|
|||
public function accountFollowRequestAccept(Request $request, $id)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
$pid = $request->user()->profile_id;
|
||||
$target = AccountService::getMastodon($id);
|
||||
|
||||
// todo
|
||||
if(!$target) {
|
||||
return response()->json(['error' => 'Record not found'], 404);
|
||||
}
|
||||
|
||||
return response()->json([]);
|
||||
$followRequest = FollowRequest::whereFollowingId($pid)->whereFollowerId($id)->first();
|
||||
|
||||
if(!$followRequest) {
|
||||
return response()->json(['error' => 'Record not found'], 404);
|
||||
}
|
||||
|
||||
$follower = $followRequest->follower;
|
||||
$follow = new Follower();
|
||||
$follow->profile_id = $follower->id;
|
||||
$follow->following_id = $pid;
|
||||
$follow->save();
|
||||
|
||||
$profile = Profile::findOrFail($pid);
|
||||
$profile->followers_count++;
|
||||
$profile->save();
|
||||
AccountService::del($profile->id);
|
||||
|
||||
$profile = Profile::findOrFail($follower->id);
|
||||
$profile->following_count++;
|
||||
$profile->save();
|
||||
AccountService::del($profile->id);
|
||||
|
||||
if($follower->domain != null && $follower->private_key === null) {
|
||||
FollowAcceptPipeline::dispatch($followRequest);
|
||||
} else {
|
||||
FollowPipeline::dispatch($follow);
|
||||
$followRequest->delete();
|
||||
}
|
||||
|
||||
RelationshipService::refresh($pid, $id);
|
||||
$res = RelationshipService::get($pid, $id);
|
||||
$res['followed_by'] = true;
|
||||
return $this->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1187,10 +1240,30 @@ class ApiV1Controller extends Controller
|
|||
public function accountFollowRequestReject(Request $request, $id)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
$pid = $request->user()->profile_id;
|
||||
$target = AccountService::getMastodon($id);
|
||||
|
||||
// todo
|
||||
if(!$target) {
|
||||
return response()->json(['error' => 'Record not found'], 404);
|
||||
}
|
||||
|
||||
return response()->json([]);
|
||||
$followRequest = FollowRequest::whereFollowingId($pid)->whereFollowerId($id)->first();
|
||||
|
||||
if(!$followRequest) {
|
||||
return response()->json(['error' => 'Record not found'], 404);
|
||||
}
|
||||
|
||||
$follower = $followRequest->follower;
|
||||
|
||||
if($follower->domain != null && $follower->private_key === null) {
|
||||
FollowRejectPipeline::dispatch($followRequest);
|
||||
} else {
|
||||
$followRequest->delete();
|
||||
}
|
||||
|
||||
RelationshipService::refresh($pid, $id);
|
||||
$res = RelationshipService::get($pid, $id);
|
||||
return $this->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1811,7 +1884,7 @@ class ApiV1Controller extends Controller
|
|||
->take(($limit * 2))
|
||||
->get()
|
||||
->map(function($s) use($pid) {
|
||||
$status = StatusService::getMastodon($s['id']);
|
||||
$status = StatusService::getMastodon($s['id'], false);
|
||||
if(!$status || !isset($status['account']) || !isset($status['account']['id'])) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1842,7 +1915,7 @@ class ApiV1Controller extends Controller
|
|||
->take(($limit * 2))
|
||||
->get()
|
||||
->map(function($s) use($pid) {
|
||||
$status = StatusService::getMastodon($s['id']);
|
||||
$status = StatusService::getMastodon($s['id'], false);
|
||||
if(!$status || !isset($status['account']) || !isset($status['account']['id'])) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1899,28 +1972,46 @@ class ApiV1Controller extends Controller
|
|||
$this->validate($request,[
|
||||
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'limit' => 'nullable|integer|max:100'
|
||||
'limit' => 'nullable|integer|max:100',
|
||||
'remote' => 'sometimes'
|
||||
]);
|
||||
|
||||
$min = $request->input('min_id');
|
||||
$max = $request->input('max_id');
|
||||
$limit = $request->input('limit') ?? 20;
|
||||
$user = $request->user();
|
||||
$remote = $request->has('remote');
|
||||
$filtered = $user ? UserFilterService::filters($user->profile_id) : [];
|
||||
|
||||
Cache::remember('api:v1:timelines:public:cache_check', 10368000, function() {
|
||||
if(PublicTimelineService::count() == 0) {
|
||||
PublicTimelineService::warmCache(true, 400);
|
||||
}
|
||||
});
|
||||
if($remote && config('instance.timeline.network.cached')) {
|
||||
Cache::remember('api:v1:timelines:network:cache_check', 10368000, function() {
|
||||
if(NetworkTimelineService::count() == 0) {
|
||||
NetworkTimelineService::warmCache(true, config('instance.timeline.network.cache_dropoff'));
|
||||
}
|
||||
});
|
||||
|
||||
if ($max) {
|
||||
$feed = PublicTimelineService::getRankedMaxId($max, $limit + 5);
|
||||
} else if ($min) {
|
||||
$feed = PublicTimelineService::getRankedMinId($min, $limit + 5);
|
||||
} else {
|
||||
$feed = PublicTimelineService::get(0, $limit + 5);
|
||||
}
|
||||
if ($max) {
|
||||
$feed = NetworkTimelineService::getRankedMaxId($max, $limit + 5);
|
||||
} else if ($min) {
|
||||
$feed = NetworkTimelineService::getRankedMinId($min, $limit + 5);
|
||||
} else {
|
||||
$feed = NetworkTimelineService::get(0, $limit + 5);
|
||||
}
|
||||
} else {
|
||||
Cache::remember('api:v1:timelines:public:cache_check', 10368000, function() {
|
||||
if(PublicTimelineService::count() == 0) {
|
||||
PublicTimelineService::warmCache(true, 400);
|
||||
}
|
||||
});
|
||||
|
||||
if ($max) {
|
||||
$feed = PublicTimelineService::getRankedMaxId($max, $limit + 5);
|
||||
} else if ($min) {
|
||||
$feed = PublicTimelineService::getRankedMinId($min, $limit + 5);
|
||||
} else {
|
||||
$feed = PublicTimelineService::get(0, $limit + 5);
|
||||
}
|
||||
}
|
||||
|
||||
$res = collect($feed)
|
||||
->map(function($k) use($user) {
|
||||
|
@ -1943,6 +2034,9 @@ class ApiV1Controller extends Controller
|
|||
// ->toArray();
|
||||
|
||||
$baseUrl = config('app.url') . '/api/v1/timelines/public?limit=' . $limit . '&';
|
||||
if($remote) {
|
||||
$baseUrl .= 'remote=1&';
|
||||
}
|
||||
$minId = $res->map(function($s) {
|
||||
return ['id' => $s['id']];
|
||||
})->min('id');
|
||||
|
|
|
@ -217,4 +217,20 @@ class LiveStreamController extends Controller
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
public function getConfig(Request $request)
|
||||
{
|
||||
abort_if(!config('livestreaming.enabled'), 400);
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$res = [
|
||||
'enabled' => config('livestreaming.enabled'),
|
||||
'broadcast' => [
|
||||
'sources' => config('livestreaming.broadcast.sources'),
|
||||
'limits' => config('livestreaming.broadcast.limits')
|
||||
],
|
||||
];
|
||||
|
||||
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ use App\Services\{
|
|||
LikeService,
|
||||
PublicTimelineService,
|
||||
ProfileService,
|
||||
NetworkTimelineService,
|
||||
ReblogService,
|
||||
RelationshipService,
|
||||
StatusService,
|
||||
|
@ -521,7 +522,7 @@ class PublicApiController extends Controller
|
|||
->limit($limit)
|
||||
->get()
|
||||
->map(function($s) use ($user) {
|
||||
$status = StatusService::get($s->id);
|
||||
$status = StatusService::get($s->id, false);
|
||||
if(!$status) {
|
||||
return false;
|
||||
}
|
||||
|
@ -567,7 +568,7 @@ class PublicApiController extends Controller
|
|||
->limit($limit)
|
||||
->get()
|
||||
->map(function($s) use ($user) {
|
||||
$status = StatusService::get($s->id);
|
||||
$status = StatusService::get($s->id, false);
|
||||
if(!$status) {
|
||||
return false;
|
||||
}
|
||||
|
@ -608,59 +609,92 @@ class PublicApiController extends Controller
|
|||
|
||||
$filtered = $user ? UserFilterService::filters($user->profile_id) : [];
|
||||
|
||||
if($min || $max) {
|
||||
$dir = $min ? '>' : '<';
|
||||
$id = $min ?? $max;
|
||||
$timeline = Status::select(
|
||||
'id',
|
||||
'uri',
|
||||
'type',
|
||||
'scope',
|
||||
'created_at',
|
||||
)
|
||||
->where('id', $dir, $id)
|
||||
->whereNull(['in_reply_to_id', 'reblog_of_id'])
|
||||
->whereNotIn('profile_id', $filtered)
|
||||
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
|
||||
->whereNotNull('uri')
|
||||
->whereScope('public')
|
||||
->where('id', '>', $amin)
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(function($s) use ($user) {
|
||||
$status = StatusService::get($s->id);
|
||||
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id);
|
||||
$status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id);
|
||||
$status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id);
|
||||
return $status;
|
||||
});
|
||||
$res = $timeline->toArray();
|
||||
} else {
|
||||
$timeline = Status::select(
|
||||
'id',
|
||||
'uri',
|
||||
'type',
|
||||
'scope',
|
||||
'created_at',
|
||||
)
|
||||
->whereNull(['in_reply_to_id', 'reblog_of_id'])
|
||||
->whereNotIn('profile_id', $filtered)
|
||||
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
|
||||
->whereNotNull('uri')
|
||||
->whereScope('public')
|
||||
->where('id', '>', $amin)
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(function($s) use ($user) {
|
||||
$status = StatusService::get($s->id);
|
||||
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id);
|
||||
$status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id);
|
||||
$status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id);
|
||||
return $status;
|
||||
});
|
||||
$res = $timeline->toArray();
|
||||
if(config('instance.timeline.network.cached') == false) {
|
||||
if($min || $max) {
|
||||
$dir = $min ? '>' : '<';
|
||||
$id = $min ?? $max;
|
||||
$timeline = Status::select(
|
||||
'id',
|
||||
'uri',
|
||||
'type',
|
||||
'scope',
|
||||
'created_at',
|
||||
)
|
||||
->where('id', $dir, $id)
|
||||
->whereNull(['in_reply_to_id', 'reblog_of_id'])
|
||||
->whereNotIn('profile_id', $filtered)
|
||||
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
|
||||
->whereNotNull('uri')
|
||||
->whereScope('public')
|
||||
->where('id', '>', $amin)
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(function($s) use ($user) {
|
||||
$status = StatusService::get($s->id);
|
||||
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id);
|
||||
$status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id);
|
||||
$status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id);
|
||||
return $status;
|
||||
});
|
||||
$res = $timeline->toArray();
|
||||
} else {
|
||||
$timeline = Status::select(
|
||||
'id',
|
||||
'uri',
|
||||
'type',
|
||||
'scope',
|
||||
'created_at',
|
||||
)
|
||||
->whereNull(['in_reply_to_id', 'reblog_of_id'])
|
||||
->whereNotIn('profile_id', $filtered)
|
||||
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
|
||||
->whereNotNull('uri')
|
||||
->whereScope('public')
|
||||
->where('id', '>', $amin)
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(function($s) use ($user) {
|
||||
$status = StatusService::get($s->id);
|
||||
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id);
|
||||
$status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id);
|
||||
$status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id);
|
||||
return $status;
|
||||
});
|
||||
$res = $timeline->toArray();
|
||||
}
|
||||
} else {
|
||||
Cache::remember('api:v1:timelines:network:cache_check', 10368000, function() {
|
||||
if(NetworkTimelineService::count() == 0) {
|
||||
NetworkTimelineService::warmCache(true, 400);
|
||||
}
|
||||
});
|
||||
|
||||
if ($max) {
|
||||
$feed = NetworkTimelineService::getRankedMaxId($max, $limit);
|
||||
} else if ($min) {
|
||||
$feed = NetworkTimelineService::getRankedMinId($min, $limit);
|
||||
} else {
|
||||
$feed = NetworkTimelineService::get(0, $limit);
|
||||
}
|
||||
|
||||
$res = collect($feed)
|
||||
->map(function($k) use($user) {
|
||||
$status = StatusService::get($k);
|
||||
if($status && isset($status['account']) && $user) {
|
||||
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $k);
|
||||
$status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $k);
|
||||
$status['reblogged'] = (bool) ReblogService::get($user->profile_id, $k);
|
||||
$status['relationship'] = RelationshipService::get($user->profile_id, $status['account']['id']);
|
||||
}
|
||||
return $status;
|
||||
})
|
||||
->filter(function($s) use($filtered) {
|
||||
return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false;
|
||||
})
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return response()->json($res);
|
||||
|
@ -704,7 +738,7 @@ class PublicApiController extends Controller
|
|||
|
||||
if($pid != $account['id']) {
|
||||
if($account['locked']) {
|
||||
if(FollowerService::follows($pid, $account['id'])) {
|
||||
if(!FollowerService::follows($pid, $account['id'])) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -744,7 +778,7 @@ class PublicApiController extends Controller
|
|||
|
||||
if($pid != $account['id']) {
|
||||
if($account['locked']) {
|
||||
if(FollowerService::follows($pid, $account['id'])) {
|
||||
if(!FollowerService::follows($pid, $account['id'])) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,5 +19,9 @@ class TrustProxies extends Middleware
|
|||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
||||
protected $headers = Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\FollowPipeline;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use Cache, Log;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use App\FollowRequest;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use App\Transformer\ActivityPub\Verb\AcceptFollow;
|
||||
|
||||
class FollowAcceptPipeline implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $followRequest;
|
||||
|
||||
/**
|
||||
* Delete the job if its models no longer exist.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(FollowRequest $followRequest)
|
||||
{
|
||||
$this->followRequest = $followRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$follow = $this->followRequest;
|
||||
$actor = $follow->actor;
|
||||
$target = $follow->target;
|
||||
|
||||
if($actor->domain == null || $actor->inbox_url == null || !$target->private_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fractal = new Fractal\Manager();
|
||||
$fractal->setSerializer(new ArraySerializer());
|
||||
$resource = new Fractal\Resource\Item($follow, new AcceptFollow());
|
||||
$activity = $fractal->createData($resource)->toArray();
|
||||
$url = $actor->sharedInbox ?? $actor->inbox_url;
|
||||
|
||||
Helpers::sendSignedObject($target, $url, $activity);
|
||||
|
||||
$follow->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -63,11 +63,6 @@ class FollowPipeline implements ShouldQueue
|
|||
$notification->item_id = $target->id;
|
||||
$notification->item_type = "App\Profile";
|
||||
$notification->save();
|
||||
|
||||
$redis = Redis::connection();
|
||||
|
||||
$nkey = config('cache.prefix').':user.'.$target->id.'.notifications';
|
||||
$redis->lpush($nkey, $notification->id);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\FollowPipeline;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use Cache, Log;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use App\FollowRequest;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use App\Transformer\ActivityPub\Verb\RejectFollow;
|
||||
|
||||
class FollowRejectPipeline implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $followRequest;
|
||||
|
||||
/**
|
||||
* Delete the job if its models no longer exist.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(FollowRequest $followRequest)
|
||||
{
|
||||
$this->followRequest = $followRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$follow = $this->followRequest;
|
||||
$actor = $follow->actor;
|
||||
$target = $follow->target;
|
||||
|
||||
if($actor->domain == null || $actor->inbox_url == null || !$target->private_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fractal = new Fractal\Manager();
|
||||
$fractal->setSerializer(new ArraySerializer());
|
||||
$resource = new Fractal\Resource\Item($follow, new RejectFollow());
|
||||
$activity = $fractal->createData($resource)->toArray();
|
||||
$url = $actor->sharedInbox ?? $actor->inbox_url;
|
||||
|
||||
Helpers::sendSignedObject($target, $url, $activity);
|
||||
|
||||
$follow->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,21 @@ class LiveStream extends Model
|
|||
$host = config('livestreaming.server.host');
|
||||
$port = ':' . config('livestreaming.server.port');
|
||||
$path = '/' . config('livestreaming.server.path') . '?';
|
||||
$query = http_build_query([
|
||||
'name' => $this->stream_id,
|
||||
'key' => $this->stream_key,
|
||||
'ts' => time()
|
||||
]);
|
||||
|
||||
return $proto . $host . $port . $path . $query;
|
||||
}
|
||||
|
||||
public function getStreamRtmpUrl()
|
||||
{
|
||||
$proto = 'rtmp://';
|
||||
$host = config('livestreaming.server.host');
|
||||
$port = ':' . config('livestreaming.server.port');
|
||||
$path = '/' . config('livestreaming.server.path') . '/'. $this->stream_id . '?';
|
||||
$query = http_build_query([
|
||||
'key' => $this->stream_key,
|
||||
'ts' => time()
|
||||
|
|
|
@ -271,7 +271,28 @@ class Profile extends Model
|
|||
$this->permalink('/followers')
|
||||
]
|
||||
];
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'unlisted':
|
||||
$audience = [
|
||||
'to' => [
|
||||
],
|
||||
'cc' => [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
$this->permalink('/followers')
|
||||
]
|
||||
];
|
||||
break;
|
||||
|
||||
case 'private':
|
||||
$audience = [
|
||||
'to' => [
|
||||
$this->permalink('/followers')
|
||||
],
|
||||
'cc' => [
|
||||
]
|
||||
];
|
||||
break;
|
||||
}
|
||||
return $audience;
|
||||
}
|
||||
|
|
|
@ -78,11 +78,11 @@ class FollowerService
|
|||
}
|
||||
return $profile
|
||||
->followers()
|
||||
->whereLocalProfile(false)
|
||||
->get()
|
||||
->map(function($follow) {
|
||||
return $follow->sharedInbox ?? $follow->inbox_url;
|
||||
})
|
||||
->filter()
|
||||
->unique()
|
||||
->values()
|
||||
->toArray();
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\{
|
||||
Profile,
|
||||
Status,
|
||||
UserFilter
|
||||
};
|
||||
|
||||
class NetworkTimelineService
|
||||
{
|
||||
const CACHE_KEY = 'pf:services:timeline:network';
|
||||
|
||||
public static function get($start = 0, $stop = 10)
|
||||
{
|
||||
if($stop > 100) {
|
||||
$stop = 100;
|
||||
}
|
||||
|
||||
return Redis::zrevrange(self::CACHE_KEY, $start, $stop);
|
||||
}
|
||||
|
||||
public static function getRankedMaxId($start = null, $limit = 10)
|
||||
{
|
||||
if(!$start) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_keys(Redis::zrevrangebyscore(self::CACHE_KEY, $start, '-inf', [
|
||||
'withscores' => true,
|
||||
'limit' => [1, $limit]
|
||||
]));
|
||||
}
|
||||
|
||||
public static function getRankedMinId($end = null, $limit = 10)
|
||||
{
|
||||
if(!$end) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_keys(Redis::zrevrangebyscore(self::CACHE_KEY, '+inf', $end, [
|
||||
'withscores' => true,
|
||||
'limit' => [0, $limit]
|
||||
]));
|
||||
}
|
||||
|
||||
public static function add($val)
|
||||
{
|
||||
if(self::count() > config('instance.timeline.network.cache_dropoff')) {
|
||||
if(config('database.redis.client') === 'phpredis') {
|
||||
Redis::zpopmin(self::CACHE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
return Redis::zadd(self::CACHE_KEY, $val, $val);
|
||||
}
|
||||
|
||||
public static function rem($val)
|
||||
{
|
||||
return Redis::zrem(self::CACHE_KEY, $val);
|
||||
}
|
||||
|
||||
public static function del($val)
|
||||
{
|
||||
return self::rem($val);
|
||||
}
|
||||
|
||||
public static function count()
|
||||
{
|
||||
return Redis::zcard(self::CACHE_KEY);
|
||||
}
|
||||
|
||||
public static function warmCache($force = false, $limit = 100)
|
||||
{
|
||||
if(self::count() == 0 || $force == true) {
|
||||
Redis::del(self::CACHE_KEY);
|
||||
$ids = Status::whereNotNull('uri')
|
||||
->whereScope('public')
|
||||
->whereNull('in_reply_to_id')
|
||||
->whereNull('reblog_of_id')
|
||||
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
|
||||
->where('created_at', '>', now()->subHours(config('instance.timeline.network.max_hours_old')))
|
||||
->orderByDesc('created_at')
|
||||
->limit($limit)
|
||||
->pluck('id');
|
||||
foreach($ids as $id) {
|
||||
self::add($id);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -57,6 +57,8 @@ class RelationshipService
|
|||
|
||||
public static function refresh($aid, $tid)
|
||||
{
|
||||
Cache::forget('pf:services:follow:audience:' . $aid);
|
||||
Cache::forget('pf:services:follow:audience:' . $tid);
|
||||
self::delete($tid, $aid);
|
||||
self::delete($aid, $tid);
|
||||
self::get($tid, $aid);
|
||||
|
|
|
@ -3,26 +3,24 @@
|
|||
namespace App\Services;
|
||||
|
||||
use Cache;
|
||||
use App\UserFilter;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
|
||||
use App\{
|
||||
Follower,
|
||||
Profile,
|
||||
UserFilter
|
||||
};
|
||||
|
||||
class UserFilterService {
|
||||
|
||||
class UserFilterService
|
||||
{
|
||||
const USER_MUTES_KEY = 'pf:services:mutes:ids:';
|
||||
const USER_BLOCKS_KEY = 'pf:services:blocks:ids:';
|
||||
|
||||
public static function mutes(int $profile_id) : array
|
||||
public static function mutes(int $profile_id)
|
||||
{
|
||||
$key = self::USER_MUTES_KEY . $profile_id;
|
||||
$cached = Redis::zrevrange($key, 0, -1);
|
||||
if($cached) {
|
||||
return $cached;
|
||||
$warm = Cache::has($key . ':cached');
|
||||
if($warm) {
|
||||
return Redis::zrevrange($key, 0, -1) ?? [];
|
||||
} else {
|
||||
if(Redis::zrevrange($key, 0, -1)) {
|
||||
return Redis::zrevrange($key, 0, -1);
|
||||
}
|
||||
$ids = UserFilter::whereFilterType('mute')
|
||||
->whereUserId($profile_id)
|
||||
->pluck('filterable_id')
|
||||
|
@ -30,29 +28,34 @@ class UserFilterService {
|
|||
foreach ($ids as $muted_id) {
|
||||
Redis::zadd($key, (int) $muted_id, (int) $muted_id);
|
||||
}
|
||||
Cache::set($key . ':cached', 1, 7776000);
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
|
||||
public static function blocks(int $profile_id) : array
|
||||
public static function blocks(int $profile_id)
|
||||
{
|
||||
$key = self::USER_BLOCKS_KEY . $profile_id;
|
||||
$cached = Redis::zrevrange($key, 0, -1);
|
||||
if($cached) {
|
||||
return $cached;
|
||||
$warm = Cache::has($key . ':cached');
|
||||
if($warm) {
|
||||
return Redis::zrevrange($key, 0, -1) ?? [];
|
||||
} else {
|
||||
if(Redis::zrevrange($key, 0, -1)) {
|
||||
return Redis::zrevrange($key, 0, -1);
|
||||
}
|
||||
$ids = UserFilter::whereFilterType('block')
|
||||
->whereUserId($profile_id)
|
||||
->pluck('filterable_id')
|
||||
->toArray();
|
||||
foreach ($ids as $blocked_id) {
|
||||
Redis::zadd($key, $blocked_id, $blocked_id);
|
||||
Redis::zadd($key, (int) $blocked_id, (int) $blocked_id);
|
||||
}
|
||||
Cache::set($key . ':cached', 1, 7776000);
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
|
||||
public static function filters(int $profile_id) : array
|
||||
public static function filters(int $profile_id)
|
||||
{
|
||||
return array_unique(array_merge(self::mutes($profile_id), self::blocks($profile_id)));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Transformer\ActivityPub\Verb;
|
||||
|
||||
use App\FollowRequest;
|
||||
use League\Fractal;
|
||||
|
||||
class AcceptFollow extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(FollowRequest $follow)
|
||||
{
|
||||
return [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'Accept',
|
||||
'id' => $follow->permalink(),
|
||||
'actor' => $follow->target->permalink(),
|
||||
'object' => [
|
||||
'type' => 'Follow',
|
||||
'id' => $follow->activity && isset($follow->activity['id']) ? $follow->activity['id'] : null,
|
||||
'actor' => $follow->actor->permalink(),
|
||||
'object' => $follow->target->permalink()
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Transformer\ActivityPub\Verb;
|
||||
|
||||
use App\FollowRequest;
|
||||
use League\Fractal;
|
||||
|
||||
class RejectFollow extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(FollowRequest $follow)
|
||||
{
|
||||
return [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'Reject',
|
||||
'id' => $follow->permalink(null, '#rejects'),
|
||||
'actor' => $follow->target->permalink(),
|
||||
'object' => [
|
||||
'type' => 'Follow',
|
||||
'id' => $follow->activity && isset($follow->activity['id']) ? $follow->activity['id'] : null,
|
||||
'actor' => $follow->actor->permalink(),
|
||||
'object' => $follow->target->permalink()
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ use App\Services\CustomEmojiService;
|
|||
use App\Services\InstanceService;
|
||||
use App\Services\MediaPathService;
|
||||
use App\Services\MediaStorageService;
|
||||
use App\Services\NetworkTimelineService;
|
||||
use App\Jobs\MediaPipeline\MediaStoragePipeline;
|
||||
use App\Jobs\AvatarPipeline\RemoteAvatarFetch;
|
||||
use App\Util\Media\License;
|
||||
|
@ -490,6 +491,16 @@ class Helpers {
|
|||
if(isset($activity['tag']) && is_array($activity['tag']) && !empty($activity['tag'])) {
|
||||
StatusTagsPipeline::dispatch($activity, $status);
|
||||
}
|
||||
|
||||
if( config('instance.timeline.network.cached') &&
|
||||
$status->in_reply_to_id === null &&
|
||||
$status->reblog_of_id === null &&
|
||||
in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) &&
|
||||
$status->created_at->gt(now()->subHours(config('instance.timeline.network.max_hours_old')))
|
||||
) {
|
||||
NetworkTimelineService::add($status->id);
|
||||
}
|
||||
|
||||
return $status;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -473,17 +473,12 @@ class Inbox
|
|||
return;
|
||||
}
|
||||
if($target->is_private == true) {
|
||||
FollowRequest::firstOrCreate([
|
||||
FollowRequest::updateOrCreate([
|
||||
'follower_id' => $actor->id,
|
||||
'following_id' => $target->id
|
||||
'following_id' => $target->id,
|
||||
],[
|
||||
'activity' => collect($this->payload)->only(['id','actor','object','type'])->toArray()
|
||||
]);
|
||||
|
||||
Cache::forget('profile:follower_count:'.$target->id);
|
||||
Cache::forget('profile:follower_count:'.$actor->id);
|
||||
Cache::forget('profile:following_count:'.$target->id);
|
||||
Cache::forget('profile:following_count:'.$actor->id);
|
||||
FollowerService::add($actor->id, $target->id);
|
||||
|
||||
} else {
|
||||
$follower = new Follower;
|
||||
$follower->profile_id = $actor->id;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,6 +24,12 @@ return [
|
|||
'timeline' => [
|
||||
'local' => [
|
||||
'is_public' => env('INSTANCE_PUBLIC_LOCAL_TIMELINE', false)
|
||||
],
|
||||
|
||||
'network' => [
|
||||
'cached' => env('PF_NETWORK_TIMELINE') ? env('INSTANCE_NETWORK_TIMELINE_CACHED', false) : false,
|
||||
'cache_dropoff' => env('INSTANCE_NETWORK_TIMELINE_CACHE_DROPOFF', 100),
|
||||
'max_hours_old' => env('INSTANCE_NETWORK_TIMELINE_CACHE_MAX_HOUR_INGEST', 6)
|
||||
]
|
||||
],
|
||||
|
||||
|
|
|
@ -10,14 +10,20 @@ return [
|
|||
],
|
||||
|
||||
'broadcast' => [
|
||||
'max_duration' => env('HLS_LIVE_BROADCAST_MAX_DURATION', 60),
|
||||
'max_active' => env('HLS_LIVE_BROADCAST_MAX_ACTIVE', 10),
|
||||
'delete_token_after_finished' => (bool) env('HLS_LIVE_BROADCAST_DELETE_TOKEN_AFTER', true),
|
||||
'max_duration' => (int) env('HLS_LIVE_BROADCAST_MAX_DURATION', 60),
|
||||
'max_active' => (int) env('HLS_LIVE_BROADCAST_MAX_ACTIVE', 10),
|
||||
|
||||
'limits' => [
|
||||
'enabled' => env('HLS_LIVE_BROADCAST_LIMITS', true),
|
||||
'min_follower_count' => env('HLS_LIVE_BROADCAST_LIMITS_MIN_FOLLOWERS', 100),
|
||||
'min_account_age' => env('HLS_LIVE_BROADCAST_LIMITS_MIN_ACCOUNT_AGE', 14),
|
||||
'admins_only' => env('HLS_LIVE_BROADCAST_LIMITS_ADMINS_ONLY', true)
|
||||
'enabled' => (bool) env('HLS_LIVE_BROADCAST_LIMITS', true),
|
||||
'min_follower_count' => (int) env('HLS_LIVE_BROADCAST_LIMITS_MIN_FOLLOWERS', 100),
|
||||
'min_account_age' => (int) env('HLS_LIVE_BROADCAST_LIMITS_MIN_ACCOUNT_AGE', 14),
|
||||
'admins_only' => (bool) env('HLS_LIVE_BROADCAST_LIMITS_ADMINS_ONLY', true)
|
||||
],
|
||||
|
||||
'sources' => [
|
||||
'app' => (bool) env('HLS_LIVE_BROADCAST_SOURCE_APP', false),
|
||||
'web' => (bool) env('HLS_LIVE_BROADCAST_SOURCE_WEB', false)
|
||||
]
|
||||
],
|
||||
|
||||
|
|
|
@ -239,7 +239,7 @@ return [
|
|||
]
|
||||
],
|
||||
|
||||
'max_collection_length' => (int) env('PF_MAX_COLLECTION_LENGTH', 18),
|
||||
'max_collection_length' => (int) env('PF_MAX_COLLECTION_LENGTH', 100),
|
||||
|
||||
'media_types' => env('MEDIA_TYPES', 'image/jpeg,image/png,image/gif'),
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddObjectColumnToFollowRequestsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('follow_requests', function (Blueprint $table) {
|
||||
$table->json('activity')->nullable()->after('following_id');
|
||||
$table->timestamp('handled_at')->nullable()->after('is_local');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('follow_requests', function (Blueprint $table) {
|
||||
$table->dropColumn('activity');
|
||||
$table->dropColumn('handled_at');
|
||||
});
|
||||
}
|
||||
}
|
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
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
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
(()=>{"use strict";var e,o,t,r={},s={};function a(e){var o=s[e];if(void 0!==o)return o.exports;var t=s[e]={id:e,loaded:!1,exports:{}};return r[e].call(t.exports,t,t.exports,a),t.loaded=!0,t.exports}a.m=r,e=[],a.O=(o,t,r,s)=>{if(!t){var n=1/0;for(j=0;j<e.length;j++){for(var[t,r,s]=e[j],d=!0,i=0;i<t.length;i++)(!1&s||n>=s)&&Object.keys(a.O).every((e=>a.O[e](t[i])))?t.splice(i--,1):(d=!1,s<n&&(n=s));if(d){e.splice(j--,1);var l=r();void 0!==l&&(o=l)}}return o}s=s||0;for(var j=e.length;j>0&&e[j-1][2]>s;j--)e[j]=e[j-1];e[j]=[t,r,s]},a.n=e=>{var o=e&&e.__esModule?()=>e.default:()=>e;return a.d(o,{a:o}),o},a.d=(e,o)=>{for(var t in o)a.o(o,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce(((o,t)=>(a.f[t](e,o),o)),[])),a.u=e=>319===e?"js/home-ojtjadoml.js":500===e?"js/compose-ojtjadoml.js":132===e?"js/post-ojtjadoml.js":620===e?"js/profile-ojtjadoml.js":566===e?"js/dmym-ojtjadoml.js":935===e?"js/dmyh-ojtjadoml.js":97===e?"js/daci-ojtjadoml.js":340===e?"js/dffc-ojtjadoml.js":575===e?"js/dsfc-ojtjadoml.js":545===e?"js/dssc-ojtjadoml.js":417===e?"js/discover-ojtjadoml.js":863===e?"js/notifications-ojtjadoml.js":888===e?"js/dms-ojtjadoml.js":43===e?"js/dmsg-ojtjadoml.js":void 0,a.miniCssF=e=>({138:"css/spa",170:"css/app",242:"css/appdark",703:"css/admin",994:"css/landing"}[e]+".css"),a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),o={},t="pixelfed:",a.l=(e,r,s,n)=>{if(o[e])o[e].push(r);else{var d,i;if(void 0!==s)for(var l=document.getElementsByTagName("script"),j=0;j<l.length;j++){var c=l[j];if(c.getAttribute("src")==e||c.getAttribute("data-webpack")==t+s){d=c;break}}d||(i=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,a.nc&&d.setAttribute("nonce",a.nc),d.setAttribute("data-webpack",t+s),d.src=e),o[e]=[r];var u=(t,r)=>{d.onerror=d.onload=null,clearTimeout(f);var s=o[e];if(delete o[e],d.parentNode&&d.parentNode.removeChild(d),s&&s.forEach((e=>e(r))),t)return t(r)},f=setTimeout(u.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=u.bind(null,d.onerror),d.onload=u.bind(null,d.onload),i&&document.head.appendChild(d)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.p="/",(()=>{var e={929:0,242:0,170:0,138:0,703:0,994:0};a.f.j=(o,t)=>{var r=a.o(e,o)?e[o]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(138|170|242|703|929|994)$/.test(o))e[o]=0;else{var s=new Promise(((t,s)=>r=e[o]=[t,s]));t.push(r[2]=s);var n=a.p+a.u(o),d=new Error;a.l(n,(t=>{if(a.o(e,o)&&(0!==(r=e[o])&&(e[o]=void 0),r)){var s=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;d.message="Loading chunk "+o+" failed.\n("+s+": "+n+")",d.name="ChunkLoadError",d.type=s,d.request=n,r[1](d)}}),"chunk-"+o,o)}},a.O.j=o=>0===e[o];var o=(o,t)=>{var r,s,[n,d,i]=t,l=0;if(n.some((o=>0!==e[o]))){for(r in d)a.o(d,r)&&(a.m[r]=d[r]);if(i)var j=i(a)}for(o&&o(t);l<n.length;l++)s=n[l],a.o(e,s)&&e[s]&&e[s][0](),e[s]=0;return a.O(j)},t=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))})()})();
|
||||
(()=>{"use strict";var e,o,t,r={},s={};function a(e){var o=s[e];if(void 0!==o)return o.exports;var t=s[e]={id:e,loaded:!1,exports:{}};return r[e].call(t.exports,t,t.exports,a),t.loaded=!0,t.exports}a.m=r,e=[],a.O=(o,t,r,s)=>{if(!t){var n=1/0;for(j=0;j<e.length;j++){for(var[t,r,s]=e[j],d=!0,i=0;i<t.length;i++)(!1&s||n>=s)&&Object.keys(a.O).every((e=>a.O[e](t[i])))?t.splice(i--,1):(d=!1,s<n&&(n=s));if(d){e.splice(j--,1);var l=r();void 0!==l&&(o=l)}}return o}s=s||0;for(var j=e.length;j>0&&e[j-1][2]>s;j--)e[j]=e[j-1];e[j]=[t,r,s]},a.n=e=>{var o=e&&e.__esModule?()=>e.default:()=>e;return a.d(o,{a:o}),o},a.d=(e,o)=>{for(var t in o)a.o(o,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce(((o,t)=>(a.f[t](e,o),o)),[])),a.u=e=>319===e?"js/home-ojtjadoml.js":500===e?"js/compose-ojtjadoml.js":132===e?"js/post-ojtjadoml.js":620===e?"js/profile-ojtjadoml.js":566===e?"js/dmym-ojtjadoml.js":935===e?"js/dmyh-ojtjadoml.js":97===e?"js/daci-ojtjadoml.js":340===e?"js/dffc-ojtjadoml.js":575===e?"js/dsfc-ojtjadoml.js":545===e?"js/dssc-ojtjadoml.js":417===e?"js/discover-ojtjadoml.js":863===e?"js/notifications-ojtjadoml.js":888===e?"js/dms-ojtjadoml.js":43===e?"js/dmsg-ojtjadoml.js":void 0,a.miniCssF=e=>({138:"css/spa",170:"css/app",242:"css/appdark",703:"css/admin",994:"css/landing"}[e]+".css"),a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),o={},t="pixelfed:",a.l=(e,r,s,n)=>{if(o[e])o[e].push(r);else{var d,i;if(void 0!==s)for(var l=document.getElementsByTagName("script"),j=0;j<l.length;j++){var c=l[j];if(c.getAttribute("src")==e||c.getAttribute("data-webpack")==t+s){d=c;break}}d||(i=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,a.nc&&d.setAttribute("nonce",a.nc),d.setAttribute("data-webpack",t+s),d.src=e),o[e]=[r];var u=(t,r)=>{d.onerror=d.onload=null,clearTimeout(f);var s=o[e];if(delete o[e],d.parentNode&&d.parentNode.removeChild(d),s&&s.forEach((e=>e(r))),t)return t(r)},f=setTimeout(u.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=u.bind(null,d.onerror),d.onload=u.bind(null,d.onload),i&&document.head.appendChild(d)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.p="/",(()=>{var e={929:0,242:0,170:0,138:0,703:0,994:0};a.f.j=(o,t)=>{var r=a.o(e,o)?e[o]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(138|170|242|703|929|994)$/.test(o))e[o]=0;else{var s=new Promise(((t,s)=>r=e[o]=[t,s]));t.push(r[2]=s);var n=a.p+a.u(o),d=new Error;a.l(n,(t=>{if(a.o(e,o)&&(0!==(r=e[o])&&(e[o]=void 0),r)){var s=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;d.message="Loading chunk "+o+" failed.\n("+s+": "+n+")",d.name="ChunkLoadError",d.type=s,d.request=n,r[1](d)}}),"chunk-"+o,o)}},a.O.j=o=>0===e[o];var o=(o,t)=>{var r,s,[n,d,i]=t,l=0;if(n.some((o=>0!==e[o]))){for(r in d)a.o(d,r)&&(a.m[r]=d[r]);if(i)var j=i(a)}for(o&&o(t);l<n.length;l++)s=n[l],a.o(e,s)&&e[s]&&e[s][0](),e[s]=0;return a.O(j)},t=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))})(),a.nc=void 0})();
|
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
|
||||
/*!
|
||||
* BootstrapVue Icons, generated from Bootstrap Icons 1.2.2
|
||||
* BootstrapVue Icons, generated from Bootstrap Icons 1.5.0
|
||||
*
|
||||
* @link https://icons.getbootstrap.com/
|
||||
* @license MIT
|
||||
|
|
|
@ -19,27 +19,27 @@
|
|||
"/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=52d322d61711c604f97f4b86a21924b8",
|
||||
"/js/spa.js": "/js/spa.js?id=b5ca9478a06aa0d2261f9777c481756b",
|
||||
"/js/stories.js": "/js/stories.js?id=814a25875cac8987d85c801dcb453114",
|
||||
"/js/manifest.js": "/js/manifest.js?id=b0ef97b4a0e9ee752c2eb3881efff18b",
|
||||
"/js/home-ojtjadoml.js": "/js/home-ojtjadoml.js?id=69272e7c0c055896aae37f9ebb548151",
|
||||
"/js/compose-ojtjadoml.js": "/js/compose-ojtjadoml.js?id=9b5cb17afea0b49605047cf4973cb089",
|
||||
"/js/post-ojtjadoml.js": "/js/post-ojtjadoml.js?id=0e4e8e761635eb7073fd1050ab4bc2a9",
|
||||
"/js/profile-ojtjadoml.js": "/js/profile-ojtjadoml.js?id=41bc6f10686cf5240fe71c39949b9438",
|
||||
"/js/dmym-ojtjadoml.js": "/js/dmym-ojtjadoml.js?id=1aae0b19ccdd50bb8fb4d3e94c56169d",
|
||||
"/js/dmyh-ojtjadoml.js": "/js/dmyh-ojtjadoml.js?id=1351e6da2369aaf40d010c106edaebf8",
|
||||
"/js/daci-ojtjadoml.js": "/js/daci-ojtjadoml.js?id=8b78c4aa654141ff4c77ad75df152ead",
|
||||
"/js/dffc-ojtjadoml.js": "/js/dffc-ojtjadoml.js?id=04a0d18dfd838e0d2b37fffa0b16e36d",
|
||||
"/js/dsfc-ojtjadoml.js": "/js/dsfc-ojtjadoml.js?id=bdd463cae7ee27e08bf0bb92d13451aa",
|
||||
"/js/dssc-ojtjadoml.js": "/js/dssc-ojtjadoml.js?id=a7dce2f9166901ad967bcfdcb9ad318d",
|
||||
"/js/manifest.js": "/js/manifest.js?id=4e6dd9cb251d9698bfccb781db000cca",
|
||||
"/js/home-ojtjadoml.js": "/js/home-ojtjadoml.js?id=17d512a55e9a924b1aaf7c600ef7d3a5",
|
||||
"/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",
|
||||
"/js/dmym-ojtjadoml.js": "/js/dmym-ojtjadoml.js?id=688e86d82d7b3f43d177f5250a3c7fab",
|
||||
"/js/dmyh-ojtjadoml.js": "/js/dmyh-ojtjadoml.js?id=d326529a8b4499979a99a3e6828c8fbe",
|
||||
"/js/daci-ojtjadoml.js": "/js/daci-ojtjadoml.js?id=e4bd8640fbb005e43d36366d5285c218",
|
||||
"/js/dffc-ojtjadoml.js": "/js/dffc-ojtjadoml.js?id=909f6d2bb6e474e416da9375d2cf33da",
|
||||
"/js/dsfc-ojtjadoml.js": "/js/dsfc-ojtjadoml.js?id=6076fa194e83e8abff4e8c2cd1742f63",
|
||||
"/js/dssc-ojtjadoml.js": "/js/dssc-ojtjadoml.js?id=39fd4059bc9670ca5a3c62d23e5e4827",
|
||||
"/js/discover-ojtjadoml.js": "/js/discover-ojtjadoml.js?id=c0789a5495c786e11df7f3df649130f9",
|
||||
"/js/notifications-ojtjadoml.js": "/js/notifications-ojtjadoml.js?id=c0e1a109c5e375729b1bbd7a473ee1d5",
|
||||
"/js/notifications-ojtjadoml.js": "/js/notifications-ojtjadoml.js?id=e1880951a0cf2b07efd767716254a7e5",
|
||||
"/js/dms-ojtjadoml.js": "/js/dms-ojtjadoml.js?id=d0319c4eda168b305b5599802c2a2c8c",
|
||||
"/js/dmsg-ojtjadoml.js": "/js/dmsg-ojtjadoml.js?id=36a079d71cb4441d891fd1bdd8e83999",
|
||||
"/css/appdark.css": "/css/appdark.css?id=b8ba36bb062ae3f600ab358f9f28f365",
|
||||
"/css/app.css": "/css/app.css?id=7d00fff9bedc99dc27955a7ad3e88df7",
|
||||
"/js/dmsg-ojtjadoml.js": "/js/dmsg-ojtjadoml.js?id=de70e20249d9a4f3c0ae68b5cc737d1f",
|
||||
"/css/appdark.css": "/css/appdark.css?id=d6006aa8d3880210368434c64b942a40",
|
||||
"/css/app.css": "/css/app.css?id=66c10c9502955c88d471277fface8c32",
|
||||
"/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=001124b36242eb5ae784f6532121219d"
|
||||
"/js/vendor.js": "/js/vendor.js?id=467b36b2c099089a7639448443158ceb"
|
||||
}
|
||||
|
|
|
@ -105,5 +105,6 @@ Route::group(['prefix' => 'api'], function() use($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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue