Skip to content
Clone
a06673b
125 lines 4.7 KB Rust
use db::conn::sea_orm::{
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set, TransactionTrait,
};
use db::user::user_deletion_requests::UserDeletionRequestStatus as DbDeletionStatus;
use uuid::Uuid;
use crate::error::{UserError, UserResult};
use crate::features::UserService;
use jwt::session;
use super::types::{DeletionRequestView, RequestDeletionCmd};
pub async fn request(
svc: &UserService,
caller: Uuid,
cmd: RequestDeletionCmd,
) -> UserResult<DeletionRequestView> {
let writer = svc.db.writer();
let existing = db::user::user_deletion_requests::Entity::find()
.filter(db::user::user_deletion_requests::COLUMN.user_id.eq(caller))
.filter(
db::user::user_deletion_requests::COLUMN.status.eq(DbDeletionStatus::Pending),
)
.one(&writer)
.await?;
if let Some(row) = existing {
return Ok(DeletionRequestView::from(row));
}
let now = time::OffsetDateTime::now_utc();
let active: db::user::user_deletion_requests::ActiveModel =
db::user::user_deletion_requests::ActiveModel::builder()
.set_id(Uuid::new_v4())
.set_user_id(caller)
.set_reason(cmd.reason)
.set_status(DbDeletionStatus::Pending)
.set_scheduled_for(None)
.set_completed_at(None)
.set_cancelled_at(None)
.set_created_at(now)
.set_updated_at(now)
.into();
let saved = active.insert(&writer).await?;
Ok(DeletionRequestView::from(saved))
}
pub async fn cancel(svc: &UserService, caller: Uuid) -> UserResult<()> {
let writer = svc.db.writer();
let row = db::user::user_deletion_requests::Entity::find()
.filter(db::user::user_deletion_requests::COLUMN.user_id.eq(caller))
.filter(
db::user::user_deletion_requests::COLUMN.status.is_in([
DbDeletionStatus::Pending,
DbDeletionStatus::Scheduled,
]),
)
.one(&writer)
.await?
.ok_or_else(|| UserError::NotFound("no active deletion request".into()))?;
let mut am: db::user::user_deletion_requests::ActiveModel = row.into();
am.status = Set(DbDeletionStatus::Cancelled);
am.cancelled_at = Set(Some(time::OffsetDateTime::now_utc()));
am.updated_at = Set(time::OffsetDateTime::now_utc());
am.update(&writer).await?;
Ok(())
}
pub async fn confirm(svc: &UserService, caller: Uuid) -> UserResult<DeletionRequestView> {
let writer = svc.db.writer();
let row = db::user::user_deletion_requests::Entity::find()
.filter(db::user::user_deletion_requests::COLUMN.user_id.eq(caller))
.filter(
db::user::user_deletion_requests::COLUMN.status.eq(DbDeletionStatus::Pending),
)
.one(&writer)
.await?
.ok_or_else(|| UserError::NotFound("no pending deletion request".into()))?;
let now = time::OffsetDateTime::now_utc();
let scheduled_for = now + time::Duration::days(svc.config.deletion_grace_days);
let mut am: db::user::user_deletion_requests::ActiveModel = row.into();
am.status = Set(DbDeletionStatus::Scheduled);
am.scheduled_for = Set(Some(scheduled_for));
am.updated_at = Set(now);
let saved = am.update(&writer).await?;
Ok(DeletionRequestView::from(saved))
}
pub async fn list_pending(svc: &UserService) -> UserResult<Vec<DeletionRequestView>> {
let db = svc.db.reader();
let rows = db::user::user_deletion_requests::Entity::find()
.filter(
db::user::user_deletion_requests::COLUMN.status.is_in([
DbDeletionStatus::Pending,
DbDeletionStatus::Scheduled,
]),
)
.all(&db)
.await?;
Ok(rows.into_iter().map(DeletionRequestView::from).collect())
}
pub async fn complete(svc: &UserService, request_id: Uuid) -> UserResult<()> {
let writer = svc.db.writer();
let txn = writer.begin().await?;
let row = db::user::user_deletion_requests::Entity::find_by_id(request_id)
.one(&txn)
.await?
.ok_or_else(|| UserError::NotFound(format!("deletion request {request_id}")))?;
if !matches!(row.status, DbDeletionStatus::Scheduled) {
return Err(UserError::BadRequest("request not in Scheduled state".into()));
}
let user_id = row.user_id;
let mut am: db::user::user_deletion_requests::ActiveModel = row.into();
am.status = Set(DbDeletionStatus::Completed);
am.completed_at = Set(Some(time::OffsetDateTime::now_utc()));
am.updated_at = Set(time::OffsetDateTime::now_utc());
am.update(&txn).await?;
session::delete_sessions_by_user(&txn, user_id)
.await
.map_err(|e| UserError::Internal(format!("jwt delete_sessions_by_user: {e}")))?;
txn.commit().await?;
Ok(())
}