Merge pull request #3910 from pixelfed/staging

Staging
This commit is contained in:
daniel 2022-12-09 03:56:24 -07:00 committed by GitHub
commit c584e76471
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
87 changed files with 339 additions and 182 deletions

View file

@ -41,6 +41,11 @@
- Update AvatarObserver, fix cloud delete bug by checking if cloud storage is enabled ([9f7672f5](https://github.com/pixelfed/pixelfed/commit/9f7672f5)) - Update AvatarObserver, fix cloud delete bug by checking if cloud storage is enabled ([9f7672f5](https://github.com/pixelfed/pixelfed/commit/9f7672f5))
- Update DeleteAccountPipeline, dispatch on low queue ([6eabe07c](https://github.com/pixelfed/pixelfed/commit/6eabe07c)) - Update DeleteAccountPipeline, dispatch on low queue ([6eabe07c](https://github.com/pixelfed/pixelfed/commit/6eabe07c))
- Update DeleteAccountPipeline, handle flysystem v3 changes by checking files exist before attempting to delete ([23e2998f](https://github.com/pixelfed/pixelfed/commit/23e2998f)) - Update DeleteAccountPipeline, handle flysystem v3 changes by checking files exist before attempting to delete ([23e2998f](https://github.com/pixelfed/pixelfed/commit/23e2998f))
- Update FollowerService, use redis sorted sets for follower relations ([356cc277](https://github.com/pixelfed/pixelfed/commit/356cc277))
- Update FollowerService, use redis sorted sets for following relations ([f46b01af](https://github.com/pixelfed/pixelfed/commit/f46b01af))
- Update PublicApiController, refactor follower/following api endpoints to consume FollowerService instead of querying database ([b39f91b4](https://github.com/pixelfed/pixelfed/commit/b39f91b4))
- Update follower/following profile layout, optimized for mobile devices and use FollowerService ([78a5575d](https://github.com/pixelfed/pixelfed/commit/78a5575d))
- Update sidebar menu, when clicking on the active feed/timeline buttons force a reload and scroll to top of feed ([78a5575d](https://github.com/pixelfed/pixelfed/commit/78a5575d))
- ([](https://github.com/pixelfed/pixelfed/commit/)) - ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.4 (2022-10-04)](https://github.com/pixelfed/pixelfed/compare/v0.11.3...v0.11.4) ## [v0.11.4 (2022-10-04)](https://github.com/pixelfed/pixelfed/compare/v0.11.3...v0.11.4)

View file

@ -3,7 +3,6 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use App\Story; use App\Story;
use App\StoryView; use App\StoryView;
@ -51,7 +50,7 @@ class StoryGC extends Command
protected function archiveExpiredStories() protected function archiveExpiredStories()
{ {
$stories = Story::whereActive(true) $stories = Story::whereActive(true)
->where('created_at', '<', now()->subHours(24)) ->where('expires_at', '<', now())
->get(); ->get();
foreach($stories as $story) { foreach($stories as $story) {
@ -79,6 +78,7 @@ class StoryGC extends Command
} }
StoryRotateMedia::dispatch($story)->onQueue('story'); StoryRotateMedia::dispatch($story)->onQueue('story');
StoryService::removeRotateQueue($id); StoryService::removeRotateQueue($id);
return;
}); });
} }
} }

View file

@ -650,7 +650,6 @@ class ApiV1Controller extends Controller
->whereNull('status') ->whereNull('status')
->findOrFail($id); ->findOrFail($id);
$private = (bool) $target->is_private; $private = (bool) $target->is_private;
$remote = (bool) $target->domain; $remote = (bool) $target->domain;
$blocked = UserFilter::whereUserId($target->id) $blocked = UserFilter::whereUserId($target->id)
@ -701,6 +700,7 @@ class ApiV1Controller extends Controller
(new FollowerController())->sendFollow($user->profile, $target); (new FollowerController())->sendFollow($user->profile, $target);
} }
FollowPipeline::dispatch($follower); FollowPipeline::dispatch($follower);
$target->increment('followers_count');
} }
RelationshipService::refresh($user->profile_id, $target->id); RelationshipService::refresh($user->profile_id, $target->id);
@ -778,6 +778,10 @@ class ApiV1Controller extends Controller
->whereFollowingId($target->id) ->whereFollowingId($target->id)
->delete(); ->delete();
FollowerService::remove($user->profile_id, $target->id);
$target->decrement('followers_count');
if($remote == true && config('federation.activitypub.remoteFollow') == true) { if($remote == true && config('federation.activitypub.remoteFollow') == true) {
(new FollowerController())->sendUndoFollow($user->profile, $target); (new FollowerController())->sendUndoFollow($user->profile, $target);
} }

View file

@ -133,7 +133,7 @@ class FederationController extends Controller
// }); // });
$res = []; $res = [];
return response(json_encode($res, JSON_UNESCAPED_SLASHES))->header('Content-Type', 'application/activity+json'); return response(json_encode($res, JSON_UNESCAPED_SLASHES))->header('Content-Type', 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"');
} }
public function userInbox(Request $request, $username) public function userInbox(Request $request, $username)

View file

@ -14,7 +14,7 @@ class InstanceActorController extends Controller
$res = (new InstanceActor())->first()->getActor(); $res = (new InstanceActor())->first()->getActor();
return json_encode($res, JSON_UNESCAPED_SLASHES); return json_encode($res, JSON_UNESCAPED_SLASHES);
}); });
return response($res)->header('Content-Type', 'application/activity+json'); return response($res)->header('Content-Type', 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"');
} }
public function inbox() public function inbox()
@ -32,6 +32,6 @@ class InstanceActorController extends Controller
'first' => config('app.url') . '/i/actor/outbox?page=true', 'first' => config('app.url') . '/i/actor/outbox?page=true',
'last' => config('app.url') . '/i/actor/outbox?min_id=0page=true' 'last' => config('app.url') . '/i/actor/outbox?min_id=0page=true'
], JSON_UNESCAPED_SLASHES); ], JSON_UNESCAPED_SLASHES);
return response($res)->header('Content-Type', 'application/activity+json'); return response($res)->header('Content-Type', 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"');
} }
} }

View file

@ -191,7 +191,7 @@ class ProfileController extends Controller
$fractal = new Fractal\Manager(); $fractal = new Fractal\Manager();
$resource = new Fractal\Resource\Item($user, new ProfileTransformer); $resource = new Fractal\Resource\Item($user, new ProfileTransformer);
$res = $fractal->createData($resource)->toArray(); $res = $fractal->createData($resource)->toArray();
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json'); return response(json_encode($res['data']))->header('Content-Type', 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"');
}); });
} }

View file

@ -747,7 +747,7 @@ class PublicApiController extends Controller
public function accountFollowers(Request $request, $id) public function accountFollowers(Request $request, $id)
{ {
abort_if(!$request->user(), 403); abort_if(!$request->user(), 403);
$account = AccountService::get($id); $account = AccountService::get($id, true);
abort_if(!$account, 404); abort_if(!$account, 404);
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
@ -762,24 +762,15 @@ class PublicApiController extends Controller
return []; return [];
} }
if($request->has('page') && $request->page >= 5) { if($request->has('page') && $request->page >= 10) {
return []; return [];
} }
} }
$res = DB::table('followers') $res = collect(FollowerService::followersPaginate($account['id'], $request->input('page', 1)))
->select('id', 'profile_id', 'following_id') ->map(fn($id) => AccountService::get($id, true))
->whereFollowingId($account['id']) ->filter()
->orderByDesc('id') ->values();
->simplePaginate(10)
->map(function($follower) {
return AccountService::get($follower->profile_id);
})
->filter(function($account) {
return $account && isset($account['id']);
})
->values()
->toArray();
return response()->json($res); return response()->json($res);
} }
@ -787,7 +778,7 @@ class PublicApiController extends Controller
public function accountFollowing(Request $request, $id) public function accountFollowing(Request $request, $id)
{ {
abort_if(!$request->user(), 403); abort_if(!$request->user(), 403);
$account = AccountService::get($id); $account = AccountService::get($id, true);
abort_if(!$account, 404); abort_if(!$account, 404);
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
@ -802,24 +793,15 @@ class PublicApiController extends Controller
return []; return [];
} }
if($request->has('page') && $request->page >= 5) { if($request->has('page') && $request->page >= 10) {
return []; return [];
} }
} }
$res = DB::table('followers') $res = collect(FollowerService::followingPaginate($account['id'], $request->input('page', 1)))
->select('id', 'profile_id', 'following_id') ->map(fn($id) => AccountService::get($id, true))
->whereProfileId($account['id']) ->filter()
->orderByDesc('id') ->values();
->simplePaginate(10)
->map(function($follower) {
return AccountService::get($follower->following_id);
})
->filter(function($account) {
return $account && isset($account['id']);
})
->values()
->toArray();
return response()->json($res); return response()->json($res);
} }

View file

@ -293,7 +293,7 @@ class StatusController extends Controller
$resource = new Fractal\Resource\Item($status, $object); $resource = new Fractal\Resource\Item($status, $object);
$res = $fractal->createData($resource)->toArray(); $res = $fractal->createData($resource)->toArray();
return response()->json($res['data'], 200, ['Content-Type' => 'application/activity+json'], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); return response()->json($res['data'], 200, ['Content-Type' => 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"'], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
} }
public function edit(Request $request, $username, $id) public function edit(Request $request, $username, $id)

View file

@ -17,7 +17,7 @@ class EmailVerificationCheck
public function handle($request, Closure $next) public function handle($request, Closure $next)
{ {
if ($request->user() && if ($request->user() &&
config_cache('pixelfed.enforce_email_verification') && config('pixelfed.enforce_email_verification') &&
is_null($request->user()->email_verified_at) && is_null($request->user()->email_verified_at) &&
!$request->is( !$request->is(
'i/auth/*', 'i/auth/*',

View file

@ -11,6 +11,7 @@ use DB;
use Storage; use Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use App\Services\AccountService; use App\Services\AccountService;
use App\Services\FollowerService;
use App\Services\PublicTimelineService; use App\Services\PublicTimelineService;
use App\{ use App\{
AccountInterstitial, AccountInterstitial,
@ -133,7 +134,11 @@ class DeleteAccountPipeline implements ShouldQueue
->forceDelete(); ->forceDelete();
Follower::whereProfileId($id) Follower::whereProfileId($id)
->orWhere('following_id', $id) ->orWhere('following_id', $id)
->forceDelete(); ->each(function($follow) {
FollowerService::remove($follow->profile_id, $follow->following_id);
$follow->delete();
});
FollowerService::delCache($id);
Like::whereProfileId($id)->forceDelete(); Like::whereProfileId($id)->forceDelete();
}); });

View file

@ -0,0 +1,87 @@
<?php
namespace App\Jobs\FollowPipeline;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Services\AccountService;
use App\Services\FollowerService;
use Cache;
use DB;
use App\Profile;
class FollowServiceWarmCache implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $profileId;
public $tries = 5;
public $timeout = 300;
public $failOnTimeout = true;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($profileId)
{
$this->profileId = $profileId;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$id = $this->profileId;
$account = AccountService::get($id, true);
if(!$account) {
Cache::put(FollowerService::FOLLOWERS_SYNC_KEY . $id, 1);
Cache::put(FollowerService::FOLLOWING_SYNC_KEY . $id, 1);
return;
}
DB::table('followers')
->select('id', 'following_id', 'profile_id')
->whereFollowingId($id)
->orderBy('id')
->chunk(200, function($followers) use($id) {
foreach($followers as $follow) {
FollowerService::add($follow->profile_id, $id);
}
});
DB::table('followers')
->select('id', 'following_id', 'profile_id')
->whereProfileId($id)
->orderBy('id')
->chunk(200, function($followers) use($id) {
foreach($followers as $follow) {
FollowerService::add($id, $follow->following_id);
}
});
Cache::put(FollowerService::FOLLOWERS_SYNC_KEY . $id, 1);
Cache::put(FollowerService::FOLLOWING_SYNC_KEY . $id, 1);
$profile = Profile::find($id);
if($profile) {
$profile->following_count = DB::table('followers')->whereProfileId($id)->count();
$profile->followers_count = DB::table('followers')->whereFollowingId($id)->count();
$profile->save();
}
AccountService::del($id);
return;
}
}

View file

@ -97,7 +97,7 @@ class RemoteFollowPipeline implements ShouldQueue
$res = $this->response; $res = $this->response;
$url = $res['inbox']; $url = $res['inbox'];
$activity = Zttp::withHeaders(['Content-Type' => 'application/activity+json'])->post($url, [ $activity = Zttp::withHeaders(['Content-Type' => 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"'])->post($url, [
'type' => 'Follow', 'type' => 'Follow',
'object' => $this->follower->url(), 'object' => $this->follower->url(),
]); ]);

View file

@ -69,7 +69,7 @@ class StoryFetch implements ShouldQueue
$version = config('pixelfed.version'); $version = config('pixelfed.version');
$appUrl = config('app.url'); $appUrl = config('app.url');
$headers = [ $headers = [
'Accept' => 'application/json', 'Accept' => 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"',
'Authorization' => 'Bearer ' . $token, 'Authorization' => 'Bearer ' . $token,
'User-Agent' => "(Pixelfed/{$version}; +{$appUrl})", 'User-Agent' => "(Pixelfed/{$version}; +{$appUrl})",
]; ];

View file

@ -0,0 +1,42 @@
<?php
namespace App\Observers;
use App\Follower;
use App\Services\FollowerService;
class FollowerObserver
{
/**
* Handle the Follower "created" event.
*
* @param \App\Follower $follower
* @return void
*/
public function created(Follower $follower)
{
FollowerService::add($follower->profile_id, $follower->following_id);
}
/**
* Handle the Follower "deleted" event.
*
* @param \App\Follower $follower
* @return void
*/
public function deleted(Follower $follower)
{
FollowerService::remove($follower->profile_id, (string) $follower->following_id);
}
/**
* Handle the Follower "force deleted" event.
*
* @param \App\Follower $follower
* @return void
*/
public function forceDeleted(Follower $follower)
{
FollowerService::remove($follower->profile_id, (string) $follower->following_id);
}
}

View file

@ -9,6 +9,7 @@ use App\User;
use App\UserSetting; use App\UserSetting;
use App\Jobs\FollowPipeline\FollowPipeline; use App\Jobs\FollowPipeline\FollowPipeline;
use DB; use DB;
use App\Services\FollowerService;
class UserObserver class UserObserver
{ {
@ -85,6 +86,16 @@ class UserObserver
]); ]);
}); });
} }
}
/**
* Handle the user "deleted" event.
*
* @param \App\User $user
* @return void
*/
public function deleted(User $user)
{
FollowerService::delCache($user->profile_id);
} }
} }

View file

@ -4,6 +4,7 @@ namespace App\Providers;
use App\Observers\{ use App\Observers\{
AvatarObserver, AvatarObserver,
FollowerObserver,
LikeObserver, LikeObserver,
NotificationObserver, NotificationObserver,
ModLogObserver, ModLogObserver,
@ -15,6 +16,7 @@ use App\Observers\{
}; };
use App\{ use App\{
Avatar, Avatar,
Follower,
Like, Like,
Notification, Notification,
ModLog, ModLog,
@ -47,6 +49,7 @@ class AppServiceProvider extends ServiceProvider
Schema::defaultStringLength(191); Schema::defaultStringLength(191);
Paginator::useBootstrap(); Paginator::useBootstrap();
Avatar::observe(AvatarObserver::class); Avatar::observe(AvatarObserver::class);
Follower::observe(FollowerObserver::class);
Like::observe(LikeObserver::class); Like::observe(LikeObserver::class);
Notification::observe(NotificationObserver::class); Notification::observe(NotificationObserver::class);
ModLog::observe(ModLogObserver::class); ModLog::observe(ModLogObserver::class);

View file

@ -17,7 +17,7 @@ class ActivityPubFetchService
} }
$headers = HttpSignature::instanceActorSign($url, false); $headers = HttpSignature::instanceActorSign($url, false);
$headers['Accept'] = 'application/activity+json, application/json'; $headers['Accept'] = 'application/activity+json, application/ld+json; profile="http://www.w3.org/ns/activitystreams"';
$headers['User-Agent'] = '(Pixelfed/'.config('pixelfed.version').'; +'.config('app.url').')'; $headers['User-Agent'] = '(Pixelfed/'.config('pixelfed.version').'; +'.config('app.url').')';
try { try {

View file

@ -10,43 +10,122 @@ use App\{
Profile, Profile,
User User
}; };
use App\Jobs\FollowPipeline\FollowServiceWarmCache;
class FollowerService class FollowerService
{ {
const CACHE_KEY = 'pf:services:followers:'; const CACHE_KEY = 'pf:services:followers:';
const FOLLOWERS_SYNC_ACTIVE = 'pf:services:followers:sync-active:';
const FOLLOWERS_SYNC_KEY = 'pf:services:followers:sync-followers:';
const FOLLOWING_SYNC_KEY = 'pf:services:followers:sync-following:';
const FOLLOWING_KEY = 'pf:services:follow:following:id:'; const FOLLOWING_KEY = 'pf:services:follow:following:id:';
const FOLLOWERS_KEY = 'pf:services:follow:followers:id:'; const FOLLOWERS_KEY = 'pf:services:follow:followers:id:';
public static function add($actor, $target) public static function add($actor, $target)
{ {
$ts = (int) microtime(true);
RelationshipService::refresh($actor, $target); RelationshipService::refresh($actor, $target);
Redis::zadd(self::FOLLOWING_KEY . $actor, $target, $target); Redis::zadd(self::FOLLOWING_KEY . $actor, $ts, $target);
Redis::zadd(self::FOLLOWERS_KEY . $target, $actor, $actor); Redis::zadd(self::FOLLOWERS_KEY . $target, $ts, $actor);
} }
public static function remove($actor, $target) public static function remove($actor, $target)
{ {
RelationshipService::refresh($actor, $target);
Redis::zrem(self::FOLLOWING_KEY . $actor, $target); Redis::zrem(self::FOLLOWING_KEY . $actor, $target);
Redis::zrem(self::FOLLOWERS_KEY . $target, $actor); Redis::zrem(self::FOLLOWERS_KEY . $target, $actor);
Cache::forget('pf:services:follow:audience:' . $actor); Cache::forget('pf:services:follow:audience:' . $actor);
Cache::forget('pf:services:follow:audience:' . $target); Cache::forget('pf:services:follow:audience:' . $target);
AccountService::del($actor);
AccountService::del($target);
RelationshipService::refresh($actor, $target);
} }
public static function followers($id, $start = 0, $stop = 10) public static function followers($id, $start = 0, $stop = 10)
{ {
return Redis::zrange(self::FOLLOWERS_KEY . $id, $start, $stop); self::cacheSyncCheck($id, 'followers');
return Redis::zrevrange(self::FOLLOWERS_KEY . $id, $start, $stop);
} }
public static function following($id, $start = 0, $stop = 10) public static function following($id, $start = 0, $stop = 10)
{ {
return Redis::zrange(self::FOLLOWING_KEY . $id, $start, $stop); self::cacheSyncCheck($id, 'following');
return Redis::zrevrange(self::FOLLOWING_KEY . $id, $start, $stop);
}
public static function followersPaginate($id, $page = 1, $limit = 10)
{
$start = $page == 1 ? 0 : $page * $limit - $limit;
$end = $start + ($limit - 1);
return self::followers($id, $start, $end);
}
public static function followingPaginate($id, $page = 1, $limit = 10)
{
$start = $page == 1 ? 0 : $page * $limit - $limit;
$end = $start + ($limit - 1);
return self::following($id, $start, $end);
}
public static function followerCount($id, $warmCache = true)
{
if($warmCache) {
self::cacheSyncCheck($id, 'followers');
}
return Redis::zCard(self::FOLLOWERS_KEY . $id);
}
public static function followingCount($id, $warmCache = true)
{
if($warmCache) {
self::cacheSyncCheck($id, 'following');
}
return Redis::zCard(self::FOLLOWING_KEY . $id);
} }
public static function follows(string $actor, string $target) public static function follows(string $actor, string $target)
{ {
if($actor == $target) {
return false;
}
if(self::followerCount($target, false) && self::followingCount($actor, false)) {
self::cacheSyncCheck($target, 'followers');
return (bool) Redis::zScore(self::FOLLOWERS_KEY . $target, $actor);
} else {
self::cacheSyncCheck($target, 'followers');
self::cacheSyncCheck($actor, 'following');
return Follower::whereProfileId($actor)->whereFollowingId($target)->exists(); return Follower::whereProfileId($actor)->whereFollowingId($target)->exists();
} }
}
public static function cacheSyncCheck($id, $scope = 'followers')
{
if($scope === 'followers') {
if(Cache::get(self::FOLLOWERS_SYNC_KEY . $id) != null) {
return;
}
if(Cache::get(self::FOLLOWERS_SYNC_ACTIVE . $id) != null) {
return;
}
FollowServiceWarmCache::dispatch($id)->onQueue('low');
Cache::put(self::FOLLOWERS_SYNC_ACTIVE . $id, 1, 604800);
}
if($scope === 'following') {
if(Cache::get(self::FOLLOWING_SYNC_KEY . $id) != null) {
return;
}
if(Cache::get(self::FOLLOWERS_SYNC_ACTIVE . $id) != null) {
return;
}
FollowServiceWarmCache::dispatch($id)->onQueue('low');
Cache::put(self::FOLLOWERS_SYNC_ACTIVE . $id, 1, 604800);
}
return;
}
public static function audience($profile, $scope = null) public static function audience($profile, $scope = null)
{ {
@ -114,4 +193,13 @@ class FollowerService
}); });
} }
public static function delCache($id)
{
Redis::del(self::CACHE_KEY . $id);
Redis::del(self::FOLLOWING_KEY . $id);
Redis::del(self::FOLLOWERS_KEY . $id);
Cache::forget(self::FOLLOWERS_SYNC_KEY . $id);
Cache::forget(self::FOLLOWING_SYNC_KEY . $id);
Cache::forget(self::FOLLOWERS_SYNC_ACTIVE . $id);
}
} }

View file

@ -14,8 +14,8 @@ class RelationshipService
public static function get($aid, $tid) public static function get($aid, $tid)
{ {
$actor = AccountService::get($aid); $actor = AccountService::get($aid, true);
$target = AccountService::get($tid); $target = AccountService::get($tid, true);
if(!$actor || !$target) { if(!$actor || !$target) {
return self::defaultRelation($tid); return self::defaultRelation($tid);
} }

View file

@ -3,7 +3,9 @@
namespace App\Transformer\Api; namespace App\Transformer\Api;
use Auth; use Auth;
use Cache;
use App\Profile; use App\Profile;
use App\User;
use League\Fractal; use League\Fractal;
use App\Services\PronounService; use App\Services\PronounService;
@ -15,8 +17,16 @@ class AccountTransformer extends Fractal\TransformerAbstract
public function transform(Profile $profile) public function transform(Profile $profile)
{ {
$local = $profile->domain == null; if(!$profile) {
$is_admin = !$local ? false : $profile->user->is_admin; return [];
}
$adminIds = Cache::remember('pf:admin-ids', 604800, function() {
return User::whereIsAdmin(true)->pluck('profile_id')->toArray();
});
$local = $profile->private_key != null;
$is_admin = !$local ? false : in_array($profile->id, $adminIds);
$acct = $local ? $profile->username : substr($profile->username, 1); $acct = $local ? $profile->username : substr($profile->username, 1);
$username = $local ? $profile->username : explode('@', $acct)[0]; $username = $local ? $profile->username : explode('@', $acct)[0];
return [ return [
@ -26,9 +36,9 @@ class AccountTransformer extends Fractal\TransformerAbstract
'display_name' => $profile->name, 'display_name' => $profile->name,
'discoverable' => true, 'discoverable' => true,
'locked' => (bool) $profile->is_private, 'locked' => (bool) $profile->is_private,
'followers_count' => (int) $profile->followerCount(), 'followers_count' => (int) $profile->followers_count,
'following_count' => (int) $profile->followingCount(), 'following_count' => (int) $profile->following_count,
'statuses_count' => (int) $profile->statusCount(), 'statuses_count' => (int) $profile->status_count,
'note' => $profile->bio ?? '', 'note' => $profile->bio ?? '',
'note_text' => $profile->bio ? strip_tags($profile->bio) : null, 'note_text' => $profile->bio ? strip_tags($profile->bio) : null,
'url' => $profile->url(), 'url' => $profile->url(),

View file

@ -132,7 +132,7 @@ class HttpSignature {
'(request-target)' => 'post '.parse_url($url, PHP_URL_PATH), '(request-target)' => 'post '.parse_url($url, PHP_URL_PATH),
'Date' => $date->format('D, d M Y H:i:s \G\M\T'), 'Date' => $date->format('D, d M Y H:i:s \G\M\T'),
'Host' => parse_url($url, PHP_URL_HOST), 'Host' => parse_url($url, PHP_URL_HOST),
'Accept' => 'application/activity+json, application/json', 'Accept' => 'application/activity+json, application/ld+json; profile="http://www.w3.org/ns/activitystreams"',
]; ];
if($digest) { if($digest) {

View file

@ -121,6 +121,7 @@ class RestrictedNames
'broadcaster', 'broadcaster',
'booth', 'booth',
'bouncer', 'bouncer',
'browse',
'c', 'c',
'cdn', 'cdn',
'circle', 'circle',
@ -165,6 +166,7 @@ class RestrictedNames
'exports', 'exports',
'f', 'f',
'feed', 'feed',
'featured',
'font', 'font',
'fonts', 'fonts',
'follow', 'follow',
@ -203,6 +205,7 @@ class RestrictedNames
'import', 'import',
'imports', 'imports',
'j', 'j',
'join',
'js', 'js',
'k', 'k',
'key', 'key',
@ -257,6 +260,8 @@ class RestrictedNames
'photo', 'photo',
'photos', 'photos',
'password', 'password',
'portfolio',
'portfolios',
'privacy', 'privacy',
'private', 'private',
'q', 'q',

12
package-lock.json generated
View file

@ -2027,9 +2027,9 @@
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.11.10", "version": "18.11.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==" "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
}, },
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
@ -10804,9 +10804,9 @@
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA=="
}, },
"@types/node": { "@types/node": {
"version": "18.11.10", "version": "18.11.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz",
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==" "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g=="
}, },
"@types/parse-json": { "@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",

BIN
public/css/admin.css vendored

Binary file not shown.

BIN
public/css/app.css vendored

Binary file not shown.

BIN
public/css/appdark.css vendored

Binary file not shown.

BIN
public/css/landing.css vendored

Binary file not shown.

Binary file not shown.

BIN
public/css/spa.css vendored

Binary file not shown.

BIN
public/js/activity.js vendored

Binary file not shown.

BIN
public/js/admin.js vendored

Binary file not shown.

BIN
public/js/app.js vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/compose-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/compose.js vendored

Binary file not shown.

BIN
public/js/daci-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/dffc-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/direct.js vendored

Binary file not shown.

BIN
public/js/discover-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/discover.js vendored

Binary file not shown.

BIN
public/js/dms-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/dmsg-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/dmyh-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/dmym-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/dsfc-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/dssc-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/hashtag.js vendored

Binary file not shown.

BIN
public/js/home-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/installer.js vendored

Binary file not shown.

Binary file not shown.

BIN
public/js/manifest.js vendored

Binary file not shown.

BIN
public/js/notifications-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/portfolio.js vendored

Binary file not shown.

View file

@ -0,0 +1 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

BIN
public/js/post-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
public/js/profile-12722-3lkw2.js vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/js/profile.js vendored

Binary file not shown.

BIN
public/js/rempos.js vendored

Binary file not shown.

BIN
public/js/rempro.js vendored

Binary file not shown.

BIN
public/js/search.js vendored

Binary file not shown.

BIN
public/js/spa.js vendored

Binary file not shown.

BIN
public/js/status.js vendored

Binary file not shown.

BIN
public/js/stories.js vendored

Binary file not shown.

Binary file not shown.

BIN
public/js/timeline.js vendored

Binary file not shown.

BIN
public/js/vendor.js vendored

Binary file not shown.

View file

@ -1,6 +1,6 @@
/*! /*!
* Bootstrap v4.6.1 (https://getbootstrap.com/) * Bootstrap v4.6.2 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/ */
@ -31,13 +31,13 @@
*/ */
/*! /*!
* Cropper.js v1.5.12 * Cropper.js v1.5.13
* https://fengyuanchen.github.io/cropperjs * https://fengyuanchen.github.io/cropperjs
* *
* Copyright 2015-present Chen Fengyuan * Copyright 2015-present Chen Fengyuan
* Released under the MIT license * Released under the MIT license
* *
* Date: 2021-06-12T08:00:17.411Z * Date: 2022-11-20T05:30:46.114Z
*/ */
/*! /*!
@ -49,7 +49,7 @@
*/ */
/*! /*!
* Pusher JavaScript Library v7.1.1-beta * Pusher JavaScript Library v7.5.0
* https://pusher.com/ * https://pusher.com/
* *
* Copyright 2020, Pusher * Copyright 2020, Pusher
@ -76,13 +76,13 @@
*/ */
/*! /*!
* Vue.js v2.6.14 * Vue.js v2.7.14
* (c) 2014-2021 Evan You * (c) 2014-2022 Evan You
* Released under the MIT License. * Released under the MIT License.
*/ */
/*! /*!
* jQuery JavaScript Library v3.6.0 * jQuery JavaScript Library v3.6.1
* https://jquery.com/ * https://jquery.com/
* *
* Includes Sizzle.js * Includes Sizzle.js
@ -92,7 +92,7 @@
* Released under the MIT license * Released under the MIT license
* https://jquery.org/license * https://jquery.org/license
* *
* Date: 2021-03-02T17:08Z * Date: 2022-08-26T17:52Z
*/ */
/*! /*!
@ -114,7 +114,7 @@
*/ */
/*! /*!
* vue-i18n v8.27.1 * vue-i18n v8.28.2
* (c) 2022 kazuya kawaguchi * (c) 2022 kazuya kawaguchi
* Released under the MIT License. * Released under the MIT License.
*/ */
@ -191,6 +191,8 @@ and limitations under the License.
/*! ../is-supported */ /*! ../is-supported */
/*! ../loader/date-range */
/*! ../loader/fragment */ /*! ../loader/fragment */
/*! ../loader/fragment-loader */ /*! ../loader/fragment-loader */
@ -205,6 +207,8 @@ and limitations under the License.
/*! ../types/cmcd */ /*! ../types/cmcd */
/*! ../types/demuxer */
/*! ../types/level */ /*! ../types/level */
/*! ../types/loader */ /*! ../types/loader */
@ -265,8 +269,6 @@ and limitations under the License.
/*! ./buffer-operation-queue */ /*! ./buffer-operation-queue */
/*! ./chunk-cache */
/*! ./config */ /*! ./config */
/*! ./controller/abr-controller */ /*! ./controller/abr-controller */
@ -301,6 +303,8 @@ and limitations under the License.
/*! ./controller/timeline-controller */ /*! ./controller/timeline-controller */
/*! ./date-range */
/*! ./dummy-demuxed-track */ /*! ./dummy-demuxed-track */
/*! ./errors */ /*! ./errors */
@ -319,8 +323,6 @@ and limitations under the License.
/*! ./gap-controller */ /*! ./gap-controller */
/*! ./id3 */
/*! ./is-supported */ /*! ./is-supported */
/*! ./level-details */ /*! ./level-details */
@ -341,6 +343,8 @@ and limitations under the License.
/*! ./mp4-generator */ /*! ./mp4-generator */
/*! ./mp4-remuxer */
/*! ./mp4-tools */ /*! ./mp4-tools */
/*! ./mpegaudio */ /*! ./mpegaudio */
@ -353,8 +357,6 @@ and limitations under the License.
/*! ./timescale-conversion */ /*! ./timescale-conversion */
/*! ./tsdemuxer */
/*! ./typed-array */ /*! ./typed-array */
/*! ./utils/cues */ /*! ./utils/cues */
@ -375,110 +377,14 @@ and limitations under the License.
/*! ./webvtt-parser */ /*! ./webvtt-parser */
/*! ./webworkify-webpack */
/*! eventemitter3 */ /*! 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 */ /*! https://mths.be/punycode v1.4.1 by @mathias */
/*! no static exports found */
/*! url-toolkit */ /*! url-toolkit */
/*! webworkify-webpack */
/*!********************!*\ /*!********************!*\
!*** ./src/hls.ts ***! !*** ./src/hls.ts ***!
\********************/ \********************/
@ -543,6 +449,10 @@ and limitations under the License.
!*** ./src/utils/vttcue.ts ***! !*** ./src/utils/vttcue.ts ***!
\*****************************/ \*****************************/
/*!******************************!*\
!*** ./src/types/demuxer.ts ***!
\******************************/
/*!********************************!*\ /*!********************************!*\
!*** ./src/crypt/decrypter.ts ***! !*** ./src/crypt/decrypter.ts ***!
\********************************/ \********************************/
@ -623,6 +533,10 @@ and limitations under the License.
!*** ./src/demux/chunk-cache.ts ***! !*** ./src/demux/chunk-cache.ts ***!
\**********************************/ \**********************************/
/*!**********************************!*\
!*** ./src/loader/date-range.ts ***!
\**********************************/
/*!**********************************!*\ /*!**********************************!*\
!*** ./src/loader/key-loader.ts ***! !*** ./src/loader/key-loader.ts ***!
\**********************************/ \**********************************/
@ -723,6 +637,10 @@ and limitations under the License.
!*** ./src/demux/base-audio-demuxer.ts ***! !*** ./src/demux/base-audio-demuxer.ts ***!
\*****************************************/ \*****************************************/
/*!*****************************************!*\
!*** ./src/demux/webworkify-webpack.js ***!
\*****************************************/
/*!*****************************************!*\ /*!*****************************************!*\
!*** ./src/utils/mediasource-helper.ts ***! !*** ./src/utils/mediasource-helper.ts ***!
\*****************************************/ \*****************************************/
@ -807,10 +725,6 @@ and limitations under the License.
!*** ./src/controller/id3-track-controller.ts ***! !*** ./src/controller/id3-track-controller.ts ***!
\************************************************/ \************************************************/
/*!**************************************************!*\
!*** ./node_modules/webworkify-webpack/index.js ***!
\**************************************************/
/*!**************************************************!*\ /*!**************************************************!*\
!*** ./src/controller/audio-track-controller.ts ***! !*** ./src/controller/audio-track-controller.ts ***!
\**************************************************/ \**************************************************/

Binary file not shown.