Pusher service
The Pusher service file provides the implementation of internal notifications using the Pusher service. Pusher enables real-time communication and facilitates the establishment of private channels for users to interact with each other and receive notifications in real time. It's important to note that in order to use Pusher, developers need to set up an account and configure Pusher on pusher.com.
Pusher Advantages
- Real-time communication: Pusher allows users to establish private channels and enables real-time communication between users and the backend. This can be utilized for various purposes, such as user-to-user communication and delivering notifications instantly.
Pusher Limitations
- Inability to fetch old events: One limitation of Pusher is that it does not have the ability to fetch old events. It can only listen to live events triggered after establishing a connection. This means that if a user is offline during an event trigger, they won't receive the event when they come back online.
- Handling historical data: Storing historical data locally can be a challenge when using Pusher, especially in scenarios where users uninstall and reinstall the app or use the app on multiple devices. If historical data is required, it is recommended to implement a backend solution to store events in a cloud database prior to sending them via Pusher.
For further information and guidance on Pusher, refer to the official Pusher documentation and support resources provided by Pusher at Pusher.
- Pusher can only trigger events on public channels from the client side. It cannot trigger events on private channels directly.
- To send events on private channels, you need to establish a private channel and configure the necessary authorization process.
- When using a private channel, you need to set up an onAuthorizer function and a corresponding backend implementation.
- The onAuthorizer function sends a request to your backend server to obtain an authorization object, which will be used by Pusher to authenticate the client's access to the private channel.
- By using the onAuthorizer function and backend implementation, you can ensure that only authorized clients can subscribe to and trigger events on private channels. This adds an extra layer of security and control over the communication.
Please note that setting up the authorization process and implementing the backend functionality are essential steps when using private channels with Pusher. Make sure to configure the on Authorizer function and handle the backend logic accordingly to enable secure and authorized communication on private channels.
Please refere
onAuthorizer
and Backend
sample code here.Integration
Developers integrating Pusher-based internal notifications should follow these steps:
- Sign up for a Pusher account and configure Pusher on pusher.com.
- Verify the necessary logic to establish a connection with Pusher and handle user authentication and authorization for private channels.
- Ensure that you handle the management of private channels, event triggers, and event subscriptions within your main internal notifications file, while considering the limitations and challenges mentioned above.
- You might want to setup the user id in private channel to target specific user. When subscribing for perticular user; user id is needed so for that, logic should be implemented by developer in the
init()
method of this class to assign user id in the service, the logic may vary depending on the project. This user ID will be used to establish private channels for the user. - Obtain the Pusher configuration details, such as the API key and cluster, and put it in your environment configuration.
Remember to adapt and modify the provided Pusher service file according to your specific application requirements and architectural design.Please note that the Pusher service file provided here serves as a part of the overall internal notifications system. It should be used within the context of the main internal notifications file, which acts as a central point for integrating different notification services.
Source Code
import 'dart:async';
import 'package:pusher_channels_flutter/pusher_channels_flutter.dart';
import './base_service.dart';
import '../../../../env.dart';
import '../../../logging_library/logging_library.dart';
import '../../models/notification.dart';
class InternalNotificationsWithPusher implements InternalNotificationsService {
late final PusherChannelsFlutter _pusher;
final StreamController<int> _pendingNotificationsCountStreamController =
StreamController<int>.broadcast();
@override
late final Stream<int> pendingNotificationsCountStream;
final StreamController<List<InternalNotification>> _notificationsStreamController =
StreamController<List<InternalNotification>>.broadcast();
@override
late final Stream<List<InternalNotification>> notificationsStream;
List<InternalNotification> _notifications = [];
@override
List<InternalNotification> get notifications => _notifications;
late final String userId;
@override
Future<void> init() async {
pendingNotificationsCountStream = _pendingNotificationsCountStreamController.stream;
notificationsStream = _notificationsStreamController.stream;
// Write your logic here to get user id
userId = 'userId';
final EnvironmentConfig environmentConfig = EnvironmentConfig.getEnvConfig();
if (environmentConfig.pusherConfig == null) return;
_pusher = PusherChannelsFlutter.getInstance();
await _pusher.init(
apiKey: environmentConfig.pusherConfig!.apiKey,
cluster: environmentConfig.pusherConfig!.cluster,
onAuthorizer: (String channelName, String socketId, dynamic options) {
// return {"shared_secret": "11518af14b1d4acbd3b9"};
},
onEvent: (event) {
Log.warning(event);
if (event.data != null) {
_notificationsUpdated([event.data]);
}
},
);
await _pusher.subscribe(channelName: 'private-$userId');
await _pusher.connect(); // Connect with cluster
}
@override
Future<void> dispose() async {}
@override
Future<void> subscribe() async {}
@override
Future<void> unsubscribe() async {}
@override
Future<void> push(List<String> userIds, InternalNotification notification) async {
for (final id in userIds) {
await _pusher.trigger(
PusherEvent(
channelName: 'private-$id',
eventName: 'Internal Notification',
data: notification.toJson(),
),
);
}
}
void _notificationsUpdated(List<Map<String, dynamic>> notifications) {
int count = 0;
final List<InternalNotification> updatedNotifications = [];
for (final jsonNotification in notifications) {
final InternalNotification notification = InternalNotification.fromJson(jsonNotification);
if (!notification.opened) count++;
updatedNotifications.add(notification);
}
_pendingNotificationsCountStreamController.add(count);
_notificationsStreamController.add(updatedNotifications);
_notifications = updatedNotifications;
}
}
onAuthorizer and Backend sample code
1. Client-side Configuration (Dart/Flutter):
_pusher = PusherChannelsFlutter.getInstance();
await _pusher.init(
// Rest of your Pusher configuration...
onAuthorizer: (String channelName, String socketId, dynamic options) async {
final response = await http.post(
'https://your-backend-server.com/pusher/authorize',
body: {
'channelName': channelName,
'socketId': socketId,
},
);
return jsonDecode(response.body);
},
);
2. Backend Configuration
Assuming you have an Express server set up, you'll need to create a route that handles the authorization request and generates the authorization data.
In this example, when the client requests authorization for a private channel, the server receives the request and performs the necessary authorization logic. It generates the authorization data (authData) based on the user's permissions or any other relevant information. The server then uses the Pusher library to generate the authorization signature (auth) required by Pusher. Finally, the server sends the auth data back to the client as a response.
Please note that this is a simplified example, and you'll need to adapt it to your specific backend setup and requirements. Make sure to install the required dependencies (pusher in this case) and configure your Pusher credentials appropriately.
a. Example using Node.js and Express:
const express = require('express');
const Pusher = require('content/vaahflutter/5.directory_structure/3.vaahextendflutter/5.services/2.notification/3.internal/3.services/3.pusher');
const app = express();
// Pusher configuration
const pusher = new Pusher({
appId: 'YOUR_APP_ID',
key: 'YOUR_APP_KEY',
secret: 'YOUR_APP_SECRET',
cluster: 'YOUR_APP_CLUSTER',
});
app.post('/pusher/authorize', async (req, res) => {
const { channelName, socketId } = req.body;
// Implement your authorization logic here
// This can include checking user permissions, validating session, etc.
// Generate the necessary auth data based on your requirements
const authData = {
user_id: '',
user_info: {
name: '',
},
};
const auth = pusher.authenticate(channelName, socketId, authData);
res.send(auth);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
a. Example using PHP:
- Install the Pusher PHP library:
composer require pusher/pusher-php-server
- Create a route and controller in your Laravel application:
- 2 (a). Define the route in routes/web.php:
Route::post('/pusher/authorize', [PusherAuthController::class, 'authorize']);
- 2 (b). Create a new controller using the command:
php artisan make:controller PusherAuthController
- 2 (c). Implement the authorization logic in PusherAuthController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Pusher\Pusher;
class PusherAuthController extends Controller
{
public function authorize(Request $request)
{
$channelName = $request->input('channelName');
$socketId = $request->input('socketId');
// Implement your authorization logic here
// This can include checking user permissions, validating session, etc.
// Generate the necessary auth data based on your requirements
$pusher = new Pusher(
config('broadcasting.connections.pusher.key'),
config('broadcasting.connections.pusher.secret'),
config('broadcasting.connections.pusher.app_id'),
config('broadcasting.connections.pusher.options')
);
$authData = $pusher->socket_auth($channelName, $socketId);
// Send the authorization data as a response
return response()->json($authData);
}
}
- Configure Laravel to use Pusher:
Set your Pusher credentials in the .env file:
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=your-app-id
PUSHER_APP_KEY=your-app-key
PUSHER_APP_SECRET=your-app-secret
PUSHER_APP_CLUSTER=your-app-cluster
Configure the broadcasting settings in config/broadcasting.php:
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
],
],
// Other broadcasting connections...
],