shoutdotdev/src/v0_router.rs

119 lines
3.8 KiB
Rust
Raw Normal View History

2025-02-26 13:10:47 -08:00
use anyhow::Context;
use axum::{
2025-03-08 22:18:24 -08:00
extract::Query,
2025-02-26 13:10:47 -08:00
response::{IntoResponse, Json},
routing::get,
Router,
};
use diesel::{dsl::insert_into, prelude::*, update};
2025-02-26 13:10:45 -08:00
use serde::Deserialize;
use serde_json::json;
2025-02-26 13:10:47 -08:00
use uuid::Uuid;
use crate::{
api_keys::ApiKey,
app_error::AppError,
2025-02-26 13:10:43 -08:00
app_state::{AppState, DbConn},
2025-03-08 22:18:24 -08:00
channels::Channel,
2025-02-26 13:10:47 -08:00
projects::Project,
2025-03-08 22:18:24 -08:00
schema::{api_keys, messages, projects},
2025-02-26 13:10:47 -08:00
};
pub fn new_router(state: AppState) -> Router<AppState> {
Router::new().route("/say", get(say_get)).with_state(state)
}
#[derive(Deserialize)]
struct SayQuery {
key: Uuid,
project: String,
message: String,
}
async fn say_get(
DbConn(db_conn): DbConn,
Query(query): Query<SayQuery>,
) -> Result<impl IntoResponse, AppError> {
// TODO: do some validation of message contents
let api_key = {
let query_key = query.key.clone();
db_conn
.interact::<_, Result<ApiKey, AppError>>(move |conn| {
update(api_keys::table.filter(ApiKey::with_id(query_key)))
.set(api_keys::last_used_at.eq(diesel::dsl::now))
.returning(ApiKey::as_returning())
.get_result(conn)
.optional()
.context("failed to get API key")?
.ok_or(AppError::ForbiddenError("Key not accepted.".to_string()))
})
.await
.unwrap()?
};
2025-03-08 22:18:24 -08:00
let project = {
let project_name = query.project.to_lowercase();
db_conn
2025-03-08 22:18:24 -08:00
.interact::<_, Result<Project, AppError>>(move |conn| {
conn.transaction(move |conn| {
2025-03-08 22:18:24 -08:00
Ok(
match Project::all()
.filter(Project::with_team(api_key.team_id))
.filter(Project::with_name(project_name.clone()))
.first(conn)
.optional()
.context("failed to load project")?
{
Some(project) => project,
None => insert_into(projects::table)
.values((
projects::id.eq(Uuid::now_v7()),
projects::team_id.eq(api_key.team_id),
projects::name.eq(project_name),
))
.get_result(conn)
.context("failed to insert project")?,
},
)
})
})
.await
.unwrap()?
2025-02-26 13:10:47 -08:00
};
2025-03-08 22:18:24 -08:00
let selected_channels = {
let project = project.clone();
db_conn
.interact::<_, Result<Vec<Channel>, AppError>>(move |conn| {
Ok(project
.selected_channels()
.load(conn)
.context("failed to load selected channels")?)
})
.await
.unwrap()?
};
2025-02-26 13:10:45 -08:00
2025-03-08 22:18:24 -08:00
{
let selected_channels = selected_channels.clone();
db_conn
.interact::<_, Result<_, AppError>>(move |conn| {
for channel in selected_channels {
insert_into(messages::table)
.values((
messages::id.eq(Uuid::now_v7()),
messages::channel_id.eq(&channel.id),
messages::project_id.eq(&project.id),
messages::message.eq(&query.message),
))
.execute(conn)?;
}
Ok(())
})
.await
.unwrap()?;
2025-02-26 13:10:47 -08:00
}
2025-03-08 22:18:24 -08:00
tracing::debug!("queued {} messages", selected_channels.len());
2025-02-26 13:10:45 -08:00
Ok(Json(json!({ "ok": true })))
2025-02-26 13:10:47 -08:00
}