use sqlx::{Sqlite, SqliteConnection, Transaction}; use web_push::SubscriptionInfo; use crate::{login::Login, token::Token}; pub trait Provider { fn push(&mut self) -> Push<'_>; } impl Provider for Transaction<'_, Sqlite> { fn push(&mut self) -> Push<'_> { Push(self) } } pub struct Push<'t>(&'t mut SqliteConnection); impl Push<'_> { pub async fn create( &mut self, token: &Token, subscription: &SubscriptionInfo, ) -> Result<(), sqlx::Error> { sqlx::query!( r#" insert into push_subscription (token, endpoint, p256dh, auth) values ($1, $2, $3, $4) "#, token.id, subscription.endpoint, subscription.keys.p256dh, subscription.keys.auth, ) .execute(&mut *self.0) .await?; Ok(()) } pub async fn by_login(&mut self, login: &Login) -> Result, sqlx::Error> { sqlx::query!( r#" select subscription.endpoint, subscription.p256dh, subscription.auth from push_subscription as subscription join token on subscription.token = token.id where token.login = $1 "#, login.id, ) .map(|row| SubscriptionInfo::new(row.endpoint, row.p256dh, row.auth)) .fetch_all(&mut *self.0) .await } pub async fn by_endpoint( &mut self, subscriber: &Login, endpoint: &str, ) -> Result { let row = sqlx::query!( r#" select subscription.endpoint, subscription.p256dh, subscription.auth from push_subscription as subscription join token on subscription.token = token.id join login as subscriber on token.login = subscriber.id where subscriber.id = $1 and subscription.endpoint = $2 "#, subscriber.id, endpoint, ) .fetch_one(&mut *self.0) .await?; let info = SubscriptionInfo::new(row.endpoint, row.p256dh, row.auth); Ok(info) } pub async fn unsubscribe( &mut self, subscription: &SubscriptionInfo, ) -> Result<(), sqlx::Error> { sqlx::query!( r#" delete from push_subscription where endpoint = $1 "#, subscription.endpoint, ) .execute(&mut *self.0) .await?; Ok(()) } pub async fn unsubscribe_token(&mut self, token: &Token) -> Result<(), sqlx::Error> { sqlx::query!( r#" delete from push_subscription where token = $1 "#, token.id, ) .execute(&mut *self.0) .await?; Ok(()) } pub async fn unsubscribe_login(&mut self, login: &Login) -> Result<(), sqlx::Error> { sqlx::query!( r#" with tokens as ( select id from token where login = $1 ) delete from push_subscription where token in tokens "#, login.id, ) .execute(&mut *self.0) .await?; Ok(()) } // Unsubscribe logic for token expiry lives in the `tokens` repository, for maintenance reasons. pub async fn clear(&mut self) -> Result<(), sqlx::Error> { // We assume that _all_ stored subscriptions are for a VAPID key we're about to delete. sqlx::query!( r#" delete from push_subscription "#, ) .execute(&mut *self.0) .await?; Ok(()) } }