api/routes/modules/assignments/tickets/ticket_messages/
post.rs1use axum::{
8 Extension, Json,
9 extract::{Path, State},
10 http::StatusCode,
11 response::IntoResponse,
12};
13use db::models::{
14 ticket_messages::Model as TicketMessageModel,
15 user::{Column as UserColumn, Entity as UserEntity, Model as UserModel},
16};
17use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
18use util::state::AppState;
19
20use crate::{
21 auth::AuthUser,
22 response::ApiResponse,
23 routes::modules::assignments::tickets::{
24 common::is_valid,
25 ticket_messages::common::{MessageResponse, UserResponse},
26 }, ws::tickets::topics::ticket_chat_topic,
27};
28
29pub async fn create_message(
93 Path((module_id, _, ticket_id)): Path<(i64, i64, i64)>,
94 State(app_state): State<AppState>,
95 Extension(AuthUser(claims)): Extension<AuthUser>,
96 Json(req): Json<serde_json::Value>,
97) -> impl IntoResponse {
98 let db = app_state.db();
99 let user_id = claims.sub;
100
101 if !is_valid(user_id, ticket_id, module_id, claims.admin, db).await {
102 return (
103 StatusCode::FORBIDDEN,
104 Json(ApiResponse::<()>::error("Forbidden")),
105 )
106 .into_response();
107 }
108
109 let content = match req.get("content").and_then(|v| v.as_str()) {
110 Some(c) if !c.trim().is_empty() => c.trim().to_string(),
111 _ => {
112 return (
113 StatusCode::BAD_REQUEST,
114 Json(ApiResponse::<()>::error("Content is required")),
115 )
116 .into_response();
117 }
118 };
119
120 let user: Option<UserModel> = UserEntity::find()
121 .filter(UserColumn::Id.eq(user_id))
122 .one(db)
123 .await
124 .unwrap_or(None);
125
126 let user = match user {
127 Some(u) => u,
128 None => {
129 return (
130 StatusCode::NOT_FOUND,
131 Json(ApiResponse::<()>::error("User not found")),
132 )
133 .into_response();
134 }
135 };
136
137 let message = match TicketMessageModel::create(db, ticket_id, user_id, &content).await {
138 Ok(msg) => msg,
139 Err(_) => {
140 return (
141 StatusCode::INTERNAL_SERVER_ERROR,
142 Json(ApiResponse::<()>::error("Failed to create message")),
143 )
144 .into_response();
145 }
146 };
147
148 let response = MessageResponse {
149 id: message.id,
150 ticket_id: message.ticket_id,
151 content: message.content,
152 created_at: message.created_at.to_rfc3339(),
153 updated_at: message.updated_at.to_rfc3339(),
154 user: Some(UserResponse {
155 id: user.id,
156 username: user.username,
157 }),
158 };
159
160 let topic = ticket_chat_topic(ticket_id);
163 let ws = app_state.ws_clone();
164 let event_json = serde_json::json!({
165 "event": "message_created",
166 "payload": &response
167 });
168 ws.broadcast(&topic, event_json.to_string()).await;
169
170 (
171 StatusCode::OK,
172 Json(ApiResponse::success(
173 response,
174 "Message created successfully",
175 )),
176 )
177 .into_response()
178}