Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ a2 = { git = "https://github.com/WalletConnect/a2/", branch = "master" }
anyhow = "1.0.32"
axum = "0.7.5"
base64 = "0.22.1"
chrono = { version = "0.4.41", default-features = false }
femme = "2.1.0"
humantime = "2.0.1"
log = "0.4.11"
Expand Down
13 changes: 12 additions & 1 deletion src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub struct Metrics {
/// Number of successfully sent visible FCM notifications.
pub fcm_notifications_total: Counter,

/// Number of successfully sent visible UBports notifications.
pub ubports_notifications_total: Counter,

/// Number of successfully sent heartbeat notifications.
pub heartbeat_notifications_total: Counter,

Expand Down Expand Up @@ -58,6 +61,13 @@ impl Metrics {
fcm_notifications_total.clone(),
);

let ubports_notifications_total = Counter::default();
registry.register(
"ubports_notifications",
"Number of UBports notifications",
ubports_notifications_total.clone(),
);

let heartbeat_notifications_total = Counter::default();
registry.register(
"heartbeat_notifications",
Expand Down Expand Up @@ -88,8 +98,9 @@ impl Metrics {

Self {
registry,
fcm_notifications_total,
direct_notifications_total,
fcm_notifications_total,
ubports_notifications_total,
heartbeat_notifications_total,
heartbeat_registrations_total,
heartbeat_tokens,
Expand Down
2 changes: 1 addition & 1 deletion src/notifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ async fn wakeup(
let device_token: NotificationToken = key_device_token.as_str().parse()?;

let (client, device_token) = match device_token {
NotificationToken::Fcm { .. } => {
NotificationToken::Fcm { .. } | NotificationToken::UBports(..) => {
// Only APNS tokens can be registered for periodic notifications.
info!("Removing FCM token {key_device_token}");
schedule
Expand Down
54 changes: 54 additions & 0 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use anyhow::{bail, Error, Result};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::routing::{get, post};
use chrono::{Local, TimeDelta};
use log::*;
use serde::Deserialize;
use std::str::FromStr;
Expand Down Expand Up @@ -76,6 +77,9 @@ async fn register_device(
}

pub(crate) enum NotificationToken {
/// Ubuntu touch app
UBports(String),

/// Android App.
Fcm {
/// Package name such as `chat.delta`.
Expand Down Expand Up @@ -105,6 +109,8 @@ impl FromStr for NotificationToken {
} else {
bail!("Invalid FCM token");
}
} else if let Some(s) = s.strip_prefix("ubports-") {
Ok(Self::UBports(s.to_string()))
} else if let Some(token) = s.strip_prefix("sandbox:") {
Ok(Self::ApnsSandbox(token.to_string()))
} else {
Expand All @@ -113,6 +119,49 @@ impl FromStr for NotificationToken {
}
}

/// Notify the UBports push server
///
/// API documentation is available at
/// <https://docs.ubports.com/en/latest/appdev/guides/pushnotifications.html>
async fn notify_ubports(
client: &reqwest::Client,
token: &str,
metrics: &Metrics,
) -> Result<StatusCode> {
if !token
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == ':' || c == '-')
{
return Ok(StatusCode::GONE);
}

let url = "https://push.ubports.com/notify";
let expire_on = (Local::now() + TimeDelta::weeks(1)).to_rfc3339();
let body = format!(
r#"{{"expire_on":"{expire_on}","appid":"deltatouch.lotharketterer_deltatouch","token":"{token}","data":{{"notification":{{"tag":"sent_by_chatmail_server","card":{{"popup":true,"persist":true,"summary":"New message","body":"You have a new message"}},"sound":true,"vibrate":{{"pattern":[200],"duration":200,"repeat":1}} }},"sent-by":"Chatmail Server"}} }}"#
);
let res = client
.post(url)
.body(body.clone())
.header("Content-Type", "application/json")
.send()
.await?;
let status = res.status();
if status.is_client_error() {
warn!("Failed to deliver UBports notification to {token}");
warn!("BODY: {body:?}");
warn!("RES: {res:?}");
return Ok(StatusCode::GONE);
}
if status.is_server_error() {
warn!("Internal server error while attempting to deliver UBports notification to {token}");
return Ok(StatusCode::INTERNAL_SERVER_ERROR);
}
info!("Delivered notification to UBports token {token}");
metrics.ubports_notifications_total.inc();
Ok(StatusCode::OK)
}

/// Notifies a single FCM token.
///
/// API documentation is available at
Expand Down Expand Up @@ -247,6 +296,11 @@ async fn notify_device(
let device_token: NotificationToken = device_token.as_str().parse()?;

let status_code = match device_token {
NotificationToken::UBports(token) => {
let client = state.fcm_client().clone();
let metrics = state.metrics();
notify_ubports(&client, &token, metrics).await?
}
NotificationToken::Fcm {
package_name,
token,
Expand Down
Loading