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}