feat: moderation locking (#5070)

* feat: base locking impl

* feat: lock logic in place in rev endpoint + fetch rev

* feat: frontend impl and finalize

* feat: auto skip if using the moderation queue page

* fix: qa issues

* fix: async state + locking fix

* fix: lint

* fix: fmt

* fix: qa issue

* fix: qa + redirect bug

* fix: lint

* feat: delete all locks endpoint for admins

* fix: dedupe

* fix: fmt

* fix: project redirect move to middleware

* fix: lint
This commit is contained in:
Calum H.
2026-01-12 17:08:30 +00:00
committed by GitHub
parent 915d8c68bf
commit b46f6d0141
21 changed files with 1644 additions and 321 deletions

View File

@@ -6,7 +6,9 @@ use crate::auth::{filter_visible_projects, get_user_from_headers};
use crate::database::models::notification_item::NotificationBuilder;
use crate::database::models::project_item::{DBGalleryItem, DBModCategory};
use crate::database::models::thread_item::ThreadMessageBuilder;
use crate::database::models::{DBTeamMember, ids as db_ids, image_item};
use crate::database::models::{
DBModerationLock, DBTeamMember, ids as db_ids, image_item,
};
use crate::database::redis::RedisPool;
use crate::database::{self, models as db_models};
use crate::file_hosting::{FileHost, FileHostPublicity};
@@ -368,6 +370,23 @@ pub async fn project_edit(
));
}
// If a moderator is completing a review (changing from Processing to another status),
// check if another moderator holds an active lock on this project
if user.role.is_mod()
&& project_item.inner.status == ProjectStatus::Processing
&& status != &ProjectStatus::Processing
&& let Some(lock) =
DBModerationLock::get_with_user(project_item.inner.id, &pool)
.await?
&& lock.moderator_id != db_ids::DBUserId::from(user.id)
&& !lock.expired
{
return Err(ApiError::CustomAuthentication(format!(
"This project is currently being moderated by @{}. Please wait for them to finish or for the lock to expire.",
lock.moderator_username
)));
}
if status == &ProjectStatus::Processing {
if project_item.versions.is_empty() {
return Err(ApiError::InvalidInput(String::from(