feat: access labrinth backend (#6284)

* feat: redirect `/hosting` to archon

* feat: server invite notification type

* feat: direct email notification endpoint

* feat: revoke notification endpoint

* feat: specify users to remove notifications from

* refactor: insert notifications before sending emails

* refactor: rename endpoint

* refactor: remove archon redirect

* style: mark field unused

* feat: dedup external notifications

* feat: add server invite email templates

* style: remove unnecessary format

---------

Co-authored-by: sychic <47618543+Sychic@users.noreply.github.com>
This commit is contained in:
Calum H.
2026-06-02 16:34:04 +00:00
committed by GitHub
co-authored by sychic
parent d61397097c
commit 6ee5e4df19
11 changed files with 550 additions and 8 deletions
@@ -129,12 +129,11 @@ impl NotificationBuilder {
Ok(())
}
pub async fn insert_many(
async fn insert_many_records(
&self,
users: Vec<DBUserId>,
users: &[DBUserId],
transaction: &mut PgTransaction<'_>,
redis: &RedisPool,
) -> Result<(), DatabaseError> {
) -> Result<Vec<i64>, DatabaseError> {
let notification_ids =
generate_many_notification_ids(users.len(), &mut *transaction)
.await?;
@@ -163,6 +162,20 @@ impl NotificationBuilder {
.execute(&mut *transaction)
.await?;
Ok(notification_ids)
}
pub async fn insert_many(
&self,
users: Vec<DBUserId>,
transaction: &mut PgTransaction<'_>,
redis: &RedisPool,
) -> Result<(), DatabaseError> {
let notification_ids =
self.insert_many_records(&users, transaction).await?;
let users_raw_ids = users.iter().map(|x| x.0).collect::<Vec<_>>();
let notification_types = notification_ids
.iter()
.map(|_| self.body.notification_type().as_str())
@@ -181,6 +194,19 @@ impl NotificationBuilder {
Ok(())
}
/// Like [`insert_many`], but skips queuing deliveries so the caller can
/// manually send the notifications.
pub async fn insert_many_without_delivery(
&self,
users: Vec<DBUserId>,
transaction: &mut PgTransaction<'_>,
redis: &RedisPool,
) -> Result<(), DatabaseError> {
self.insert_many_records(&users, transaction).await?;
DBNotification::clear_user_notifications_cache(&users, redis).await?;
Ok(())
}
pub async fn insert_many_deliveries(
transaction: &mut PgTransaction<'_>,
redis: &RedisPool,
@@ -571,6 +597,38 @@ impl DBNotification {
Ok(Some(()))
}
pub async fn remove_many_matching_body(
body_filter: &serde_json::Value,
users: &[DBUserId],
transaction: &mut PgTransaction<'_>,
redis: &RedisPool,
) -> Result<usize, DatabaseError> {
let user_ids = users.iter().map(|x| x.0).collect::<Vec<i64>>();
let ids = sqlx::query!(
"
SELECT id
FROM notifications
WHERE body @> $1::jsonb
AND user_id = ANY($2::bigint[])
",
body_filter,
&user_ids
)
.fetch(&mut *transaction)
.map_ok(|x| DBNotificationId(x.id))
.try_collect::<Vec<_>>()
.await?;
if ids.is_empty() {
return Ok(0);
}
Self::remove_many(&ids, transaction, redis).await?;
Ok(ids.len())
}
pub async fn clear_user_notifications_cache(
user_ids: impl IntoIterator<Item = &DBUserId>,
redis: &RedisPool,