Skip to content

Commit

Permalink
Add /event/<id> endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
yukibtc committed Jun 11, 2024
1 parent 6d1649f commit 06a68e7
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 20 deletions.
7 changes: 6 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ where
pub enum AppError {
// Too many filters were provided in the request
FilterError(usize),
// Too many filters were provided in the request
EventIdNotFound,
// The request body contained invalid JSON
JsonRejection(JsonRejection),
// An Nostr Client error occurred
Expand All @@ -38,8 +40,11 @@ impl IntoResponse for AppError {
let (status, message) = match self {
AppError::FilterError(max_filters) => (
StatusCode::BAD_REQUEST,
format!("Too many filters (max allowed {})", max_filters).to_string(),
format!("Too many filters (max allowed {max_filters})"),
),
AppError::EventIdNotFound => {
(StatusCode::BAD_REQUEST, String::from("Event ID not found"))
}
AppError::JsonRejection(rejection) => (rejection.status(), rejection.body_text()),
AppError::NostrClientError(err) => (StatusCode::BAD_REQUEST, err.to_string()),
AppError::NostrEventError(err) => (StatusCode::BAD_REQUEST, err.to_string()),
Expand Down
61 changes: 42 additions & 19 deletions src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
// Copyright (c) 2023 Nostr Development Kit Devs
// Distributed under the MIT software license

use axum::extract::State;
use axum::extract::{Path, State};
use axum::response::Json;
use nostr_sdk::hashes::sha256::Hash as Sha256Hash;
use nostr_sdk::hashes::Hash;
use nostr_sdk::{Event, Filter};
use nostr_sdk::{Event, EventId, Filter};
use redis::AsyncCommands;
use serde::Deserialize;
use serde_json::{json, Value};

use crate::error::{AppError, AppJson};
use crate::AppState;

#[derive(Deserialize)]
pub struct GetEventByIdParams {
event_id: EventId,
}

pub async fn ping() -> Json<Value> {
Json(json!({
"success": true,
Expand Down Expand Up @@ -44,39 +50,56 @@ pub async fn get_events(
return Err(AppError::FilterError(state.config.limit.max_filters));
}

let events: Vec<Event> = get_events_by_filters(state, filters).await?;

Ok(AppJson(json!({
"success": true,
"message": format!("Got {} events", events.len()),
"data": events,
})))
}

pub async fn get_event_by_id(
state: State<AppState>,
path: Path<GetEventByIdParams>,
) -> Result<AppJson<Value>, AppError> {
let event_id: EventId = path.event_id;
let filter: Filter = Filter::new().id(event_id);
let filters: Vec<Filter> = vec![filter];
let events: Vec<Event> = get_events_by_filters(state, filters).await?;
let event: &Event = events.first().ok_or(AppError::EventIdNotFound)?;
Ok(AppJson(json!({
"success": true,
"message": "Got 1 events",
"data": event,
})))
}

async fn get_events_by_filters(
state: State<AppState>,
filters: Vec<Filter>,
) -> Result<Vec<Event>, AppError> {
if let Some(redis) = &state.redis {
let mut connection = redis.get_multiplexed_async_connection().await.unwrap();
let mut connection = redis.get_multiplexed_async_connection().await.unwrap(); // TODO: remove unwrap
let hash: String = Sha256Hash::hash(format!("{filters:?}").as_bytes()).to_string();
let exists = connection.exists::<&str, bool>(&hash).await?;
if exists {
let result = connection.get(&hash).await?;
let bytes: Vec<u8> = result;
let events: Vec<Event> = bincode::deserialize(&bytes).unwrap();
Ok(AppJson(json!({
"success": true,
"message": format!("Got {} events", events.len()),
"data": events,
})))
let events: Vec<Event> = bincode::deserialize(&bytes).unwrap(); // TODO: remove unwrap
Ok(events)
} else {
let events = state.client.get_events_of(filters, None).await?;
let encoded: Vec<u8> = bincode::serialize(&events).unwrap();
let _: () = connection
.set_ex(hash, encoded, state.config.redis.expiration)
.await
.unwrap();
Ok(AppJson(json!({
"success": true,
"message": format!("Got {} events", events.len()),
"data": events,
})))
Ok(events)
}
} else {
// TODO: add a timeout
let events = state.client.get_events_of(filters, None).await?;
Ok(AppJson(json!({
"success": true,
"message": format!("Got {} events", events.len()),
"data": events,
})))
Ok(events)
}
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ async fn main() -> Result<()> {
let app = Router::new()
.route("/ping", get(handler::ping))
.route("/event", post(handler::publish_event))
.route("/event/:event_id", get(handler::get_event_by_id))
.route("/events", post(handler::get_events))
.layer(if config.network.permissive_cors {
CorsLayer::permissive()
Expand Down

0 comments on commit 06a68e7

Please sign in to comment.