api/routes/modules/announcements/delete.rs
1//! Announcement deletion handler.
2//!
3//! Provides an endpoint to delete an existing announcement.
4//!
5//! **Permissions:** Only users with the proper roles (e.g., lecturer/assistant) can delete announcements.
6
7use axum::{extract::{Path, State}, http::StatusCode, response::IntoResponse, Json};
8use db::models::announcements::Model as AnnouncementModel;
9use util::state::AppState;
10use crate::response::ApiResponse;
11
12/// DELETE /api/modules/{module_id}/announcements/{announcement_id}
13///
14/// Deletes a single announcement by ID under the given module.
15///
16/// # AuthZ / AuthN
17/// - Requires a valid `Bearer` token (JWT).
18/// - Caller must be **lecturer** or **assistant_lecturer** on the module
19/// (enforced by `require_lecturer_or_assistant_lecturer` on this route).
20///
21/// # Path Parameters
22/// - `module_id` — ID of the parent module (used for route nesting and auth).
23/// - `announcement_id` — ID of the announcement to delete.
24///
25/// # Behavior
26/// - Deletion is **idempotent**: attempting to delete a non-existent announcement
27/// will still return `200 OK` in this implementation (the driver returns
28/// `rows_affected = 0`, which we do not currently treat as an error).
29///
30/// # Example cURL
31/// ```bash
32/// curl -X DELETE "https://your.api/api/modules/101/announcements/1234" \
33/// -H "Authorization: Bearer <JWT>"
34/// ```
35///
36/// # Responses
37/// - `200 OK` — Always returned on successful DB call, even if the record didn’t exist.
38/// - `401 UNAUTHORIZED` — Missing/invalid token.
39/// - `403 FORBIDDEN` — Authenticated but not lecturer/assistant on this module.
40/// - `500 INTERNAL SERVER ERROR` — Database error.
41///
42/// ## 200 OK — Example
43/// ```json
44/// {
45/// "success": true,
46/// "data": null,
47/// "message": "Announcement deleted successfully"
48/// }
49/// ```
50///
51/// ## 403 Forbidden — Example
52/// ```json
53/// {
54/// "success": false,
55/// "message": "Forbidden"
56/// }
57/// ```
58///
59/// ## 500 Internal Server Error — Example
60/// ```json
61/// {
62/// "success": false,
63/// "message": "Failed to delete announcement: <database error details>"
64/// }
65/// ```
66pub async fn delete_announcement(
67 State(app_state): State<AppState>,
68 Path((_, announcement_id)): Path<(i64, i64)>,
69) -> impl IntoResponse {
70 let db = app_state.db();
71 match AnnouncementModel::delete(db, announcement_id).await {
72 Ok(_) => (
73 StatusCode::OK,
74 Json(ApiResponse::success(
75 (),
76 "Announcement deleted successfully",
77 )),
78 ),
79 Err(err) => (
80 StatusCode::INTERNAL_SERVER_ERROR,
81 Json(ApiResponse::error(
82 format!("Failed to delete announcement: {}", err),
83 )),
84 ),
85 }
86}