forked from mirror/pixelfed
Merge pull request #5303 from pixelfed/staging
Add Notify App Gateway support
This commit is contained in:
commit
e659798266
15 changed files with 719 additions and 96 deletions
72
app/Console/Commands/PushGatewayRefresh.php
Normal file
72
app/Console/Commands/PushGatewayRefresh.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use function Laravel\Prompts\select;
|
||||
|
||||
class PushGatewayRefresh extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:push-gateway-refresh';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Refresh push notification gateway support';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Checking Push Notification support...');
|
||||
$this->line(' ');
|
||||
|
||||
$currentState = NotificationAppGatewayService::enabled();
|
||||
|
||||
if ($currentState) {
|
||||
$this->info('Push Notification support is active!');
|
||||
|
||||
return;
|
||||
} else {
|
||||
$this->error('Push notification support is NOT active');
|
||||
|
||||
$action = select(
|
||||
label: 'Do you want to force re-check?',
|
||||
options: ['Yes', 'No'],
|
||||
required: true
|
||||
);
|
||||
|
||||
if ($action === 'Yes') {
|
||||
$recheck = NotificationAppGatewayService::forceSupportRecheck();
|
||||
if ($recheck) {
|
||||
$this->info('Success! Push Notifications are now active!');
|
||||
|
||||
return;
|
||||
} else {
|
||||
$this->error('Error, please ensure you have a valid API key.');
|
||||
$this->line(' ');
|
||||
$this->line('For more info, visit https://docs.pixelfed.org/running-pixelfed/push-notifications.html');
|
||||
$this->line(' ');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
exit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,8 +26,10 @@ use App\Services\FollowerService;
|
|||
use App\Services\MediaBlocklistService;
|
||||
use App\Services\MediaPathService;
|
||||
use App\Services\NetworkTimelineService;
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use App\Services\ProfileStatusService;
|
||||
use App\Services\PublicTimelineService;
|
||||
use App\Services\PushNotificationService;
|
||||
use App\Services\StatusService;
|
||||
use App\Services\UserStorageService;
|
||||
use App\Status;
|
||||
|
@ -54,8 +56,8 @@ class ApiV1Dot1Controller extends Controller
|
|||
|
||||
public function __construct()
|
||||
{
|
||||
$this->fractal = new Fractal\Manager();
|
||||
$this->fractal->setSerializer(new ArraySerializer());
|
||||
$this->fractal = new Fractal\Manager;
|
||||
$this->fractal->setSerializer(new ArraySerializer);
|
||||
}
|
||||
|
||||
public function json($res, $code = 200, $headers = [])
|
||||
|
@ -317,7 +319,7 @@ class ApiV1Dot1Controller extends Controller
|
|||
if (config('pixelfed.bouncer.cloud_ips.ban_signups')) {
|
||||
abort_if(BouncerService::checkIp($request->ip()), 404);
|
||||
}
|
||||
$agent = new Agent();
|
||||
$agent = new Agent;
|
||||
$currentIp = $request->ip();
|
||||
|
||||
$activity = AccountLog::whereUserId($user->id)
|
||||
|
@ -575,7 +577,7 @@ class ApiV1Dot1Controller extends Controller
|
|||
|
||||
$rtoken = Str::random(64);
|
||||
|
||||
$verify = new EmailVerification();
|
||||
$verify = new EmailVerification;
|
||||
$verify->user_id = $user->id;
|
||||
$verify->email = $user->email;
|
||||
$verify->user_token = $user->app_register_token;
|
||||
|
@ -1019,14 +1021,20 @@ class ApiV1Dot1Controller extends Controller
|
|||
return $this->json($account, 200, $rateLimiting ? $limits : []);
|
||||
}
|
||||
|
||||
public function getExpoPushNotifications(Request $request)
|
||||
public function getPushState(Request $request)
|
||||
{
|
||||
abort_unless($request->hasHeader('X-PIXELFED-APP'), 404, 'Not found');
|
||||
abort_if(! $request->user() || ! $request->user()->token(), 403);
|
||||
abort_unless($request->user()->tokenCan('push'), 403);
|
||||
abort_unless(config('services.expo.access_token') && strlen(config('services.expo.access_token')) > 10, 404, 'Push notifications are not supported on this server.');
|
||||
abort_unless(NotificationAppGatewayService::enabled(), 404, 'Push notifications are not supported on this server.');
|
||||
$user = $request->user();
|
||||
abort_if($user->status, 422, 'Cannot access this resource at this time');
|
||||
$res = [
|
||||
'expo_token' => (bool) $user->expo_token,
|
||||
'version' => PushNotificationService::PUSH_GATEWAY_VERSION,
|
||||
'username' => (string) $user->username,
|
||||
'profile_id' => (string) $user->profile_id,
|
||||
'notify_enabled' => (bool) $user->notify_enabled,
|
||||
'has_token' => (bool) $user->expo_token,
|
||||
'notify_like' => (bool) $user->notify_like,
|
||||
'notify_follow' => (bool) $user->notify_follow,
|
||||
'notify_mention' => (bool) $user->notify_mention,
|
||||
|
@ -1036,45 +1044,147 @@ class ApiV1Dot1Controller extends Controller
|
|||
return $this->json($res);
|
||||
}
|
||||
|
||||
public function disableExpoPushNotifications(Request $request)
|
||||
public function disablePush(Request $request)
|
||||
{
|
||||
abort_unless($request->hasHeader('X-PIXELFED-APP'), 404, 'Not found');
|
||||
abort_if(! $request->user() || ! $request->user()->token(), 403);
|
||||
abort_unless($request->user()->tokenCan('push'), 403);
|
||||
abort_unless(config('services.expo.access_token') && strlen(config('services.expo.access_token')) > 10, 404, 'Push notifications are not supported on this server.');
|
||||
abort_unless(NotificationAppGatewayService::enabled(), 404, 'Push notifications are not supported on this server.');
|
||||
abort_if($request->user()->status, 422, 'Cannot access this resource at this time');
|
||||
|
||||
$request->user()->update([
|
||||
'notify_enabled' => false,
|
||||
'expo_token' => null,
|
||||
'notify_like' => false,
|
||||
'notify_follow' => false,
|
||||
'notify_mention' => false,
|
||||
'notify_comment' => false,
|
||||
]);
|
||||
|
||||
return $this->json(['expo_token' => null]);
|
||||
PushNotificationService::removeMemberFromAll($request->user()->profile_id);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
return $this->json([
|
||||
'version' => PushNotificationService::PUSH_GATEWAY_VERSION,
|
||||
'username' => (string) $user->username,
|
||||
'profile_id' => (string) $user->profile_id,
|
||||
'notify_enabled' => (bool) $user->notify_enabled,
|
||||
'has_token' => (bool) $user->expo_token,
|
||||
'notify_like' => (bool) $user->notify_like,
|
||||
'notify_follow' => (bool) $user->notify_follow,
|
||||
'notify_mention' => (bool) $user->notify_mention,
|
||||
'notify_comment' => (bool) $user->notify_comment,
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateExpoPushNotifications(Request $request)
|
||||
public function comparePush(Request $request)
|
||||
{
|
||||
abort_unless($request->hasHeader('X-PIXELFED-APP'), 404, 'Not found');
|
||||
abort_if(! $request->user() || ! $request->user()->token(), 403);
|
||||
abort_unless($request->user()->tokenCan('push'), 403);
|
||||
abort_unless(config('services.expo.access_token') && strlen(config('services.expo.access_token')) > 10, 404, 'Push notifications are not supported on this server.');
|
||||
abort_unless(NotificationAppGatewayService::enabled(), 404, 'Push notifications are not supported on this server.');
|
||||
abort_if($request->user()->status, 422, 'Cannot access this resource at this time');
|
||||
|
||||
$this->validate($request, [
|
||||
'expo_token' => ['required', ExpoPushToken::rule()],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
if (empty($user->expo_token)) {
|
||||
return $this->json([
|
||||
'version' => PushNotificationService::PUSH_GATEWAY_VERSION,
|
||||
'username' => (string) $user->username,
|
||||
'profile_id' => (string) $user->profile_id,
|
||||
'notify_enabled' => (bool) $user->notify_enabled,
|
||||
'match' => false,
|
||||
'has_existing' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
$token = $request->input('expo_token');
|
||||
$knownToken = $user->expo_token;
|
||||
$match = hash_equals($knownToken, $token);
|
||||
|
||||
return $this->json([
|
||||
'version' => PushNotificationService::PUSH_GATEWAY_VERSION,
|
||||
'username' => (string) $user->username,
|
||||
'profile_id' => (string) $user->profile_id,
|
||||
'notify_enabled' => (bool) $user->notify_enabled,
|
||||
'match' => $match,
|
||||
'has_existing' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function updatePush(Request $request)
|
||||
{
|
||||
abort_unless($request->hasHeader('X-PIXELFED-APP'), 404, 'Not found');
|
||||
abort_if(! $request->user() || ! $request->user()->token(), 403);
|
||||
abort_unless($request->user()->tokenCan('push'), 403);
|
||||
abort_unless(NotificationAppGatewayService::enabled(), 404, 'Push notifications are not supported on this server.');
|
||||
abort_if($request->user()->status, 422, 'Cannot access this resource at this time');
|
||||
|
||||
$this->validate($request, [
|
||||
'notify_enabled' => 'required',
|
||||
'token' => ['required', ExpoPushToken::rule()],
|
||||
'notify_like' => 'sometimes',
|
||||
'notify_follow' => 'sometimes',
|
||||
'notify_mention' => 'sometimes',
|
||||
'notify_comment' => 'sometimes',
|
||||
]);
|
||||
|
||||
$user = $request->user()->update([
|
||||
'expo_token' => $request->input('expo_token'),
|
||||
'notify_like' => $request->has('notify_like') && $request->boolean('notify_like'),
|
||||
'notify_follow' => $request->has('notify_follow') && $request->boolean('notify_follow'),
|
||||
'notify_mention' => $request->has('notify_mention') && $request->boolean('notify_mention'),
|
||||
'notify_comment' => $request->has('notify_comment') && $request->boolean('notify_comment'),
|
||||
$pid = $request->user()->profile_id;
|
||||
abort_if(! $pid, 422, 'An error occured');
|
||||
$expoToken = $request->input('token');
|
||||
|
||||
$existing = User::where('profile_id', '!=', $pid)->whereExpoToken($expoToken)->count();
|
||||
abort_if($existing, 400, 'Push token is already used by another account');
|
||||
|
||||
$request->user()->update([
|
||||
'notify_enabled' => $request->boolean('notify_enabled'),
|
||||
'expo_token' => $expoToken,
|
||||
]);
|
||||
|
||||
if ($request->filled('notify_like')) {
|
||||
$request->user()->update(['notify_like' => (bool) $request->boolean('notify_like')]);
|
||||
$request->boolean('notify_like') == true ?
|
||||
PushNotificationService::set('like', $pid) :
|
||||
PushNotificationService::removeMember('like', $pid);
|
||||
}
|
||||
if ($request->filled('notify_follow')) {
|
||||
$request->user()->update(['notify_follow' => (bool) $request->boolean('notify_follow')]);
|
||||
$request->boolean('notify_follow') == true ?
|
||||
PushNotificationService::set('follow', $pid) :
|
||||
PushNotificationService::removeMember('follow', $pid);
|
||||
}
|
||||
if ($request->filled('notify_mention')) {
|
||||
$request->user()->update(['notify_mention' => (bool) $request->boolean('notify_mention')]);
|
||||
$request->boolean('notify_mention') == true ?
|
||||
PushNotificationService::set('mention', $pid) :
|
||||
PushNotificationService::removeMember('mention', $pid);
|
||||
}
|
||||
if ($request->filled('notify_comment')) {
|
||||
$request->user()->update(['notify_comment' => (bool) $request->boolean('notify_comment')]);
|
||||
$request->boolean('notify_comment') == true ?
|
||||
PushNotificationService::set('comment', $pid) :
|
||||
PushNotificationService::removeMember('comment', $pid);
|
||||
}
|
||||
|
||||
if ($request->boolean('notify_enabled') == false) {
|
||||
PushNotificationService::removeMemberFromAll($request->user()->profile_id);
|
||||
}
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
$res = [
|
||||
'expo_token' => (bool) $request->user()->expo_token,
|
||||
'notify_like' => (bool) $request->user()->notify_like,
|
||||
'notify_follow' => (bool) $request->user()->notify_follow,
|
||||
'notify_mention' => (bool) $request->user()->notify_mention,
|
||||
'notify_comment' => (bool) $request->user()->notify_comment,
|
||||
'version' => PushNotificationService::PUSH_GATEWAY_VERSION,
|
||||
'notify_enabled' => (bool) $user->notify_enabled,
|
||||
'has_token' => (bool) $user->expo_token,
|
||||
'notify_like' => (bool) $user->notify_like,
|
||||
'notify_follow' => (bool) $user->notify_follow,
|
||||
'notify_mention' => (bool) $user->notify_mention,
|
||||
'notify_comment' => (bool) $user->notify_comment,
|
||||
];
|
||||
|
||||
return $this->json($res);
|
||||
|
@ -1203,7 +1313,7 @@ class ApiV1Dot1Controller extends Controller
|
|||
abort(500, 'An error occured.');
|
||||
}
|
||||
|
||||
$media = new Media();
|
||||
$media = new Media;
|
||||
$media->status_id = $status->id;
|
||||
$media->profile_id = $profile->id;
|
||||
$media->user_id = $user->id;
|
||||
|
@ -1252,4 +1362,13 @@ class ApiV1Dot1Controller extends Controller
|
|||
|
||||
return $this->json($res);
|
||||
}
|
||||
|
||||
public function nagState(Request $request)
|
||||
{
|
||||
abort_unless((bool) config_cache('pixelfed.oauth_enabled'), 404);
|
||||
|
||||
return [
|
||||
'active' => NotificationAppGatewayService::enabled(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,13 @@
|
|||
namespace App\Jobs\FollowPipeline;
|
||||
|
||||
use App\Follower;
|
||||
use App\Jobs\PushNotificationPipeline\FollowPushNotifyPipeline;
|
||||
use App\Notification;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\FollowerService;
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use App\Services\PushNotificationService;
|
||||
use App\User;
|
||||
use Cache;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
@ -11,9 +17,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Log;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\FollowerService;
|
||||
|
||||
class FollowPipeline implements ShouldQueue
|
||||
{
|
||||
|
@ -49,16 +52,16 @@ class FollowPipeline implements ShouldQueue
|
|||
$actor = $follower->actor;
|
||||
$target = $follower->target;
|
||||
|
||||
if(!$actor || !$target) {
|
||||
if (! $actor || ! $target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if($target->domain || !$target->private_key) {
|
||||
if ($target->domain || ! $target->private_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
Cache::forget('profile:following:' . $actor->id);
|
||||
Cache::forget('profile:following:' . $target->id);
|
||||
Cache::forget('profile:following:'.$actor->id);
|
||||
Cache::forget('profile:following:'.$target->id);
|
||||
|
||||
FollowerService::add($actor->id, $target->id);
|
||||
|
||||
|
@ -72,9 +75,9 @@ class FollowPipeline implements ShouldQueue
|
|||
$target->save();
|
||||
AccountService::del($target->id);
|
||||
|
||||
if($target->user_id && $target->domain === null) {
|
||||
if ($target->user_id && $target->domain === null) {
|
||||
try {
|
||||
$notification = new Notification();
|
||||
$notification = new Notification;
|
||||
$notification->profile_id = $target->id;
|
||||
$notification->actor_id = $actor->id;
|
||||
$notification->action = 'follow';
|
||||
|
@ -84,6 +87,15 @@ class FollowPipeline implements ShouldQueue
|
|||
} catch (Exception $e) {
|
||||
Log::error($e);
|
||||
}
|
||||
|
||||
if (NotificationAppGatewayService::enabled()) {
|
||||
if (PushNotificationService::check('follow', $target->id)) {
|
||||
$user = User::whereProfileId($target->id)->first();
|
||||
if ($user && $user->expo_token && $user->notify_enabled) {
|
||||
FollowPushNotifyPipeline::dispatch($user->expo_token, $actor->username)->onQueue('pushnotify');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,19 +2,22 @@
|
|||
|
||||
namespace App\Jobs\LikePipeline;
|
||||
|
||||
use Cache, DB, Log;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\{Like, Notification};
|
||||
use App\Jobs\PushNotificationPipeline\LikePushNotifyPipeline;
|
||||
use App\Like;
|
||||
use App\Notification;
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use App\Services\PushNotificationService;
|
||||
use App\Services\StatusService;
|
||||
use App\Transformer\ActivityPub\Verb\Like as LikeTransformer;
|
||||
use App\User;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use App\Transformer\ActivityPub\Verb\Like as LikeTransformer;
|
||||
use App\Services\StatusService;
|
||||
|
||||
class LikePipeline implements ShouldQueue
|
||||
{
|
||||
|
@ -30,6 +33,7 @@ class LikePipeline implements ShouldQueue
|
|||
public $deleteWhenMissingModels = true;
|
||||
|
||||
public $timeout = 5;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
/**
|
||||
|
@ -54,34 +58,31 @@ class LikePipeline implements ShouldQueue
|
|||
$status = $this->like->status;
|
||||
$actor = $this->like->actor;
|
||||
|
||||
if (!$status) {
|
||||
if (! $status) {
|
||||
// Ignore notifications to deleted statuses
|
||||
return;
|
||||
}
|
||||
|
||||
$status->likes_count = DB::table('likes')->whereStatusId($status->id)->count();
|
||||
$status->save();
|
||||
|
||||
StatusService::refresh($status->id);
|
||||
|
||||
if($status->url && $actor->domain == null) {
|
||||
if ($status->url && $actor->domain == null) {
|
||||
return $this->remoteLikeDeliver();
|
||||
}
|
||||
|
||||
$exists = Notification::whereProfileId($status->profile_id)
|
||||
->whereActorId($actor->id)
|
||||
->whereAction('like')
|
||||
->whereItemId($status->id)
|
||||
->whereItemType('App\Status')
|
||||
->count();
|
||||
->whereActorId($actor->id)
|
||||
->whereAction('like')
|
||||
->whereItemId($status->id)
|
||||
->whereItemType('App\Status')
|
||||
->count();
|
||||
|
||||
if ($actor->id === $status->profile_id || $exists !== 0) {
|
||||
if ($actor->id === $status->profile_id || $exists) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if($status->uri === null && $status->object_url === null && $status->url === null) {
|
||||
if ($status->uri === null && $status->object_url === null && $status->url === null) {
|
||||
try {
|
||||
$notification = new Notification();
|
||||
$notification = new Notification;
|
||||
$notification->profile_id = $status->profile_id;
|
||||
$notification->actor_id = $actor->id;
|
||||
$notification->action = 'like';
|
||||
|
@ -91,6 +92,15 @@ class LikePipeline implements ShouldQueue
|
|||
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
if (NotificationAppGatewayService::enabled()) {
|
||||
if (PushNotificationService::check('like', $status->profile_id)) {
|
||||
$user = User::whereProfileId($status->profile_id)->first();
|
||||
if ($user && $user->expo_token && $user->notify_enabled) {
|
||||
LikePushNotifyPipeline::dispatchSync($user->expo_token, $actor->username);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,9 +110,9 @@ class LikePipeline implements ShouldQueue
|
|||
$status = $this->like->status;
|
||||
$actor = $this->like->actor;
|
||||
|
||||
$fractal = new Fractal\Manager();
|
||||
$fractal->setSerializer(new ArraySerializer());
|
||||
$resource = new Fractal\Resource\Item($like, new LikeTransformer());
|
||||
$fractal = new Fractal\Manager;
|
||||
$fractal->setSerializer(new ArraySerializer);
|
||||
$resource = new Fractal\Resource\Item($like, new LikeTransformer);
|
||||
$activity = $fractal->createData($resource)->toArray();
|
||||
|
||||
$url = $status->profile->sharedInbox ?? $status->profile->inbox_url;
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\PushNotificationPipeline;
|
||||
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
|
||||
class FollowPushNotifyPipeline implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $pushToken;
|
||||
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($pushToken, $actor)
|
||||
{
|
||||
$this->pushToken = $pushToken;
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
NotificationAppGatewayService::send($this->pushToken, 'follow', $this->actor);
|
||||
} catch (Exception $e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
38
app/Jobs/PushNotificationPipeline/LikePushNotifyPipeline.php
Normal file
38
app/Jobs/PushNotificationPipeline/LikePushNotifyPipeline.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\PushNotificationPipeline;
|
||||
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
|
||||
class LikePushNotifyPipeline implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $pushToken;
|
||||
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($pushToken, $actor)
|
||||
{
|
||||
$this->pushToken = $pushToken;
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
NotificationAppGatewayService::send($this->pushToken, 'like', $this->actor);
|
||||
} catch (Exception $e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\PushNotificationPipeline;
|
||||
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
|
||||
class MentionPushNotifyPipeline implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $pushToken;
|
||||
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($pushToken, $actor)
|
||||
{
|
||||
$this->pushToken = $pushToken;
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
NotificationAppGatewayService::send($this->pushToken, 'mention', $this->actor);
|
||||
} catch (Exception $e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
125
app/Services/NotificationAppGatewayService.php
Normal file
125
app/Services/NotificationAppGatewayService.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Cache;
|
||||
use Exception;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class NotificationAppGatewayService
|
||||
{
|
||||
const GATEWAY_SUPPORT_CHECK = 'px:nags:gateway-support-check';
|
||||
|
||||
public static function config()
|
||||
{
|
||||
return config('instance.notifications.nag');
|
||||
}
|
||||
|
||||
public static function enabled()
|
||||
{
|
||||
if ((bool) config('instance.notifications.nag.enabled') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$apiKey = config('instance.notifications.nag.api_key');
|
||||
if (! $apiKey || empty($apiKey) || strlen($apiKey) !== 45) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Cache::remember(self::GATEWAY_SUPPORT_CHECK, 43200, function () {
|
||||
return self::checkServerSupport();
|
||||
});
|
||||
}
|
||||
|
||||
public static function checkServerSupport()
|
||||
{
|
||||
$endpoint = 'https://'.config('instance.notifications.nag.endpoint').'/api/v1/instance-check?domain='.config('pixelfed.domain.app');
|
||||
try {
|
||||
$res = Http::withHeaders(['X-PIXELFED-API' => 1])
|
||||
->retry(3, 500)
|
||||
->throw()
|
||||
->get($endpoint);
|
||||
|
||||
$data = $res->json();
|
||||
} catch (RequestException $e) {
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($res->successful() && isset($data['active']) && $data['active'] === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function forceSupportRecheck()
|
||||
{
|
||||
Cache::forget(self::GATEWAY_SUPPORT_CHECK);
|
||||
|
||||
return self::enabled();
|
||||
}
|
||||
|
||||
public static function isValidExpoPushToken($token)
|
||||
{
|
||||
if (! $token || empty($token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (str_starts_with($token, 'ExponentPushToken[') && mb_strlen($token) < 26) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! str_starts_with($token, 'ExponentPushToken[') && ! str_starts_with($token, 'ExpoPushToken[')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! str_ends_with($token, ']')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function send($userToken, $type, $actor = '')
|
||||
{
|
||||
if (! self::enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $userToken || empty($userToken) || ! self::isValidExpoPushToken($userToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$types = PushNotificationService::NOTIFY_TYPES;
|
||||
|
||||
if (! $type || empty($type) || ! in_array($type, $types)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$apiKey = config('instance.notifications.nag.api_key');
|
||||
|
||||
if (! $apiKey || empty($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
$url = 'https://'.config('instance.notifications.nag.endpoint').'/api/v1/relay/deliver';
|
||||
|
||||
try {
|
||||
$response = Http::withToken($apiKey)
|
||||
->withHeaders(['X-PIXELFED-API' => 1])
|
||||
->post($url, [
|
||||
'token' => $userToken,
|
||||
'type' => $type,
|
||||
'actor' => $actor,
|
||||
]);
|
||||
|
||||
$response->throw();
|
||||
} catch (RequestException $e) {
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
123
app/Services/PushNotificationService.php
Normal file
123
app/Services/PushNotificationService.php
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\User;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Log;
|
||||
|
||||
class PushNotificationService
|
||||
{
|
||||
public const ACTIVE_LIST_KEY = 'pf:services:push-notify:active_deliver:';
|
||||
|
||||
public const NOTIFY_TYPES = ['follow', 'like', 'mention', 'comment'];
|
||||
|
||||
public const DEEP_CHECK_KEY = 'pf:services:push-notify:deep-check:';
|
||||
|
||||
public const PUSH_GATEWAY_VERSION = '1.0';
|
||||
|
||||
public const LOTTERY_ODDS = 20;
|
||||
|
||||
public const CACHE_LOCK_SECONDS = 10;
|
||||
|
||||
public static function get($list)
|
||||
{
|
||||
return Redis::smembers(self::ACTIVE_LIST_KEY.$list);
|
||||
}
|
||||
|
||||
public static function set($listId, $memberId)
|
||||
{
|
||||
if (! in_array($listId, self::NOTIFY_TYPES)) {
|
||||
return false;
|
||||
}
|
||||
$user = User::whereProfileId($memberId)->first();
|
||||
if (! $user || $user->status || $user->deleted_at) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Redis::sadd(self::ACTIVE_LIST_KEY.$listId, $memberId);
|
||||
}
|
||||
|
||||
public static function check($listId, $memberId)
|
||||
{
|
||||
return random_int(1, self::LOTTERY_ODDS) === 1
|
||||
? self::isMemberDeepCheck($listId, $memberId)
|
||||
: self::isMember($listId, $memberId);
|
||||
}
|
||||
|
||||
public static function isMember($listId, $memberId)
|
||||
{
|
||||
try {
|
||||
return Redis::sismember(self::ACTIVE_LIST_KEY.$listId, $memberId);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function isMemberDeepCheck($listId, $memberId)
|
||||
{
|
||||
$lock = Cache::lock(self::DEEP_CHECK_KEY.$listId, self::CACHE_LOCK_SECONDS);
|
||||
|
||||
try {
|
||||
$lock->block(5);
|
||||
$actualCount = User::whereNull('status')->where('notify_enabled', true)->where('notify_'.$listId, true)->count();
|
||||
$cachedCount = self::count($listId);
|
||||
if ($actualCount != $cachedCount) {
|
||||
self::warmList($listId);
|
||||
$user = User::where('notify_enabled', true)->where('profile_id', $memberId)->first();
|
||||
|
||||
return $user ? (bool) $user->{"notify_{$listId}"} : false;
|
||||
} else {
|
||||
return self::isMember($listId, $memberId);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::error('Failed during deep membership check: '.$e->getMessage());
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
optional($lock)->release();
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeMember($listId, $memberId)
|
||||
{
|
||||
return Redis::srem(self::ACTIVE_LIST_KEY.$listId, $memberId);
|
||||
}
|
||||
|
||||
public static function removeMemberFromAll($memberId)
|
||||
{
|
||||
foreach (self::NOTIFY_TYPES as $type) {
|
||||
self::removeMember($type, $memberId);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static function count($listId)
|
||||
{
|
||||
if (! in_array($listId, self::NOTIFY_TYPES)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Redis::scard(self::ACTIVE_LIST_KEY.$listId);
|
||||
}
|
||||
|
||||
public static function warmList($listId)
|
||||
{
|
||||
if (! in_array($listId, self::NOTIFY_TYPES)) {
|
||||
return false;
|
||||
}
|
||||
$key = self::ACTIVE_LIST_KEY.$listId;
|
||||
Redis::del($key);
|
||||
foreach (User::where('notify_'.$listId, true)->cursor() as $acct) {
|
||||
if ($acct->status || $acct->deleted_at || ! $acct->profile_id || ! $acct->notify_enabled) {
|
||||
continue;
|
||||
}
|
||||
Redis::sadd($key, $acct->profile_id);
|
||||
}
|
||||
|
||||
return self::count($listId);
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Passport\HasApiTokens;
|
||||
use NotificationChannels\Expo\ExpoPushToken;
|
||||
use NotificationChannels\WebPush\HasPushSubscriptions;
|
||||
|
||||
class User extends Authenticatable
|
||||
|
@ -46,6 +45,7 @@ class User extends Authenticatable
|
|||
'last_active_at',
|
||||
'register_source',
|
||||
'expo_token',
|
||||
'notify_enabled',
|
||||
'notify_like',
|
||||
'notify_follow',
|
||||
'notify_mention',
|
||||
|
|
|
@ -15,6 +15,7 @@ use App\Jobs\MovePipeline\MoveMigrateFollowersPipeline;
|
|||
use App\Jobs\MovePipeline\ProcessMovePipeline;
|
||||
use App\Jobs\MovePipeline\UnfollowLegacyAccountMovePipeline;
|
||||
use App\Jobs\ProfilePipeline\HandleUpdateActivity;
|
||||
use App\Jobs\PushNotificationPipeline\MentionPushNotifyPipeline;
|
||||
use App\Jobs\StatusPipeline\RemoteStatusDelete;
|
||||
use App\Jobs\StatusPipeline\StatusRemoteUpdatePipeline;
|
||||
use App\Jobs\StoryPipeline\StoryExpire;
|
||||
|
@ -27,7 +28,9 @@ use App\Notification;
|
|||
use App\Profile;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\FollowerService;
|
||||
use App\Services\NotificationAppGatewayService;
|
||||
use App\Services\PollService;
|
||||
use App\Services\PushNotificationService;
|
||||
use App\Services\ReblogService;
|
||||
use App\Services\UserFilterService;
|
||||
use App\Status;
|
||||
|
@ -242,7 +245,7 @@ class Inbox
|
|||
$cc = isset($activity['cc']) ? $activity['cc'] : [];
|
||||
|
||||
if ($activity['type'] == 'Question') {
|
||||
$this->handlePollCreate();
|
||||
//$this->handlePollCreate();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -531,6 +534,15 @@ class Inbox
|
|||
$notification->item_id = $dm->id;
|
||||
$notification->item_type = "App\DirectMessage";
|
||||
$notification->save();
|
||||
|
||||
if (NotificationAppGatewayService::enabled()) {
|
||||
if (PushNotificationService::check('mention', $profile->id)) {
|
||||
$user = User::whereProfileId($profile->id)->first();
|
||||
if ($user && $user->expo_token && $user->notify_enabled) {
|
||||
MentionPushNotifyPipeline::dispatch($user->expo_token, $actor->username)->onQueue('pushnotify');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ return [
|
|||
'redis:adelete' => 30,
|
||||
'redis:groups' => 30,
|
||||
'redis:move' => 30,
|
||||
'redis:pushnotify' => 30,
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -176,7 +177,7 @@ return [
|
|||
'production' => [
|
||||
'supervisor-1' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo', 'intbg', 'groups', 'adelete', 'move'],
|
||||
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo', 'intbg', 'groups', 'adelete', 'move', 'pushnotify'],
|
||||
'balance' => env('HORIZON_BALANCE_STRATEGY', 'auto'),
|
||||
'minProcesses' => env('HORIZON_MIN_PROCESSES', 1),
|
||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 20),
|
||||
|
@ -190,7 +191,7 @@ return [
|
|||
'local' => [
|
||||
'supervisor-1' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo', 'intbg', 'groups', 'adelete', 'move'],
|
||||
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo', 'intbg', 'groups', 'adelete', 'move', 'pushnotify'],
|
||||
'balance' => 'auto',
|
||||
'minProcesses' => 1,
|
||||
'maxProcesses' => 20,
|
||||
|
|
|
@ -151,6 +151,12 @@ return [
|
|||
'enabled' => env('INSTANCE_NOTIFY_AUTO_GC', false),
|
||||
'delete_after_days' => env('INSTANCE_NOTIFY_AUTO_GC_DEL_AFTER_DAYS', 365),
|
||||
],
|
||||
|
||||
'nag' => [
|
||||
'enabled' => (bool) env('INSTANCE_NOTIFY_APP_GATEWAY', true),
|
||||
'api_key' => env('PIXELFED_PUSHGATEWAY_KEY', false),
|
||||
'endpoint' => 'push.pixelfed.net',
|
||||
],
|
||||
],
|
||||
|
||||
'curated_registration' => [
|
||||
|
@ -171,6 +177,7 @@ return [
|
|||
'enabled' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY', false),
|
||||
'bundle' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_BUNDLE', false),
|
||||
'max_per_day' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_MPD', 10),
|
||||
'cc_addresses' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_CC'),
|
||||
],
|
||||
'on_user_response' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_USER_RESPONSE', false),
|
||||
],
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('notify_enabled')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('notify_enabled');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,10 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Middleware\DeprecatedEndpoint;
|
||||
use App\Http\Controllers\Api\V1\TagsController;
|
||||
|
||||
$middleware = ['auth:api','validemail'];
|
||||
$middleware = ['auth:api', 'validemail'];
|
||||
|
||||
Route::post('/f/inbox', 'FederationController@sharedInbox');
|
||||
Route::post('/users/{username}/inbox', 'FederationController@userInbox');
|
||||
|
@ -20,7 +18,7 @@ Route::redirect('.well-known/change-password', '/settings/password');
|
|||
Route::get('api/nodeinfo/2.0.json', 'FederationController@nodeinfo');
|
||||
Route::get('api/service/health-check', 'HealthCheckController@get');
|
||||
|
||||
Route::prefix('api/v0/groups')->middleware($middleware)->group(function() {
|
||||
Route::prefix('api/v0/groups')->middleware($middleware)->group(function () {
|
||||
Route::get('config', 'Groups\GroupsApiController@getConfig');
|
||||
Route::post('permission/create', 'Groups\CreateGroupsController@checkCreatePermission');
|
||||
Route::post('create', 'Groups\CreateGroupsController@storeGroup');
|
||||
|
@ -87,9 +85,9 @@ Route::prefix('api/v0/groups')->middleware($middleware)->group(function() {
|
|||
Route::get('{id}', 'GroupController@getGroup');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'api'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'api'], function () use ($middleware) {
|
||||
|
||||
Route::group(['prefix' => 'v1'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'v1'], function () use ($middleware) {
|
||||
Route::post('apps', 'Api\ApiV1Controller@apps');
|
||||
Route::get('apps/verify_credentials', 'Api\ApiV1Controller@getApp')->middleware($middleware);
|
||||
Route::get('instance', 'Api\ApiV1Controller@instance');
|
||||
|
@ -170,7 +168,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::get('statuses/{id}/history', 'StatusEditController@history')->middleware($middleware);
|
||||
Route::put('statuses/{id}', 'StatusEditController@store')->middleware($middleware);
|
||||
|
||||
Route::group(['prefix' => 'admin'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'admin'], function () use ($middleware) {
|
||||
Route::get('domain_blocks', 'Api\V1\Admin\DomainBlocksController@index')->middleware($middleware);
|
||||
Route::post('domain_blocks', 'Api\V1\Admin\DomainBlocksController@create')->middleware($middleware);
|
||||
Route::get('domain_blocks/{id}', 'Api\V1\Admin\DomainBlocksController@show')->middleware($middleware);
|
||||
|
@ -179,17 +177,17 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
})->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'v2'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'v2'], function () use ($middleware) {
|
||||
Route::get('search', 'Api\ApiV2Controller@search')->middleware($middleware);
|
||||
Route::post('media', 'Api\ApiV2Controller@mediaUploadV2')->middleware($middleware);
|
||||
Route::get('streaming/config', 'Api\ApiV2Controller@getWebsocketConfig');
|
||||
Route::get('instance', 'Api\ApiV2Controller@instance');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'v1.1'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'v1.1'], function () use ($middleware) {
|
||||
Route::post('report', 'Api\ApiV1Dot1Controller@report')->middleware($middleware);
|
||||
|
||||
Route::group(['prefix' => 'accounts'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'accounts'], function () use ($middleware) {
|
||||
Route::get('timelines/home', 'Api\ApiV1Controller@timelineHome')->middleware($middleware);
|
||||
Route::delete('avatar', 'Api\ApiV1Dot1Controller@deleteAvatar')->middleware($middleware);
|
||||
Route::get('{id}/posts', 'Api\ApiV1Dot1Controller@accountPosts')->middleware($middleware);
|
||||
|
@ -202,7 +200,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::get('username/{username}', 'Api\ApiV1Dot1Controller@accountUsernameToId')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'collections'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'collections'], function () use ($middleware) {
|
||||
Route::get('accounts/{id}', 'CollectionController@getUserCollections')->middleware($middleware);
|
||||
Route::get('items/{id}', 'CollectionController@getItems')->middleware($middleware);
|
||||
Route::get('view/{id}', 'CollectionController@getCollection')->middleware($middleware);
|
||||
|
@ -213,7 +211,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::get('self', 'CollectionController@getSelfCollections')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'direct'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'direct'], function () use ($middleware) {
|
||||
Route::get('thread', 'DirectMessageController@thread')->middleware($middleware);
|
||||
Route::post('thread/send', 'DirectMessageController@create')->middleware($middleware);
|
||||
Route::delete('thread/message', 'DirectMessageController@delete')->middleware($middleware);
|
||||
|
@ -224,17 +222,17 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::post('lookup', 'DirectMessageController@composeLookup')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'archive'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'archive'], function () use ($middleware) {
|
||||
Route::post('add/{id}', 'Api\ApiV1Dot1Controller@archive')->middleware($middleware);
|
||||
Route::post('remove/{id}', 'Api\ApiV1Dot1Controller@unarchive')->middleware($middleware);
|
||||
Route::get('list', 'Api\ApiV1Dot1Controller@archivedPosts')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'places'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'places'], function () use ($middleware) {
|
||||
Route::get('posts/{id}/{slug}', 'Api\ApiV1Dot1Controller@placesById')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'stories'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'stories'], function () use ($middleware) {
|
||||
Route::get('carousel', 'Stories\StoryApiV1Controller@carousel')->middleware($middleware);
|
||||
Route::post('add', 'Stories\StoryApiV1Controller@add')->middleware($middleware);
|
||||
Route::post('publish', 'Stories\StoryApiV1Controller@publish')->middleware($middleware);
|
||||
|
@ -243,23 +241,23 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::post('comment', 'Stories\StoryApiV1Controller@comment')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'compose'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'compose'], function () use ($middleware) {
|
||||
Route::get('search/location', 'ComposeController@searchLocation')->middleware($middleware);
|
||||
Route::get('settings', 'ComposeController@composeSettings')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'discover'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'discover'], function () use ($middleware) {
|
||||
Route::get('accounts/popular', 'Api\ApiV1Controller@discoverAccountsPopular')->middleware($middleware);
|
||||
Route::get('posts/trending', 'DiscoverController@trendingApi')->middleware($middleware);
|
||||
Route::get('posts/hashtags', 'DiscoverController@trendingHashtags')->middleware($middleware);
|
||||
Route::get('posts/network/trending', 'DiscoverController@discoverNetworkTrending')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'directory'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'directory'], function () {
|
||||
Route::get('listing', 'PixelfedDirectoryController@get');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'auth'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'auth'], function () {
|
||||
Route::get('iarpfc', 'Api\ApiV1Dot1Controller@inAppRegistrationPreFlightCheck');
|
||||
Route::post('iar', 'Api\ApiV1Dot1Controller@inAppRegistration');
|
||||
Route::post('iarc', 'Api\ApiV1Dot1Controller@inAppRegistrationConfirm');
|
||||
|
@ -270,16 +268,18 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::post('invite/admin/ec', 'AdminInviteController@apiEmailCheck')->middleware('throttle:10,1440');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'expo'], function() use($middleware) {
|
||||
Route::get('push-notifications', 'Api\ApiV1Dot1Controller@getExpoPushNotifications')->middleware($middleware);
|
||||
Route::post('push-notifications/update', 'Api\ApiV1Dot1Controller@updateExpoPushNotifications')->middleware($middleware);
|
||||
Route::post('push-notifications/disable', 'Api\ApiV1Dot1Controller@disableExpoPushNotifications')->middleware($middleware);
|
||||
Route::group(['prefix' => 'push'], function () use ($middleware) {
|
||||
Route::get('state', 'Api\ApiV1Dot1Controller@getPushState')->middleware($middleware);
|
||||
Route::post('compare', 'Api\ApiV1Dot1Controller@comparePush')->middleware($middleware);
|
||||
Route::post('update', 'Api\ApiV1Dot1Controller@updatePush')->middleware($middleware);
|
||||
Route::post('disable', 'Api\ApiV1Dot1Controller@disablePush')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::post('status/create', 'Api\ApiV1Dot1Controller@statusCreate')->middleware($middleware);
|
||||
Route::get('nag/state', 'Api\ApiV1Dot1Controller@nagState');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'live'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'live'], function () {
|
||||
// Route::post('create_stream', 'LiveStreamController@createStream')->middleware($middleware);
|
||||
// Route::post('stream/edit', 'LiveStreamController@editStream')->middleware($middleware);
|
||||
// Route::get('active/list', 'LiveStreamController@getActiveStreams')->middleware($middleware);
|
||||
|
@ -297,7 +297,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
// Route::post('broadcast/finish', 'LiveStreamController@clientBroadcastFinish')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'admin'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'admin'], function () use ($middleware) {
|
||||
Route::post('moderate/post/{id}', 'Api\ApiV1Dot1Controller@moderatePost')->middleware($middleware);
|
||||
Route::get('supported', 'Api\AdminApiController@supported')->middleware($middleware);
|
||||
Route::get('stats', 'Api\AdminApiController@getStats')->middleware($middleware);
|
||||
|
@ -318,15 +318,15 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::get('instance/stats', 'Api\AdminApiController@getAllStats')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'landing/v1'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'landing/v1'], function () {
|
||||
Route::get('directory', 'LandingController@getDirectoryApi');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'pixelfed'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'v1'], function() use($middleware) {
|
||||
Route::group(['prefix' => 'pixelfed'], function () use ($middleware) {
|
||||
Route::group(['prefix' => 'v1'], function () use ($middleware) {
|
||||
Route::post('report', 'Api\ApiV1Dot1Controller@report')->middleware($middleware);
|
||||
|
||||
Route::group(['prefix' => 'accounts'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'accounts'], function () use ($middleware) {
|
||||
Route::get('timelines/home', 'Api\ApiV1Controller@timelineHome')->middleware($middleware);
|
||||
Route::delete('avatar', 'Api\ApiV1Dot1Controller@deleteAvatar')->middleware($middleware);
|
||||
Route::get('{id}/posts', 'Api\ApiV1Dot1Controller@accountPosts')->middleware($middleware);
|
||||
|
@ -337,13 +337,13 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::get('apps-and-applications', 'Api\ApiV1Dot1Controller@accountApps')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'archive'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'archive'], function () use ($middleware) {
|
||||
Route::post('add/{id}', 'Api\ApiV1Dot1Controller@archive')->middleware($middleware);
|
||||
Route::post('remove/{id}', 'Api\ApiV1Dot1Controller@unarchive')->middleware($middleware);
|
||||
Route::get('list', 'Api\ApiV1Dot1Controller@archivedPosts')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'collections'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'collections'], function () use ($middleware) {
|
||||
Route::get('accounts/{id}', 'CollectionController@getUserCollections')->middleware($middleware);
|
||||
Route::get('items/{id}', 'CollectionController@getItems')->middleware($middleware);
|
||||
Route::get('view/{id}', 'CollectionController@getCollection')->middleware($middleware);
|
||||
|
@ -354,12 +354,12 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::get('self', 'CollectionController@getSelfCollections')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'compose'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'compose'], function () use ($middleware) {
|
||||
Route::get('search/location', 'ComposeController@searchLocation')->middleware($middleware);
|
||||
Route::get('settings', 'ComposeController@composeSettings')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'direct'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'direct'], function () use ($middleware) {
|
||||
Route::get('thread', 'DirectMessageController@thread')->middleware($middleware);
|
||||
Route::post('thread/send', 'DirectMessageController@create')->middleware($middleware);
|
||||
Route::delete('thread/message', 'DirectMessageController@delete')->middleware($middleware);
|
||||
|
@ -370,17 +370,17 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::post('lookup', 'DirectMessageController@composeLookup')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'discover'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'discover'], function () use ($middleware) {
|
||||
Route::get('accounts/popular', 'Api\ApiV1Controller@discoverAccountsPopular')->middleware($middleware);
|
||||
Route::get('posts/trending', 'DiscoverController@trendingApi')->middleware($middleware);
|
||||
Route::get('posts/hashtags', 'DiscoverController@trendingHashtags')->middleware($middleware);
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'directory'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'directory'], function () {
|
||||
Route::get('listing', 'PixelfedDirectoryController@get');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'places'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'places'], function () use ($middleware) {
|
||||
Route::get('posts/{id}/{slug}', 'Api\ApiV1Dot1Controller@placesById')->middleware($middleware);
|
||||
});
|
||||
|
||||
|
@ -389,7 +389,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
Route::get('app/settings', 'UserAppSettingsController@get')->middleware($middleware);
|
||||
Route::post('app/settings', 'UserAppSettingsController@store')->middleware($middleware);
|
||||
|
||||
Route::group(['prefix' => 'stories'], function () use($middleware) {
|
||||
Route::group(['prefix' => 'stories'], function () use ($middleware) {
|
||||
Route::get('carousel', 'Stories\StoryApiV1Controller@carousel')->middleware($middleware);
|
||||
Route::get('self-carousel', 'Stories\StoryApiV1Controller@selfCarousel')->middleware($middleware);
|
||||
Route::post('add', 'Stories\StoryApiV1Controller@add')->middleware($middleware);
|
||||
|
|
Loading…
Reference in a new issue