mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-18 19:13:17 +00:00
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\MediaBlocklistService;
|
||||||
use App\Services\MediaPathService;
|
use App\Services\MediaPathService;
|
||||||
use App\Services\NetworkTimelineService;
|
use App\Services\NetworkTimelineService;
|
||||||
|
use App\Services\NotificationAppGatewayService;
|
||||||
use App\Services\ProfileStatusService;
|
use App\Services\ProfileStatusService;
|
||||||
use App\Services\PublicTimelineService;
|
use App\Services\PublicTimelineService;
|
||||||
|
use App\Services\PushNotificationService;
|
||||||
use App\Services\StatusService;
|
use App\Services\StatusService;
|
||||||
use App\Services\UserStorageService;
|
use App\Services\UserStorageService;
|
||||||
use App\Status;
|
use App\Status;
|
||||||
|
@ -54,8 +56,8 @@ class ApiV1Dot1Controller extends Controller
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->fractal = new Fractal\Manager();
|
$this->fractal = new Fractal\Manager;
|
||||||
$this->fractal->setSerializer(new ArraySerializer());
|
$this->fractal->setSerializer(new ArraySerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function json($res, $code = 200, $headers = [])
|
public function json($res, $code = 200, $headers = [])
|
||||||
|
@ -317,7 +319,7 @@ class ApiV1Dot1Controller extends Controller
|
||||||
if (config('pixelfed.bouncer.cloud_ips.ban_signups')) {
|
if (config('pixelfed.bouncer.cloud_ips.ban_signups')) {
|
||||||
abort_if(BouncerService::checkIp($request->ip()), 404);
|
abort_if(BouncerService::checkIp($request->ip()), 404);
|
||||||
}
|
}
|
||||||
$agent = new Agent();
|
$agent = new Agent;
|
||||||
$currentIp = $request->ip();
|
$currentIp = $request->ip();
|
||||||
|
|
||||||
$activity = AccountLog::whereUserId($user->id)
|
$activity = AccountLog::whereUserId($user->id)
|
||||||
|
@ -575,7 +577,7 @@ class ApiV1Dot1Controller extends Controller
|
||||||
|
|
||||||
$rtoken = Str::random(64);
|
$rtoken = Str::random(64);
|
||||||
|
|
||||||
$verify = new EmailVerification();
|
$verify = new EmailVerification;
|
||||||
$verify->user_id = $user->id;
|
$verify->user_id = $user->id;
|
||||||
$verify->email = $user->email;
|
$verify->email = $user->email;
|
||||||
$verify->user_token = $user->app_register_token;
|
$verify->user_token = $user->app_register_token;
|
||||||
|
@ -1019,14 +1021,20 @@ class ApiV1Dot1Controller extends Controller
|
||||||
return $this->json($account, 200, $rateLimiting ? $limits : []);
|
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_if(! $request->user() || ! $request->user()->token(), 403);
|
||||||
abort_unless($request->user()->tokenCan('push'), 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();
|
$user = $request->user();
|
||||||
|
abort_if($user->status, 422, 'Cannot access this resource at this time');
|
||||||
$res = [
|
$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_like' => (bool) $user->notify_like,
|
||||||
'notify_follow' => (bool) $user->notify_follow,
|
'notify_follow' => (bool) $user->notify_follow,
|
||||||
'notify_mention' => (bool) $user->notify_mention,
|
'notify_mention' => (bool) $user->notify_mention,
|
||||||
|
@ -1036,45 +1044,147 @@ class ApiV1Dot1Controller extends Controller
|
||||||
return $this->json($res);
|
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_if(! $request->user() || ! $request->user()->token(), 403);
|
||||||
abort_unless($request->user()->tokenCan('push'), 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([
|
$request->user()->update([
|
||||||
|
'notify_enabled' => false,
|
||||||
'expo_token' => null,
|
'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_if(! $request->user() || ! $request->user()->token(), 403);
|
||||||
abort_unless($request->user()->tokenCan('push'), 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, [
|
$this->validate($request, [
|
||||||
'expo_token' => ['required', ExpoPushToken::rule()],
|
'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_like' => 'sometimes',
|
||||||
'notify_follow' => 'sometimes',
|
'notify_follow' => 'sometimes',
|
||||||
'notify_mention' => 'sometimes',
|
'notify_mention' => 'sometimes',
|
||||||
'notify_comment' => 'sometimes',
|
'notify_comment' => 'sometimes',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = $request->user()->update([
|
$pid = $request->user()->profile_id;
|
||||||
'expo_token' => $request->input('expo_token'),
|
abort_if(! $pid, 422, 'An error occured');
|
||||||
'notify_like' => $request->has('notify_like') && $request->boolean('notify_like'),
|
$expoToken = $request->input('token');
|
||||||
'notify_follow' => $request->has('notify_follow') && $request->boolean('notify_follow'),
|
|
||||||
'notify_mention' => $request->has('notify_mention') && $request->boolean('notify_mention'),
|
$existing = User::where('profile_id', '!=', $pid)->whereExpoToken($expoToken)->count();
|
||||||
'notify_comment' => $request->has('notify_comment') && $request->boolean('notify_comment'),
|
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 = [
|
$res = [
|
||||||
'expo_token' => (bool) $request->user()->expo_token,
|
'version' => PushNotificationService::PUSH_GATEWAY_VERSION,
|
||||||
'notify_like' => (bool) $request->user()->notify_like,
|
'notify_enabled' => (bool) $user->notify_enabled,
|
||||||
'notify_follow' => (bool) $request->user()->notify_follow,
|
'has_token' => (bool) $user->expo_token,
|
||||||
'notify_mention' => (bool) $request->user()->notify_mention,
|
'notify_like' => (bool) $user->notify_like,
|
||||||
'notify_comment' => (bool) $request->user()->notify_comment,
|
'notify_follow' => (bool) $user->notify_follow,
|
||||||
|
'notify_mention' => (bool) $user->notify_mention,
|
||||||
|
'notify_comment' => (bool) $user->notify_comment,
|
||||||
];
|
];
|
||||||
|
|
||||||
return $this->json($res);
|
return $this->json($res);
|
||||||
|
@ -1203,7 +1313,7 @@ class ApiV1Dot1Controller extends Controller
|
||||||
abort(500, 'An error occured.');
|
abort(500, 'An error occured.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$media = new Media();
|
$media = new Media;
|
||||||
$media->status_id = $status->id;
|
$media->status_id = $status->id;
|
||||||
$media->profile_id = $profile->id;
|
$media->profile_id = $profile->id;
|
||||||
$media->user_id = $user->id;
|
$media->user_id = $user->id;
|
||||||
|
@ -1252,4 +1362,13 @@ class ApiV1Dot1Controller extends Controller
|
||||||
|
|
||||||
return $this->json($res);
|
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;
|
namespace App\Jobs\FollowPipeline;
|
||||||
|
|
||||||
use App\Follower;
|
use App\Follower;
|
||||||
|
use App\Jobs\PushNotificationPipeline\FollowPushNotifyPipeline;
|
||||||
use App\Notification;
|
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 Cache;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
@ -11,9 +17,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Log;
|
use Log;
|
||||||
use Illuminate\Support\Facades\Redis;
|
|
||||||
use App\Services\AccountService;
|
|
||||||
use App\Services\FollowerService;
|
|
||||||
|
|
||||||
class FollowPipeline implements ShouldQueue
|
class FollowPipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -74,7 +77,7 @@ class FollowPipeline implements ShouldQueue
|
||||||
|
|
||||||
if ($target->user_id && $target->domain === null) {
|
if ($target->user_id && $target->domain === null) {
|
||||||
try {
|
try {
|
||||||
$notification = new Notification();
|
$notification = new Notification;
|
||||||
$notification->profile_id = $target->id;
|
$notification->profile_id = $target->id;
|
||||||
$notification->actor_id = $actor->id;
|
$notification->actor_id = $actor->id;
|
||||||
$notification->action = 'follow';
|
$notification->action = 'follow';
|
||||||
|
@ -84,6 +87,15 @@ class FollowPipeline implements ShouldQueue
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error($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;
|
namespace App\Jobs\LikePipeline;
|
||||||
|
|
||||||
use Cache, DB, Log;
|
use App\Jobs\PushNotificationPipeline\LikePushNotifyPipeline;
|
||||||
use Illuminate\Support\Facades\Redis;
|
use App\Like;
|
||||||
use App\{Like, Notification};
|
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\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use App\Util\ActivityPub\Helpers;
|
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
use League\Fractal\Serializer\ArraySerializer;
|
use League\Fractal\Serializer\ArraySerializer;
|
||||||
use App\Transformer\ActivityPub\Verb\Like as LikeTransformer;
|
|
||||||
use App\Services\StatusService;
|
|
||||||
|
|
||||||
class LikePipeline implements ShouldQueue
|
class LikePipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -30,6 +33,7 @@ class LikePipeline implements ShouldQueue
|
||||||
public $deleteWhenMissingModels = true;
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
public $timeout = 5;
|
public $timeout = 5;
|
||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,9 +63,6 @@ class LikePipeline implements ShouldQueue
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$status->likes_count = DB::table('likes')->whereStatusId($status->id)->count();
|
|
||||||
$status->save();
|
|
||||||
|
|
||||||
StatusService::refresh($status->id);
|
StatusService::refresh($status->id);
|
||||||
|
|
||||||
if ($status->url && $actor->domain == null) {
|
if ($status->url && $actor->domain == null) {
|
||||||
|
@ -75,13 +76,13 @@ class LikePipeline implements ShouldQueue
|
||||||
->whereItemType('App\Status')
|
->whereItemType('App\Status')
|
||||||
->count();
|
->count();
|
||||||
|
|
||||||
if ($actor->id === $status->profile_id || $exists !== 0) {
|
if ($actor->id === $status->profile_id || $exists) {
|
||||||
return true;
|
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 {
|
try {
|
||||||
$notification = new Notification();
|
$notification = new Notification;
|
||||||
$notification->profile_id = $status->profile_id;
|
$notification->profile_id = $status->profile_id;
|
||||||
$notification->actor_id = $actor->id;
|
$notification->actor_id = $actor->id;
|
||||||
$notification->action = 'like';
|
$notification->action = 'like';
|
||||||
|
@ -91,6 +92,15 @@ class LikePipeline implements ShouldQueue
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} 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;
|
$status = $this->like->status;
|
||||||
$actor = $this->like->actor;
|
$actor = $this->like->actor;
|
||||||
|
|
||||||
$fractal = new Fractal\Manager();
|
$fractal = new Fractal\Manager;
|
||||||
$fractal->setSerializer(new ArraySerializer());
|
$fractal->setSerializer(new ArraySerializer);
|
||||||
$resource = new Fractal\Resource\Item($like, new LikeTransformer());
|
$resource = new Fractal\Resource\Item($like, new LikeTransformer);
|
||||||
$activity = $fractal->createData($resource)->toArray();
|
$activity = $fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
$url = $status->profile->sharedInbox ?? $status->profile->inbox_url;
|
$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\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Laravel\Passport\HasApiTokens;
|
use Laravel\Passport\HasApiTokens;
|
||||||
use NotificationChannels\Expo\ExpoPushToken;
|
|
||||||
use NotificationChannels\WebPush\HasPushSubscriptions;
|
use NotificationChannels\WebPush\HasPushSubscriptions;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
|
@ -46,6 +45,7 @@ class User extends Authenticatable
|
||||||
'last_active_at',
|
'last_active_at',
|
||||||
'register_source',
|
'register_source',
|
||||||
'expo_token',
|
'expo_token',
|
||||||
|
'notify_enabled',
|
||||||
'notify_like',
|
'notify_like',
|
||||||
'notify_follow',
|
'notify_follow',
|
||||||
'notify_mention',
|
'notify_mention',
|
||||||
|
|
|
@ -15,6 +15,7 @@ use App\Jobs\MovePipeline\MoveMigrateFollowersPipeline;
|
||||||
use App\Jobs\MovePipeline\ProcessMovePipeline;
|
use App\Jobs\MovePipeline\ProcessMovePipeline;
|
||||||
use App\Jobs\MovePipeline\UnfollowLegacyAccountMovePipeline;
|
use App\Jobs\MovePipeline\UnfollowLegacyAccountMovePipeline;
|
||||||
use App\Jobs\ProfilePipeline\HandleUpdateActivity;
|
use App\Jobs\ProfilePipeline\HandleUpdateActivity;
|
||||||
|
use App\Jobs\PushNotificationPipeline\MentionPushNotifyPipeline;
|
||||||
use App\Jobs\StatusPipeline\RemoteStatusDelete;
|
use App\Jobs\StatusPipeline\RemoteStatusDelete;
|
||||||
use App\Jobs\StatusPipeline\StatusRemoteUpdatePipeline;
|
use App\Jobs\StatusPipeline\StatusRemoteUpdatePipeline;
|
||||||
use App\Jobs\StoryPipeline\StoryExpire;
|
use App\Jobs\StoryPipeline\StoryExpire;
|
||||||
|
@ -27,7 +28,9 @@ use App\Notification;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use App\Services\AccountService;
|
use App\Services\AccountService;
|
||||||
use App\Services\FollowerService;
|
use App\Services\FollowerService;
|
||||||
|
use App\Services\NotificationAppGatewayService;
|
||||||
use App\Services\PollService;
|
use App\Services\PollService;
|
||||||
|
use App\Services\PushNotificationService;
|
||||||
use App\Services\ReblogService;
|
use App\Services\ReblogService;
|
||||||
use App\Services\UserFilterService;
|
use App\Services\UserFilterService;
|
||||||
use App\Status;
|
use App\Status;
|
||||||
|
@ -242,7 +245,7 @@ class Inbox
|
||||||
$cc = isset($activity['cc']) ? $activity['cc'] : [];
|
$cc = isset($activity['cc']) ? $activity['cc'] : [];
|
||||||
|
|
||||||
if ($activity['type'] == 'Question') {
|
if ($activity['type'] == 'Question') {
|
||||||
$this->handlePollCreate();
|
//$this->handlePollCreate();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -531,6 +534,15 @@ class Inbox
|
||||||
$notification->item_id = $dm->id;
|
$notification->item_id = $dm->id;
|
||||||
$notification->item_type = "App\DirectMessage";
|
$notification->item_type = "App\DirectMessage";
|
||||||
$notification->save();
|
$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:adelete' => 30,
|
||||||
'redis:groups' => 30,
|
'redis:groups' => 30,
|
||||||
'redis:move' => 30,
|
'redis:move' => 30,
|
||||||
|
'redis:pushnotify' => 30,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -176,7 +177,7 @@ return [
|
||||||
'production' => [
|
'production' => [
|
||||||
'supervisor-1' => [
|
'supervisor-1' => [
|
||||||
'connection' => 'redis',
|
'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'),
|
'balance' => env('HORIZON_BALANCE_STRATEGY', 'auto'),
|
||||||
'minProcesses' => env('HORIZON_MIN_PROCESSES', 1),
|
'minProcesses' => env('HORIZON_MIN_PROCESSES', 1),
|
||||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 20),
|
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 20),
|
||||||
|
@ -190,7 +191,7 @@ return [
|
||||||
'local' => [
|
'local' => [
|
||||||
'supervisor-1' => [
|
'supervisor-1' => [
|
||||||
'connection' => 'redis',
|
'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',
|
'balance' => 'auto',
|
||||||
'minProcesses' => 1,
|
'minProcesses' => 1,
|
||||||
'maxProcesses' => 20,
|
'maxProcesses' => 20,
|
||||||
|
|
|
@ -151,6 +151,12 @@ return [
|
||||||
'enabled' => env('INSTANCE_NOTIFY_AUTO_GC', false),
|
'enabled' => env('INSTANCE_NOTIFY_AUTO_GC', false),
|
||||||
'delete_after_days' => env('INSTANCE_NOTIFY_AUTO_GC_DEL_AFTER_DAYS', 365),
|
'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' => [
|
'curated_registration' => [
|
||||||
|
@ -171,6 +177,7 @@ return [
|
||||||
'enabled' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY', false),
|
'enabled' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY', false),
|
||||||
'bundle' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_BUNDLE', 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),
|
'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),
|
'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,7 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Http\Middleware\DeprecatedEndpoint;
|
|
||||||
use App\Http\Controllers\Api\V1\TagsController;
|
use App\Http\Controllers\Api\V1\TagsController;
|
||||||
|
|
||||||
$middleware = ['auth:api', 'validemail'];
|
$middleware = ['auth:api', 'validemail'];
|
||||||
|
@ -255,11 +253,11 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
||||||
Route::get('posts/network/trending', 'DiscoverController@discoverNetworkTrending')->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::get('listing', 'PixelfedDirectoryController@get');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group(['prefix' => 'auth'], function () use($middleware) {
|
Route::group(['prefix' => 'auth'], function () {
|
||||||
Route::get('iarpfc', 'Api\ApiV1Dot1Controller@inAppRegistrationPreFlightCheck');
|
Route::get('iarpfc', 'Api\ApiV1Dot1Controller@inAppRegistrationPreFlightCheck');
|
||||||
Route::post('iar', 'Api\ApiV1Dot1Controller@inAppRegistration');
|
Route::post('iar', 'Api\ApiV1Dot1Controller@inAppRegistration');
|
||||||
Route::post('iarc', 'Api\ApiV1Dot1Controller@inAppRegistrationConfirm');
|
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::post('invite/admin/ec', 'AdminInviteController@apiEmailCheck')->middleware('throttle:10,1440');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group(['prefix' => 'expo'], function() use($middleware) {
|
Route::group(['prefix' => 'push'], function () use ($middleware) {
|
||||||
Route::get('push-notifications', 'Api\ApiV1Dot1Controller@getExpoPushNotifications')->middleware($middleware);
|
Route::get('state', 'Api\ApiV1Dot1Controller@getPushState')->middleware($middleware);
|
||||||
Route::post('push-notifications/update', 'Api\ApiV1Dot1Controller@updateExpoPushNotifications')->middleware($middleware);
|
Route::post('compare', 'Api\ApiV1Dot1Controller@comparePush')->middleware($middleware);
|
||||||
Route::post('push-notifications/disable', 'Api\ApiV1Dot1Controller@disableExpoPushNotifications')->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::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('create_stream', 'LiveStreamController@createStream')->middleware($middleware);
|
||||||
// Route::post('stream/edit', 'LiveStreamController@editStream')->middleware($middleware);
|
// Route::post('stream/edit', 'LiveStreamController@editStream')->middleware($middleware);
|
||||||
// Route::get('active/list', 'LiveStreamController@getActiveStreams')->middleware($middleware);
|
// Route::get('active/list', 'LiveStreamController@getActiveStreams')->middleware($middleware);
|
||||||
|
@ -318,7 +318,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
||||||
Route::get('instance/stats', 'Api\AdminApiController@getAllStats')->middleware($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::get('directory', 'LandingController@getDirectoryApi');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -376,7 +376,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
||||||
Route::get('posts/hashtags', 'DiscoverController@trendingHashtags')->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::get('listing', 'PixelfedDirectoryController@get');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue