Skip to content
Clone
ef0b83a
125 lines 4.3 KB Rust
use models::users::user_push_subscriptions::{self, PushSubscriptionPlatform};
use sea_orm::{
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set,
};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use utoipa::ToSchema;
use uuid::Uuid;
use crate::errors::UserError;
use crate::services::service::UserService;
use crate::share::page::PageQuery;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PushSubscriptionDto {
pub id: Uuid,
pub endpoint: String,
pub platform: PushSubscriptionPlatform,
pub user_agent: Option<String>,
pub created_at: OffsetDateTime,
pub updated_at: Option<OffsetDateTime>,
}
impl From<user_push_subscriptions::Model> for PushSubscriptionDto {
fn from(m: user_push_subscriptions::Model) -> Self {
Self {
id: m.id,
endpoint: m.endpoint,
platform: m.platform,
user_agent: m.user_agent,
created_at: m.created_at,
updated_at: m.updated_at,
}
}
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct UpsertPushCmd {
pub endpoint: String,
pub p256dh_key: String,
pub auth_key: String,
pub platform: PushSubscriptionPlatform,
pub user_agent: Option<String>,
}
impl UserService {
pub async fn list_push_subscriptions(
&self,
uid: i64,
q: PageQuery,
) -> Result<Vec<PushSubscriptionDto>, UserError> {
let rows = user_push_subscriptions::Entity::find()
.filter(user_push_subscriptions::Column::UserId.eq(uid))
.order_by_desc(user_push_subscriptions::Column::CreatedAt)
.limit(q.limit())
.offset(q.offset())
.all(self.db().read())
.await?;
Ok(rows.into_iter().map(PushSubscriptionDto::from).collect())
}
#[tracing::instrument(name = "user.upsert_push_subscription", skip_all, fields(uid = %uid))]
pub async fn upsert_push_subscription(
&self,
uid: i64,
cmd: UpsertPushCmd,
) -> Result<PushSubscriptionDto, UserError> {
if cmd.endpoint.is_empty() || cmd.endpoint.len() > 2048 {
return Err(UserError::BadRequest("endpoint invalid".into()));
}
let existing = user_push_subscriptions::Entity::find()
.filter(user_push_subscriptions::Column::UserId.eq(uid))
.filter(user_push_subscriptions::Column::Endpoint.eq(&cmd.endpoint))
.one(self.db().read())
.await?;
let now = OffsetDateTime::now_utc();
let model = match existing {
Some(row) => {
let mut am: user_push_subscriptions::ActiveModel = row.into();
am.p256dh_key = Set(cmd.p256dh_key);
am.auth_key = Set(cmd.auth_key);
am.platform = Set(cmd.platform);
am.user_agent = Set(cmd.user_agent);
am.updated_at = Set(Some(now));
am.update(self.db().write()).await?
}
None => {
let active = user_push_subscriptions::ActiveModel {
id: Set(Uuid::now_v7()),
user_id: Set(uid),
endpoint: Set(cmd.endpoint),
p256dh_key: Set(cmd.p256dh_key),
auth_key: Set(cmd.auth_key),
platform: Set(cmd.platform),
user_agent: Set(cmd.user_agent),
created_at: Set(now),
updated_at: Set(Some(now)),
};
active.insert(self.db().write()).await?
}
};
Ok(PushSubscriptionDto::from(model))
}
#[tracing::instrument(
name = "user.delete_push_subscription",
skip_all,
fields(uid = %uid, push_id = %push_id)
)]
pub async fn delete_push_subscription(&self, uid: i64, push_id: Uuid) -> Result<(), UserError> {
let row = user_push_subscriptions::Entity::find_by_id(push_id)
.one(self.db().read())
.await?
.ok_or(UserError::NotFound)?;
if row.user_id != uid {
return Err(UserError::Forbidden);
}
user_push_subscriptions::Entity::delete_by_id(push_id)
.exec(self.db().write())
.await?;
Ok(())
}
}