mirror of
https://gitlab.com/comunic/comunicapiv3
synced 2025-04-21 03:50:55 +00:00
145 lines
4.3 KiB
Rust
145 lines
4.3 KiB
Rust
//! # Firebase notifications helper
|
|
//!
|
|
//! Helper used to send notifications through the Firebase Cloud Messaging services
|
|
//!
|
|
//! @author Pierre Hubert
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use reqwest::blocking::Client;
|
|
use reqwest::header::{HeaderMap, HeaderValue};
|
|
use serde::Serialize;
|
|
|
|
use crate::constants::FIREBASE_PUSH_MESSAGE_URL;
|
|
use crate::data::api_client::APIClient;
|
|
use crate::data::error::{ExecError, Res};
|
|
use crate::data::push_notification::PushNotification;
|
|
use crate::data::user_token::{PushNotificationToken, UserAccessToken};
|
|
use crate::helpers::api_helper;
|
|
|
|
struct FirebaseClientIdentifier {
|
|
project_name: String,
|
|
authorization_token: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct FirebaseNotification {
|
|
title: String,
|
|
body: String,
|
|
image: Option<String>,
|
|
}
|
|
|
|
|
|
#[derive(Serialize)]
|
|
struct FirebaseAndroidNotification {
|
|
tag: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct FirebaseAndroid {
|
|
notification: FirebaseAndroidNotification,
|
|
ttl: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct FirebaseMessage {
|
|
token: String,
|
|
notification: FirebaseNotification,
|
|
android: FirebaseAndroid,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct FirebaseNotificationRequest {
|
|
validate_only: bool,
|
|
message: FirebaseMessage,
|
|
}
|
|
|
|
/// Get short-lived authorization token for a specific client
|
|
fn get_credentials(client: &APIClient) -> Res<String> {
|
|
let service_account_file = match &client.firebase_service_account_file {
|
|
Some(file) => file,
|
|
None => {
|
|
return Err(ExecError::boxed_string(format!("No service account file for client {}!", client.name)));
|
|
}
|
|
};
|
|
|
|
let token = gouth::Builder::new()
|
|
.scopes(&["https://www.googleapis.com/auth/firebase.messaging"])
|
|
.json(service_account_file)
|
|
.build()?
|
|
.header_value()?
|
|
.to_string();
|
|
|
|
Ok(token)
|
|
}
|
|
|
|
/// Send a single notification through Firebase service
|
|
fn send_notification(n: &PushNotification, client_token: &str, access: &FirebaseClientIdentifier) -> Res {
|
|
let notif = FirebaseNotificationRequest {
|
|
validate_only: false,
|
|
message: FirebaseMessage {
|
|
token: client_token.to_string(),
|
|
notification: FirebaseNotification {
|
|
title: n.title.to_string(),
|
|
body: n.body.to_string(),
|
|
image: n.image.clone(),
|
|
},
|
|
android: FirebaseAndroid {
|
|
notification: FirebaseAndroidNotification {
|
|
tag: n.id.to_string(),
|
|
},
|
|
ttl: n.timeout.map(|t| format!("{}s", t)),
|
|
},
|
|
},
|
|
};
|
|
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("Authorization", HeaderValue::from_str(&access.authorization_token)?);
|
|
let client = Client::builder().default_headers(headers).build()?;
|
|
|
|
client
|
|
.post(&FIREBASE_PUSH_MESSAGE_URL.replace("{PROJECT_ID}", &access.project_name))
|
|
.json(¬if)
|
|
.send()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Send a notification
|
|
pub fn push_notifications(n: &PushNotification, targets: Vec<UserAccessToken>) -> Res {
|
|
let mut tokens_cache: HashMap<u64, FirebaseClientIdentifier> = HashMap::new();
|
|
|
|
for target in targets {
|
|
// Get an access token if required
|
|
if !tokens_cache.contains_key(&target.client_id) {
|
|
let client = api_helper::get_by_id(target.client_id)?;
|
|
tokens_cache.insert(target.client_id, FirebaseClientIdentifier {
|
|
authorization_token: get_credentials(&client)?,
|
|
project_name: client.firebase_project_name
|
|
.ok_or(ExecError::boxed_new("Missing firebase project name!"))?,
|
|
});
|
|
}
|
|
|
|
|
|
let authorization_token = match tokens_cache.get(&target.client_id) {
|
|
None => {
|
|
return Err(ExecError::boxed_new("Should have a Firebase token now!!!"));
|
|
}
|
|
Some(token) => token
|
|
};
|
|
|
|
let client_token = match &target.push_notifications_token {
|
|
PushNotificationToken::FIREBASE(token) => token,
|
|
_ => {
|
|
return Err(ExecError::boxed_new("Invalid token!"));
|
|
}
|
|
};
|
|
|
|
|
|
if let Err(e) = send_notification(n, client_token, authorization_token) {
|
|
eprintln!("Failed to send a push notification to a device! Error: {}", e);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
} |