2024-09-23 00:12:56 +00:00
|
|
|
use axum::{
|
2024-10-04 18:18:36 +00:00
|
|
|
extract::{FromRef, Path, State},
|
|
|
|
http::{
|
|
|
|
header::{ACCEPT, CONTENT_TYPE},
|
|
|
|
HeaderMap, HeaderValue, StatusCode,
|
|
|
|
},
|
|
|
|
response::{Html, IntoResponse, Redirect},
|
|
|
|
Form, Json,
|
2024-09-23 00:12:56 +00:00
|
|
|
};
|
|
|
|
use maud::{html, Render};
|
2024-10-04 18:18:36 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-09-23 00:12:56 +00:00
|
|
|
use sqlx::{Pool, Postgres};
|
2024-10-07 19:12:49 +00:00
|
|
|
use tracing::error;
|
2024-09-23 00:12:56 +00:00
|
|
|
use uuid::Uuid;
|
|
|
|
|
2024-10-04 18:18:36 +00:00
|
|
|
use crate::{
|
2024-10-07 19:12:49 +00:00
|
|
|
markup_response::{simple_error_page, MarkupResponse},
|
2024-10-04 18:18:36 +00:00
|
|
|
model::{Chat, Message},
|
2024-10-07 19:12:49 +00:00
|
|
|
state::AppState,
|
2024-10-04 18:18:36 +00:00
|
|
|
ADMIN_TOK,
|
|
|
|
};
|
2024-09-23 00:12:56 +00:00
|
|
|
|
|
|
|
pub async fn get(
|
|
|
|
Path(url_path): Path<String>,
|
2024-10-04 18:18:36 +00:00
|
|
|
headers: HeaderMap,
|
2024-10-07 19:12:49 +00:00
|
|
|
State(state): State<AppState>,
|
2024-10-04 18:18:36 +00:00
|
|
|
) -> impl IntoResponse {
|
|
|
|
println!("headers: {headers:#?}");
|
2024-10-07 19:12:49 +00:00
|
|
|
// TODO: Error handling
|
|
|
|
let chat = match state.fetch_chat_by_url_path(&url_path).await {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(err) => {
|
|
|
|
error!("Error fetching chat: {err:?}");
|
|
|
|
return simple_error_page(err.into()).into_response();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let messages = match state.fetch_messages(&chat).await {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(err) => {
|
|
|
|
error!("Error fetching messages for chat {}: {err:?}", chat.id);
|
|
|
|
return simple_error_page(err.into()).into_response();
|
|
|
|
}
|
|
|
|
};
|
2024-10-04 18:18:36 +00:00
|
|
|
|
|
|
|
if Some(&HeaderValue::from_static("application/json")) == headers.get(ACCEPT) {
|
|
|
|
Json(messages).into_response()
|
|
|
|
} else {
|
2024-10-07 19:12:49 +00:00
|
|
|
MarkupResponse::new(
|
2024-10-04 18:18:36 +00:00
|
|
|
html! {
|
2024-11-11 15:52:32 +00:00
|
|
|
link rel="stylesheet" href="/css";
|
2024-10-07 19:12:49 +00:00
|
|
|
template #chatmessage {
|
|
|
|
div.message {
|
|
|
|
p { }
|
|
|
|
span.timestamp { }
|
|
|
|
}
|
|
|
|
}
|
2024-10-04 18:18:36 +00:00
|
|
|
main {
|
|
|
|
div #history {
|
|
|
|
@for msg in &messages {
|
2024-10-07 19:12:49 +00:00
|
|
|
div.message.(if msg.from_admin { "from_admin" } else { "from_user" }) #(msg.id) {
|
|
|
|
p { (msg.content) }
|
|
|
|
span.timestamp { (msg.timestamp) }
|
2024-10-04 18:18:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
form #send method="post"{
|
|
|
|
textarea #msgcontent name="msgcontent" rows="1" cols="80" {}
|
|
|
|
button type="submit" { "Send!" }
|
|
|
|
}
|
|
|
|
}
|
2024-11-11 15:52:32 +00:00
|
|
|
script src="/js" {};
|
2024-10-07 19:12:49 +00:00
|
|
|
|
|
|
|
},
|
|
|
|
"Cursed Messenger from hell",
|
2024-10-04 18:18:36 +00:00
|
|
|
)
|
|
|
|
.into_response()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-07 19:12:49 +00:00
|
|
|
pub async fn poll(Path(message): Path<Uuid>, State(state): State<AppState>) -> impl IntoResponse {
|
|
|
|
let message = sqlx::query_as!(Message, r#"select * from messages where id = $1"#, message)
|
|
|
|
.fetch_one(state.pool())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let new_messages = sqlx::query_as!(
|
|
|
|
Message,
|
|
|
|
r#"select * from messages where chat_id = $1 and timestamp > $2;"#,
|
|
|
|
message.chat_id,
|
|
|
|
message.timestamp
|
|
|
|
)
|
|
|
|
.fetch_all(state.pool())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
Json(new_messages)
|
|
|
|
}
|
|
|
|
|
2024-10-04 18:18:36 +00:00
|
|
|
// TODO:
|
|
|
|
// - validation of msg length
|
|
|
|
// - fix terrible returns lmao
|
|
|
|
pub async fn post(
|
|
|
|
Path(url_path): Path<String>,
|
|
|
|
headers: HeaderMap,
|
2024-10-07 19:12:49 +00:00
|
|
|
State(state): State<AppState>,
|
2024-10-04 18:18:36 +00:00
|
|
|
Form(FormMessageBody { msgcontent: body }): Form<FormMessageBody>,
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
let chat = sqlx::query_as!(Chat, r#"select * from chats where url_path = $1"#, url_path)
|
2024-10-07 19:12:49 +00:00
|
|
|
.fetch_one(state.pool())
|
2024-10-04 18:18:36 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
if body.len() > 2000 {
|
|
|
|
return StatusCode::BAD_REQUEST.into_response();
|
|
|
|
}
|
|
|
|
|
|
|
|
if headers.get("x-admin-tok") == Some(&HeaderValue::from_static(ADMIN_TOK)) {
|
|
|
|
sqlx::query!(
|
|
|
|
r#"insert into messages (chat_id, content, from_admin) values ($1, $2, true);"#,
|
|
|
|
chat.id,
|
|
|
|
body
|
|
|
|
)
|
2024-10-07 19:12:49 +00:00
|
|
|
.execute(state.pool())
|
2024-10-04 18:18:36 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
StatusCode::OK.into_response()
|
|
|
|
} else {
|
|
|
|
sqlx::query!(
|
|
|
|
r#"insert into messages (chat_id, content, from_admin) values ($1, $2, false);"#,
|
|
|
|
chat.id,
|
|
|
|
body
|
|
|
|
)
|
2024-10-07 19:12:49 +00:00
|
|
|
.execute(state.pool())
|
2024-10-04 18:18:36 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
Redirect::to(&format!("/{url_path}")).into_response()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
pub struct FormMessageBody {
|
|
|
|
msgcontent: String,
|
2024-09-23 00:12:56 +00:00
|
|
|
}
|