diff --git a/crates/html-router/src/routes/chat/mod.rs b/crates/html-router/src/routes/chat/mod.rs
index ee01bed..824a733 100644
--- a/crates/html-router/src/routes/chat/mod.rs
+++ b/crates/html-router/src/routes/chat/mod.rs
@@ -9,10 +9,9 @@ use axum::{
};
use axum_session_auth::AuthSession;
use axum_session_surreal::SessionSurrealPool;
+use serde::{Deserialize, Serialize};
use surrealdb::{engine::any::Any, Surreal};
-use tracing::info;
-use crate::routes::HtmlError;
use common::{
error::AppError,
storage::types::{
@@ -22,7 +21,11 @@ use common::{
},
};
-use crate::{html_state::HtmlState, page_data, routes::render_template};
+use crate::{
+ html_state::HtmlState,
+ middleware_auth::RequireUser,
+ template_response::{HtmlError, TemplateResponse},
+};
#[derive(Debug, Deserialize)]
pub struct ChatStartParams {
@@ -41,25 +44,19 @@ where
serde_json::from_str(&s).map_err(serde::de::Error::custom)
}
-page_data!(ChatData, "chat/base.html", {
+#[derive(Serialize)]
+pub struct ChatPageData {
user: User,
history: Vec,
conversation: Option,
- conversation_archive: Vec
-});
+ conversation_archive: Vec,
+}
pub async fn show_initialized_chat(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Form(form): Form,
) -> Result {
- info!("Displaying chat start");
-
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/").into_response()),
- };
-
let conversation = Conversation::new(user.id.clone(), "Test".to_owned());
let user_message = Message::new(
@@ -76,69 +73,48 @@ pub async fn show_initialized_chat(
Some(form.references),
);
- let (conversation_result, ai_message_result, user_message_result) = futures::join!(
- state.db.store_item(conversation.clone()),
- state.db.store_item(ai_message.clone()),
- state.db.store_item(user_message.clone())
- );
+ state.db.store_item(conversation.clone()).await?;
+ state.db.store_item(ai_message.clone()).await?;
+ state.db.store_item(user_message.clone()).await?;
- // Check each result individually
- conversation_result.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
- user_message_result.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
- ai_message_result.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
-
- let conversation_archive = User::get_user_conversations(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let conversation_archive = User::get_user_conversations(&user.id, &state.db).await?;
let messages = vec![user_message, ai_message];
- let output = render_template(
- ChatData::template_name(),
- ChatData {
+ let mut response = TemplateResponse::new_template(
+ "chat/base.html",
+ ChatPageData {
history: messages,
user,
conversation_archive,
conversation: Some(conversation.clone()),
},
- state.templates.clone(),
- )?;
+ )
+ .into_response();
- let mut response = output.into_response();
response.headers_mut().insert(
"HX-Push",
HeaderValue::from_str(&format!("/chat/{}", conversation.id)).unwrap(),
);
+
Ok(response)
}
pub async fn show_chat_base(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
) -> Result {
- info!("Displaying empty chat start");
+ let conversation_archive = User::get_user_conversations(&user.id, &state.db).await?;
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/").into_response()),
- };
-
- let conversation_archive = User::get_user_conversations(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
-
- let output = render_template(
- ChatData::template_name(),
- ChatData {
+ Ok(TemplateResponse::new_template(
+ "chat/base.html",
+ ChatPageData {
history: vec![],
user,
conversation_archive,
conversation: None,
},
- state.templates.clone(),
- )?;
-
- Ok(output.into_response())
+ ))
}
#[derive(Deserialize)]
@@ -149,92 +125,61 @@ pub struct NewMessageForm {
pub async fn show_existing_chat(
Path(conversation_id): Path,
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
) -> Result {
- info!("Displaying initialized chat with id: {}", conversation_id);
-
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/").into_response()),
- };
-
- let conversation_archive = User::get_user_conversations(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let conversation_archive = User::get_user_conversations(&user.id, &state.db).await?;
let (conversation, messages) =
Conversation::get_complete_conversation(conversation_id.as_str(), &user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ .await?;
- let output = render_template(
- ChatData::template_name(),
- ChatData {
+ Ok(TemplateResponse::new_template(
+ "chat/base.html",
+ ChatPageData {
history: messages,
user,
conversation: Some(conversation.clone()),
conversation_archive,
},
- state.templates.clone(),
- )?;
-
- Ok(output.into_response())
+ ))
}
pub async fn new_user_message(
Path(conversation_id): Path,
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Form(form): Form,
) -> Result {
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/").into_response()),
- };
-
let conversation: Conversation = state
.db
.get_item(&conversation_id)
- .await
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?
- .ok_or_else(|| {
- HtmlError::new(
- AppError::NotFound("Conversation was not found".to_string()),
- state.templates.clone(),
- )
- })?;
+ .await?
+ .ok_or_else(|| AppError::NotFound("Conversation was not found".into()))?;
if conversation.user_id != user.id {
- return Err(HtmlError::new(
- AppError::Auth("The user does not have permission for this conversation".to_string()),
- state.templates.clone(),
- ));
+ return Ok(TemplateResponse::unauthorized().into_response());
};
let user_message = Message::new(conversation_id, MessageRole::User, form.content, None);
- state
- .db
- .store_item(user_message.clone())
- .await
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
+ state.db.store_item(user_message.clone()).await?;
#[derive(Serialize)]
struct SSEResponseInitData {
user_message: Message,
}
- let output = render_template(
+ let mut response = TemplateResponse::new_template(
"chat/streaming_response.html",
SSEResponseInitData { user_message },
- state.templates.clone(),
- )?;
+ )
+ .into_response();
- let mut response = output.into_response();
response.headers_mut().insert(
"HX-Push",
HeaderValue::from_str(&format!("/chat/{}", conversation.id)).unwrap(),
);
+
Ok(response)
}
@@ -256,36 +201,27 @@ pub async fn new_chat_user_message(
None,
);
- state
- .db
- .store_item(conversation.clone())
- .await
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
- state
- .db
- .store_item(user_message.clone())
- .await
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
+ state.db.store_item(conversation.clone()).await?;
+ state.db.store_item(user_message.clone()).await?;
#[derive(Serialize)]
struct SSEResponseInitData {
user_message: Message,
conversation: Conversation,
}
-
- let output = render_template(
+ let mut response = TemplateResponse::new_template(
"chat/new_chat_first_response.html",
SSEResponseInitData {
user_message,
conversation: conversation.clone(),
},
- state.templates.clone(),
- )?;
+ )
+ .into_response();
- let mut response = output.into_response();
response.headers_mut().insert(
"HX-Push",
HeaderValue::from_str(&format!("/chat/{}", conversation.id)).unwrap(),
);
+
Ok(response)
}
diff --git a/crates/html-router/src/routes/chat/references.rs b/crates/html-router/src/routes/chat/references.rs
index 229fe02..ccdceb4 100644
--- a/crates/html-router/src/routes/chat/references.rs
+++ b/crates/html-router/src/routes/chat/references.rs
@@ -1,50 +1,33 @@
use axum::{
extract::{Path, State},
- response::{IntoResponse, Redirect},
+ response::IntoResponse,
};
-use axum_session_auth::AuthSession;
-use axum_session_surreal::SessionSurrealPool;
use serde::Serialize;
-use surrealdb::{engine::any::Any, Surreal};
-use tracing::info;
-use crate::routes::HtmlError;
use common::{
error::AppError,
storage::types::{knowledge_entity::KnowledgeEntity, user::User},
};
-use crate::{html_state::HtmlState, routes::render_template};
+use crate::{
+ html_state::HtmlState,
+ middleware_auth::RequireUser,
+ template_response::{HtmlError, TemplateResponse},
+};
pub async fn show_reference_tooltip(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Path(reference_id): Path,
) -> Result {
- info!("Showing reference");
-
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/").into_response()),
- };
-
let entity: KnowledgeEntity = state
.db
.get_item(&reference_id)
- .await
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?
- .ok_or_else(|| {
- HtmlError::new(
- AppError::NotFound("Item was not found".to_string()),
- state.templates.clone(),
- )
- })?;
+ .await?
+ .ok_or_else(|| AppError::NotFound("Item was not found".to_string()))?;
if entity.user_id != user.id {
- return Err(HtmlError::new(
- AppError::Auth("You dont have access to this entity".to_string()),
- state.templates.clone(),
- ));
+ return Ok(TemplateResponse::unauthorized());
}
#[derive(Serialize)]
@@ -53,11 +36,8 @@ pub async fn show_reference_tooltip(
user: User,
}
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"chat/reference_tooltip.html",
ReferenceTooltipData { entity, user },
- state.templates.clone(),
- )?;
-
- Ok(output.into_response())
+ ))
}
diff --git a/crates/html-router/src/routes/content/mod.rs b/crates/html-router/src/routes/content/mod.rs
index e5a4d77..cced34e 100644
--- a/crates/html-router/src/routes/content/mod.rs
+++ b/crates/html-router/src/routes/content/mod.rs
@@ -1,46 +1,36 @@
use axum::{
extract::{Path, State},
- response::{IntoResponse, Redirect},
+ response::IntoResponse,
};
-use axum_session_auth::AuthSession;
-use axum_session_surreal::SessionSurrealPool;
-use surrealdb::{engine::any::Any, Surreal};
+use serde::Serialize;
use common::storage::types::{text_content::TextContent, user::User};
-use crate::{error::HtmlError, html_state::HtmlState, page_data};
+use crate::{
+ html_state::HtmlState,
+ middleware_auth::RequireUser,
+ template_response::{HtmlError, TemplateResponse},
+};
-use super::render_template;
-
-page_data!(ContentPageData, "content/base.html", {
+#[derive(Serialize)]
+pub struct ContentPageData {
user: User,
- text_contents: Vec
-});
+ text_contents: Vec,
+}
pub async fn show_content_page(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
+ let text_contents = User::get_text_contents(&user.id, &state.db).await?;
- let text_contents = User::get_text_contents(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
-
- let output = render_template(
- ContentPageData::template_name(),
+ Ok(TemplateResponse::new_template(
+ "content/base.html",
ContentPageData {
user,
text_contents,
},
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
#[derive(Serialize)]
@@ -51,57 +41,33 @@ pub struct TextContentEditModal {
pub async fn show_text_content_edit_form(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Path(id): Path,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
+ let text_content = User::get_and_validate_text_content(&id, &user.id, &state.db).await?;
- let text_content = User::get_and_validate_text_content(&id, &user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
-
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"content/edit_text_content_modal.html",
TextContentEditModal { user, text_content },
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
pub async fn patch_text_content(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Path(id): Path,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
-
- let text_content = User::get_and_validate_text_content(&id, &user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let text_content = User::get_and_validate_text_content(&id, &user.id, &state.db).await?;
// ADD FUNCTION TO PATCH CONTENT
- let text_contents = User::get_text_contents(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let text_contents = User::get_text_contents(&user.id, &state.db).await?;
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"content/content_list.html",
ContentPageData {
user,
text_contents,
},
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
diff --git a/crates/html-router/src/routes/index.rs b/crates/html-router/src/routes/index.rs
index 28708a3..ccb40ae 100644
--- a/crates/html-router/src/routes/index.rs
+++ b/crates/html-router/src/routes/index.rs
@@ -83,7 +83,7 @@ pub async fn delete_text_content(
let text_content = get_and_validate_text_content(&state, &id, &user).await?;
// Perform concurrent deletions
- join!(
+ let (_res1, _res2, _res3, _res4, _res5) = join!(
async {
if let Some(file_info) = text_content.file_info {
FileInfo::delete_by_id(&file_info.id, &state.db).await
diff --git a/crates/html-router/src/routes/ingress_form.rs b/crates/html-router/src/routes/ingress_form.rs
index fafb305..e2a8fec 100644
--- a/crates/html-router/src/routes/ingress_form.rs
+++ b/crates/html-router/src/routes/ingress_form.rs
@@ -1,12 +1,10 @@
use axum::{
extract::State,
- response::{Html, IntoResponse, Redirect},
+ response::{Html, IntoResponse},
};
-use axum_session_auth::AuthSession;
-use axum_session_surreal::SessionSurrealPool;
use axum_typed_multipart::{FieldData, TryFromMultipart, TypedMultipart};
use futures::{future::try_join_all, TryFutureExt};
-use surrealdb::{engine::any::Any, Surreal};
+use serde::Serialize;
use tempfile::NamedTempFile;
use tracing::info;
@@ -19,47 +17,32 @@ use common::{
};
use crate::{
- error::{HtmlError, IntoHtmlError},
html_state::HtmlState,
- page_data,
- routes::{index::ActiveJobsData, render_block},
+ middleware_auth::RequireUser,
+ routes::index::ActiveJobsData,
+ template_response::{HtmlError, TemplateResponse},
};
-use super::render_template;
-
-#[derive(Serialize)]
-pub struct ShowIngressFormData {
- user_categories: Vec,
-}
-
pub async fn show_ingress_form(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
) -> Result {
- if !auth.is_authenticated() {
- return Ok(Redirect::to("/").into_response());
+ let user_categories = User::get_user_categories(&user.id, &state.db).await?;
+
+ #[derive(Serialize)]
+ pub struct ShowIngressFormData {
+ user_categories: Vec,
}
- let user_categories = User::get_user_categories(&auth.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
-
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"index/signed_in/ingress_modal.html",
ShowIngressFormData { user_categories },
- state.templates.clone(),
- )?;
-
- Ok(output.into_response())
+ ))
}
pub async fn hide_ingress_form(
- auth: AuthSession, Surreal>,
+ RequireUser(_user): RequireUser,
) -> Result {
- if !auth.is_authenticated() {
- return Ok(Redirect::to("/").into_response());
- }
-
Ok(Html(
"Add Content",
)
@@ -76,43 +59,39 @@ pub struct IngressParams {
pub files: Vec>,
}
-page_data!(IngressFormData, "ingress_form.html", {
- instructions: String,
- content: String,
- category: String,
- error: String,
-});
-
pub async fn process_ingress_form(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
TypedMultipart(input): TypedMultipart,
) -> Result {
- let user = auth.current_user.ok_or_else(|| {
- AppError::Auth("You must be signed in".to_string()).with_template(state.templates.clone())
- })?;
+ #[derive(Serialize)]
+ pub struct IngressFormData {
+ instructions: String,
+ content: String,
+ category: String,
+ error: String,
+ }
- if input.content.clone().is_some_and(|c| c.len() < 2) && input.files.is_empty() {
- let output = render_template(
- IngressFormData::template_name(),
+ if input.content.as_ref().map_or(true, |c| c.len() < 2) && input.files.is_empty() {
+ return Ok(TemplateResponse::new_template(
+ "index/signed_in/ingress_form.html",
IngressFormData {
instructions: input.instructions.clone(),
- content: input.content.clone().unwrap(),
+ content: input.content.clone().unwrap_or_default(),
category: input.category.clone(),
error: "You need to either add files or content".to_string(),
},
- state.templates.clone(),
- )?;
-
- return Ok(output.into_response());
+ ));
}
info!("{:?}", input);
- let file_infos = try_join_all(input.files.into_iter().map(|file| {
- FileInfo::new(file, &state.db, &user.id)
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))
- }))
+ let file_infos = try_join_all(
+ input
+ .files
+ .into_iter()
+ .map(|file| FileInfo::new(file, &state.db, &user.id).map_err(|e| AppError::from(e))),
+ )
.await?;
let payloads = IngestionPayload::create_ingestion_payload(
@@ -121,8 +100,7 @@ pub async fn process_ingress_form(
input.category,
file_infos,
user.id.as_str(),
- )
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ )?;
let futures: Vec<_> = payloads
.into_iter()
@@ -131,25 +109,17 @@ pub async fn process_ingress_form(
})
.collect();
- try_join_all(futures)
- .await
- .map_err(AppError::from)
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ try_join_all(futures).await?;
// Update the active jobs page with the newly created job
- let active_jobs = User::get_unfinished_ingestion_tasks(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let active_jobs = User::get_unfinished_ingestion_tasks(&user.id, &state.db).await?;
- let output = render_block(
+ Ok(TemplateResponse::new_partial(
"index/signed_in/active_jobs.html",
"active_jobs_section",
ActiveJobsData {
user: user.clone(),
active_jobs,
},
- state.templates.clone(),
- )?;
-
- Ok(output.into_response())
+ ))
}
diff --git a/crates/html-router/src/routes/knowledge/mod.rs b/crates/html-router/src/routes/knowledge/mod.rs
index 35f1228..3cbc365 100644
--- a/crates/html-router/src/routes/knowledge/mod.rs
+++ b/crates/html-router/src/routes/knowledge/mod.rs
@@ -1,55 +1,42 @@
use axum::{
extract::{Path, State},
- response::{IntoResponse, Redirect},
+ response::IntoResponse,
Form,
};
-use axum_session_auth::AuthSession;
-use axum_session_surreal::SessionSurrealPool;
use plotly::{
common::{Line, Marker, Mode},
layout::{Axis, Camera, LayoutScene, ProjectionType},
Layout, Plot, Scatter3D,
};
-use surrealdb::{engine::any::Any, Surreal};
-use tracing::info;
+use serde::{Deserialize, Serialize};
-use common::{
- error::AppError,
- storage::types::{
- knowledge_entity::{KnowledgeEntity, KnowledgeEntityType},
- knowledge_relationship::KnowledgeRelationship,
- user::User,
- },
+use common::storage::types::{
+ knowledge_entity::{KnowledgeEntity, KnowledgeEntityType},
+ knowledge_relationship::KnowledgeRelationship,
+ user::User,
};
-use crate::{error::HtmlError, html_state::HtmlState, page_data, routes::render_template};
-
-page_data!(KnowledgeBaseData, "knowledge/base.html", {
- entities: Vec,
- relationships: Vec,
- user: User,
- plot_html: String
-});
+use crate::{
+ html_state::HtmlState,
+ middleware_auth::RequireUser,
+ template_response::{HtmlError, TemplateResponse},
+};
pub async fn show_knowledge_page(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
+ #[derive(Serialize)]
+ pub struct KnowledgeBaseData {
+ entities: Vec,
+ relationships: Vec,
+ user: User,
+ plot_html: String,
+ }
- let entities = User::get_knowledge_entities(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let entities = User::get_knowledge_entities(&user.id, &state.db).await?;
- info!("Got entities ok");
-
- let relationships = User::get_knowledge_relationships(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let relationships = User::get_knowledge_relationships(&user.id, &state.db).await?;
let mut plot = Plot::new();
@@ -124,40 +111,32 @@ pub async fn show_knowledge_page(
.plot_background_color("rbga(0,0,0,0)");
plot.set_layout(layout);
+
// Convert to HTML
let html = plot.to_html();
- let output = render_template(
- KnowledgeBaseData::template_name(),
+ Ok(TemplateResponse::new_template(
+ "knowledge/base.html",
KnowledgeBaseData {
entities,
relationships,
user,
plot_html: html,
},
- state.templates,
- )?;
-
- Ok(output.into_response())
-}
-
-#[derive(Serialize)]
-pub struct EntityData {
- entity: KnowledgeEntity,
- entity_types: Vec,
- user: User,
+ ))
}
pub async fn show_edit_knowledge_entity_form(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Path(id): Path,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
+ #[derive(Serialize)]
+ pub struct EntityData {
+ entity: KnowledgeEntity,
+ entity_types: Vec,
+ user: User,
+ }
// Get entity types
let entity_types: Vec = KnowledgeEntityType::variants()
@@ -166,27 +145,16 @@ pub async fn show_edit_knowledge_entity_form(
.collect();
// Get the entity and validate ownership
- let entity = User::get_and_validate_knowledge_entity(&id, &user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let entity = User::get_and_validate_knowledge_entity(&id, &user.id, &state.db).await?;
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"knowledge/edit_knowledge_entity_modal.html",
EntityData {
entity,
user,
entity_types,
},
- state.templates,
- )?;
-
- Ok(output.into_response())
-}
-
-#[derive(Serialize)]
-pub struct EntityListData {
- entities: Vec,
- user: User,
+ ))
}
#[derive(Debug, Deserialize)]
@@ -197,21 +165,19 @@ pub struct PatchKnowledgeEntityParams {
pub description: String,
}
+#[derive(Serialize)]
+pub struct EntityListData {
+ entities: Vec,
+ user: User,
+}
+
pub async fn patch_knowledge_entity(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Form(form): Form,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
-
// Get the existing entity and validate that the user is allowed
- User::get_and_validate_knowledge_entity(&form.id, &user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ User::get_and_validate_knowledge_entity(&form.id, &user.id, &state.db).await?;
let entity_type: KnowledgeEntityType = KnowledgeEntityType::from(form.entity_type);
@@ -224,60 +190,36 @@ pub async fn patch_knowledge_entity(
&state.db,
&state.openai_client,
)
- .await
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
+ .await?;
// Get updated list of entities
- let entities = User::get_knowledge_entities(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let entities = User::get_knowledge_entities(&user.id, &state.db).await?;
// Render updated list
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"knowledge/entity_list.html",
EntityListData { entities, user },
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
pub async fn delete_knowledge_entity(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Path(id): Path,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
-
// Get the existing entity and validate that the user is allowed
- User::get_and_validate_knowledge_entity(&id, &user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ User::get_and_validate_knowledge_entity(&id, &user.id, &state.db).await?;
// Delete the entity
- state
- .db
- .delete_item::(&id)
- .await
- .map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
+ state.db.delete_item::(&id).await?;
// Get updated list of entities
- let entities = User::get_knowledge_entities(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let entities = User::get_knowledge_entities(&user.id, &state.db).await?;
- // Render updated list
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"knowledge/entity_list.html",
EntityListData { entities, user },
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
#[derive(Serialize)]
@@ -288,40 +230,25 @@ pub struct RelationshipTableData {
pub async fn delete_knowledge_relationship(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Path(id): Path,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
-
// GOTTA ADD AUTH VALIDATION
- KnowledgeRelationship::delete_relationship_by_id(&id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ KnowledgeRelationship::delete_relationship_by_id(&id, &state.db).await?;
- let entities = User::get_knowledge_entities(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let entities = User::get_knowledge_entities(&user.id, &state.db).await?;
- let relationships = User::get_knowledge_relationships(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let relationships = User::get_knowledge_relationships(&user.id, &state.db).await?;
// Render updated list
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"knowledge/relationship_table.html",
RelationshipTableData {
entities,
relationships,
},
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
#[derive(Deserialize)]
@@ -333,15 +260,9 @@ pub struct SaveKnowledgeRelationshipInput {
pub async fn save_knowledge_relationship(
State(state): State,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
Form(form): Form,
) -> Result {
- // Early return if the user is not authenticated
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
-
// Construct relationship
let relationship = KnowledgeRelationship::new(
form.in_,
@@ -351,28 +272,18 @@ pub async fn save_knowledge_relationship(
form.relationship_type,
);
- relationship
- .store_relationship(&state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ relationship.store_relationship(&state.db).await?;
- let entities = User::get_knowledge_entities(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let entities = User::get_knowledge_entities(&user.id, &state.db).await?;
- let relationships = User::get_knowledge_relationships(&user.id, &state.db)
- .await
- .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
+ let relationships = User::get_knowledge_relationships(&user.id, &state.db).await?;
// Render updated list
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"knowledge/relationship_table.html",
RelationshipTableData {
entities,
relationships,
},
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
diff --git a/crates/html-router/src/routes/mod.rs b/crates/html-router/src/routes/mod.rs
index 46aab45..0b766e9 100644
--- a/crates/html-router/src/routes/mod.rs
+++ b/crates/html-router/src/routes/mod.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
use axum::response::Html;
use minijinja_autoreload::AutoReloader;
-use crate::error::{HtmlError, IntoHtmlError};
+use crate::template_response::HtmlError;
pub mod account;
pub mod admin_panel;
@@ -19,73 +19,19 @@ pub mod signin;
pub mod signout;
pub mod signup;
-// pub trait PageData {
-// fn template_name() -> &'static str;
-// }
+// Helper function for render_template
+pub fn render_template(
+ template_name: &str,
+ context: T,
+ templates: Arc,
+) -> Result, HtmlError>
+where
+ T: serde::Serialize,
+{
+ let env = templates.acquire_env().unwrap();
+ let tmpl = env.get_template(template_name).unwrap();
+ let context = minijinja::Value::from_serialize(&context);
+ let output = tmpl.render(context).unwrap();
-// // Helper function for render_template
-// pub fn render_template(
-// template_name: &str,
-// context: T,
-// templates: Arc,
-// ) -> Result, HtmlError>
-// where
-// T: serde::Serialize,
-// {
-// let env = templates
-// .acquire_env()
-// .map_err(|e| e.with_template(templates.clone()))?;
-// let tmpl = env
-// .get_template(template_name)
-// .map_err(|e| e.with_template(templates.clone()))?;
-// let context = minijinja::Value::from_serialize(&context);
-// let output = tmpl
-// .render(context)
-// .map_err(|e| e.with_template(templates.clone()))?;
-// Ok(Html(output))
-// }
-
-// pub fn render_block(
-// template_name: &str,
-// block: &str,
-// context: T,
-// templates: Arc,
-// ) -> Result, HtmlError>
-// where
-// T: serde::Serialize,
-// {
-// let env = templates
-// .acquire_env()
-// .map_err(|e| e.with_template(templates.clone()))?;
-// let tmpl = env
-// .get_template(template_name)
-// .map_err(|e| e.with_template(templates.clone()))?;
-
-// let context = minijinja::Value::from_serialize(&context);
-// let output = tmpl
-// .eval_to_state(context)
-// .map_err(|e| e.with_template(templates.clone()))?
-// .render_block(block)
-// .map_err(|e| e.with_template(templates.clone()))?;
-
-// Ok(output.into())
-// }
-
-// #[macro_export]
-// macro_rules! page_data {
-// ($name:ident, $template_name:expr, {$($(#[$attr:meta])* $field:ident: $ty:ty),*$(,)?}) => {
-// use serde::{Serialize, Deserialize};
-// use $crate::routes::PageData;
-
-// #[derive(Debug, Deserialize, Serialize)]
-// pub struct $name {
-// $($(#[$attr])* pub $field: $ty),*
-// }
-
-// impl PageData for $name {
-// fn template_name() -> &'static str {
-// $template_name
-// }
-// }
-// };
-// }
+ Ok(Html(output))
+}
diff --git a/crates/html-router/src/routes/search_result.rs b/crates/html-router/src/routes/search_result.rs
index cced2d3..df2f778 100644
--- a/crates/html-router/src/routes/search_result.rs
+++ b/crates/html-router/src/routes/search_result.rs
@@ -1,69 +1,42 @@
use axum::{
extract::{Query, State},
- response::{IntoResponse, Redirect},
+ response::IntoResponse,
};
-use axum_session_auth::AuthSession;
-use axum_session_surreal::SessionSurrealPool;
+use composite_retrieval::answer_retrieval::get_answer_with_references;
use serde::{Deserialize, Serialize};
-use surrealdb::{engine::any::Any, Surreal};
-use tracing::info;
-use crate::routes::HtmlError;
-use common::storage::types::user::User;
+use crate::{
+ html_state::HtmlState,
+ middleware_auth::RequireUser,
+ template_response::{HtmlError, TemplateResponse},
+};
-use crate::{html_state::HtmlState, routes::render_template};
#[derive(Deserialize)]
pub struct SearchParams {
query: String,
}
-#[derive(Serialize)]
-pub struct AnswerData {
- user_query: String,
- answer_content: String,
- answer_references: Vec,
-}
-
pub async fn search_result_handler(
State(state): State,
Query(query): Query,
- auth: AuthSession, Surreal>,
+ RequireUser(user): RequireUser,
) -> Result {
- info!("Displaying search results");
+ #[derive(Serialize)]
+ pub struct AnswerData {
+ user_query: String,
+ answer_content: String,
+ answer_references: Vec,
+ }
- let user = match auth.current_user {
- Some(user) => user,
- None => return Ok(Redirect::to("/signin").into_response()),
- };
+ let answer =
+ get_answer_with_references(&state.db, &state.openai_client, &query.query, &user.id).await?;
- // let answer = get_answer_with_references(
- // &state.surreal_db_client,
- // &state.openai_client,
- // &query.query,
- // &user.id,
- // )
- // .await
- // .map_err(|e| HtmlError::new(e, state.templates.clone()))?;
-
- let answer = "The Minne project is focused on simplifying knowledge management through features such as easy capture, smart analysis, and visualization of connections between ideas. It includes various functionalities like the Smart Analysis Feature, which provides content analysis and organization, and the Easy Capture Feature, which allows users to effortlessly capture and retrieve knowledge in various formats. Additionally, it offers tools like Knowledge Graph Visualization to enhance understanding and organization of knowledge. The project also emphasizes a user-friendly onboarding experience and mobile-friendly options for accessing its services.".to_string();
-
- let references = vec![
- "i81cd5be8-557c-4b2b-ba3a-4b8d28e74b9b".to_string(),
- "5f72a724-d7a3-467d-8783-7cca6053ddc7".to_string(),
- "ad106a1f-ccda-415e-9e87-c3a34e202624".to_string(),
- "8797b57d-094d-4ee9-a3a7-c3195b246254".to_string(),
- "69763f43-82e6-4cb5-ba3e-f6da13777dab".to_string(),
- ];
-
- let output = render_template(
+ Ok(TemplateResponse::new_template(
"index/signed_in/search_response.html",
AnswerData {
user_query: query.query,
- answer_content: answer,
- answer_references: references,
+ answer_content: answer.content,
+ answer_references: answer.references,
},
- state.templates,
- )?;
-
- Ok(output.into_response())
+ ))
}
diff --git a/crates/html-router/src/template_response.rs b/crates/html-router/src/template_response.rs
index 1bfe055..e226552 100644
--- a/crates/html-router/src/template_response.rs
+++ b/crates/html-router/src/template_response.rs
@@ -10,7 +10,7 @@ use minijinja_autoreload::AutoReloader;
use serde::Serialize;
use std::sync::Arc;
-use crate::{html_state::HtmlState, AuthSessionType};
+use crate::html_state::HtmlState;
// Enum for template types
#[derive(Clone)]
@@ -113,10 +113,8 @@ impl IntoResponse for TemplateResponse {
}
}
-// Wrapper to avoid recursion
struct TemplateStateWrapper {
state: HtmlState,
- auth: AuthSessionType,
template_response: TemplateResponse,
}
@@ -228,7 +226,6 @@ fn fallback_error() -> Html {
pub async fn with_template_response(
State(state): State,
- auth: AuthSessionType,
response: Response,
) -> Response {
// Clone the TemplateResponse from extensions
@@ -237,7 +234,6 @@ pub async fn with_template_response(
if let Some(template_response) = template_response {
TemplateStateWrapper {
state,
- auth,
template_response,
}
.into_response()
@@ -249,7 +245,6 @@ pub async fn with_template_response(
// Define HtmlError
pub enum HtmlError {
AppError(AppError),
- TemplateError(String),
}
// Conversion from AppError to HtmlError
@@ -282,7 +277,6 @@ impl IntoResponse for HtmlError {
};
template_response.into_response()
}
- HtmlError::TemplateError(_) => TemplateResponse::server_error().into_response(),
}
}
}