mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-06-25 15:37:42 +00:00
285 lines
8 KiB
Rust
285 lines
8 KiB
Rust
|
|
//! Conflict Resolution API endpoints.
|
||
|
|
|
||
|
|
use axum::{
|
||
|
|
extract::{Path, State},
|
||
|
|
http::StatusCode,
|
||
|
|
routing::{get, post},
|
||
|
|
Json, Router,
|
||
|
|
};
|
||
|
|
use serde::Deserialize;
|
||
|
|
use serde_json::{json, Value};
|
||
|
|
use sqlx::PgPool;
|
||
|
|
use uuid::Uuid;
|
||
|
|
|
||
|
|
use crate::auth::AuthUser;
|
||
|
|
use crate::plugins::builtin::conflict_resolution::{ConflictCase, ConflictService};
|
||
|
|
|
||
|
|
// ============================================================================
|
||
|
|
// Request Types
|
||
|
|
// ============================================================================
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
pub struct ReportConflictRequest {
|
||
|
|
pub conflict_type: String,
|
||
|
|
pub title: String,
|
||
|
|
pub description: String,
|
||
|
|
pub party_a_id: Uuid,
|
||
|
|
pub party_b_id: Option<Uuid>,
|
||
|
|
pub anonymous: bool,
|
||
|
|
pub severity: i32,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
pub struct TransitionStatusRequest {
|
||
|
|
pub new_status: String,
|
||
|
|
pub notes: Option<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
pub struct ProposeCompromiseRequest {
|
||
|
|
pub title: String,
|
||
|
|
pub description: String,
|
||
|
|
pub proposed_actions: Value,
|
||
|
|
pub proposed_by_role: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
pub struct RespondToCompromiseRequest {
|
||
|
|
pub party: String,
|
||
|
|
pub response: String,
|
||
|
|
pub feedback: Option<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
pub struct ScheduleSessionRequest {
|
||
|
|
pub scheduled_for: chrono::DateTime<chrono::Utc>,
|
||
|
|
pub duration_minutes: i32,
|
||
|
|
pub agenda: Option<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
pub struct AddNoteRequest {
|
||
|
|
pub content: String,
|
||
|
|
pub note_type: String,
|
||
|
|
pub session_id: Option<Uuid>,
|
||
|
|
pub is_confidential: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
pub struct AddMediatorRequest {
|
||
|
|
pub user_id: Uuid,
|
||
|
|
pub certification_level: Option<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
// ============================================================================
|
||
|
|
// Handlers
|
||
|
|
// ============================================================================
|
||
|
|
|
||
|
|
/// Get active conflicts in a community
|
||
|
|
async fn get_active_conflicts(
|
||
|
|
_auth: AuthUser,
|
||
|
|
Path(community_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
) -> Result<Json<Vec<ConflictCase>>, (StatusCode, String)> {
|
||
|
|
let conflicts = ConflictService::get_active_conflicts(&pool, community_id)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(conflicts))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get a specific conflict
|
||
|
|
async fn get_conflict(
|
||
|
|
_auth: AuthUser,
|
||
|
|
Path(conflict_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
) -> Result<Json<Option<ConflictCase>>, (StatusCode, String)> {
|
||
|
|
let conflict = ConflictService::get_conflict(&pool, conflict_id)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(conflict))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Report a new conflict
|
||
|
|
async fn report_conflict(
|
||
|
|
auth: AuthUser,
|
||
|
|
Path(community_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
Json(req): Json<ReportConflictRequest>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
let conflict_id = ConflictService::report_conflict(
|
||
|
|
&pool,
|
||
|
|
community_id,
|
||
|
|
&req.title,
|
||
|
|
&req.description,
|
||
|
|
&req.conflict_type,
|
||
|
|
req.party_a_id,
|
||
|
|
req.party_b_id,
|
||
|
|
Some(auth.user_id),
|
||
|
|
req.anonymous,
|
||
|
|
req.severity,
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(json!({"id": conflict_id})))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Transition conflict status
|
||
|
|
async fn transition_status(
|
||
|
|
auth: AuthUser,
|
||
|
|
Path(conflict_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
Json(req): Json<TransitionStatusRequest>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
ConflictService::transition_status(
|
||
|
|
&pool,
|
||
|
|
conflict_id,
|
||
|
|
&req.new_status,
|
||
|
|
auth.user_id,
|
||
|
|
req.notes.as_deref(),
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(json!({"success": true})))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Propose a compromise
|
||
|
|
async fn propose_compromise(
|
||
|
|
auth: AuthUser,
|
||
|
|
Path(conflict_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
Json(req): Json<ProposeCompromiseRequest>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
let proposal_id = ConflictService::propose_compromise(
|
||
|
|
&pool,
|
||
|
|
conflict_id,
|
||
|
|
&req.title,
|
||
|
|
&req.description,
|
||
|
|
req.proposed_actions,
|
||
|
|
auth.user_id,
|
||
|
|
&req.proposed_by_role,
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(json!({"id": proposal_id})))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Respond to a compromise proposal
|
||
|
|
async fn respond_to_compromise(
|
||
|
|
_auth: AuthUser,
|
||
|
|
Path(proposal_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
Json(req): Json<RespondToCompromiseRequest>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
ConflictService::respond_to_compromise(
|
||
|
|
&pool,
|
||
|
|
proposal_id,
|
||
|
|
&req.party,
|
||
|
|
&req.response,
|
||
|
|
req.feedback.as_deref(),
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(json!({"success": true})))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Schedule a mediation session
|
||
|
|
async fn schedule_session(
|
||
|
|
_auth: AuthUser,
|
||
|
|
Path(conflict_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
Json(req): Json<ScheduleSessionRequest>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
let session_id = ConflictService::schedule_session(
|
||
|
|
&pool,
|
||
|
|
conflict_id,
|
||
|
|
req.scheduled_for,
|
||
|
|
req.duration_minutes,
|
||
|
|
req.agenda.as_deref(),
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(json!({"id": session_id})))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Add a note to a conflict
|
||
|
|
async fn add_note(
|
||
|
|
auth: AuthUser,
|
||
|
|
Path(conflict_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
Json(req): Json<AddNoteRequest>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
let note_id = ConflictService::add_note(
|
||
|
|
&pool,
|
||
|
|
conflict_id,
|
||
|
|
req.session_id,
|
||
|
|
auth.user_id,
|
||
|
|
&req.content,
|
||
|
|
&req.note_type,
|
||
|
|
req.is_confidential,
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(json!({"id": note_id})))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get conflict statistics for a community
|
||
|
|
async fn get_statistics(
|
||
|
|
_auth: AuthUser,
|
||
|
|
Path(community_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
let stats = ConflictService::get_statistics(&pool, community_id)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(stats))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Add user to mediator pool
|
||
|
|
async fn add_to_mediator_pool(
|
||
|
|
_auth: AuthUser,
|
||
|
|
Path(community_id): Path<Uuid>,
|
||
|
|
State(pool): State<PgPool>,
|
||
|
|
Json(req): Json<AddMediatorRequest>,
|
||
|
|
) -> Result<Json<Value>, (StatusCode, String)> {
|
||
|
|
let mediator_id = ConflictService::add_to_mediator_pool(
|
||
|
|
&pool,
|
||
|
|
community_id,
|
||
|
|
req.user_id,
|
||
|
|
req.certification_level.as_deref(),
|
||
|
|
)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||
|
|
|
||
|
|
Ok(Json(json!({"id": mediator_id})))
|
||
|
|
}
|
||
|
|
|
||
|
|
// ============================================================================
|
||
|
|
// Router
|
||
|
|
// ============================================================================
|
||
|
|
|
||
|
|
pub fn router(pool: PgPool) -> Router {
|
||
|
|
Router::new()
|
||
|
|
// Community conflicts
|
||
|
|
.route("/api/communities/{community_id}/conflicts", get(get_active_conflicts).post(report_conflict))
|
||
|
|
.route("/api/communities/{community_id}/conflicts/stats", get(get_statistics))
|
||
|
|
.route("/api/communities/{community_id}/mediators", post(add_to_mediator_pool))
|
||
|
|
// Individual conflict operations
|
||
|
|
.route("/api/conflicts/{conflict_id}", get(get_conflict))
|
||
|
|
.route("/api/conflicts/{conflict_id}/status", post(transition_status))
|
||
|
|
.route("/api/conflicts/{conflict_id}/compromise", post(propose_compromise))
|
||
|
|
.route("/api/conflicts/{conflict_id}/session", post(schedule_session))
|
||
|
|
.route("/api/conflicts/{conflict_id}/note", post(add_note))
|
||
|
|
// Compromise responses
|
||
|
|
.route("/api/compromises/{proposal_id}/respond", post(respond_to_compromise))
|
||
|
|
.with_state(pool)
|
||
|
|
}
|