mirror of
https://github.com/perstarkse/minne.git
synced 2026-03-21 00:49:54 +01:00
in progress, routers and main split up
This commit is contained in:
179
Cargo.lock
generated
179
Cargo.lock
generated
@@ -156,6 +156,21 @@ version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "api-router"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"axum_typed_multipart",
|
||||
"common",
|
||||
"futures",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.4.0"
|
||||
@@ -1029,6 +1044,50 @@ dependencies = [
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-openai",
|
||||
"async-stream",
|
||||
"axum",
|
||||
"axum-htmx",
|
||||
"axum_session",
|
||||
"axum_session_auth",
|
||||
"axum_session_surreal",
|
||||
"axum_typed_multipart",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"config",
|
||||
"futures",
|
||||
"json-stream-parser",
|
||||
"lettre",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"minijinja",
|
||||
"minijinja-autoreload",
|
||||
"minijinja-contrib",
|
||||
"mockall",
|
||||
"plotly",
|
||||
"reqwest",
|
||||
"scraper",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"surrealdb",
|
||||
"tempfile",
|
||||
"text-splitter",
|
||||
"thiserror",
|
||||
"tiktoken-rs",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@@ -1981,6 +2040,35 @@ dependencies = [
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html-router"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-openai",
|
||||
"async-stream",
|
||||
"axum",
|
||||
"axum-htmx",
|
||||
"axum_session",
|
||||
"axum_session_auth",
|
||||
"axum_session_surreal",
|
||||
"axum_typed_multipart",
|
||||
"chrono-tz",
|
||||
"common",
|
||||
"futures",
|
||||
"json-stream-parser",
|
||||
"minijinja",
|
||||
"minijinja-autoreload",
|
||||
"minijinja-contrib",
|
||||
"plotly",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"surrealdb",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.27.0"
|
||||
@@ -2626,6 +2714,53 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "main"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api-router",
|
||||
"async-openai",
|
||||
"async-stream",
|
||||
"axum",
|
||||
"axum-htmx",
|
||||
"axum_session",
|
||||
"axum_session_auth",
|
||||
"axum_session_surreal",
|
||||
"axum_typed_multipart",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"common",
|
||||
"config",
|
||||
"futures",
|
||||
"html-router",
|
||||
"json-stream-parser",
|
||||
"lettre",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"minijinja",
|
||||
"minijinja-autoreload",
|
||||
"minijinja-contrib",
|
||||
"mockall",
|
||||
"plotly",
|
||||
"reqwest",
|
||||
"scraper",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"surrealdb",
|
||||
"tempfile",
|
||||
"text-splitter",
|
||||
"thiserror",
|
||||
"tiktoken-rs",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
@@ -6034,47 +6169,3 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zettle_db"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-openai",
|
||||
"async-stream",
|
||||
"axum",
|
||||
"axum-htmx",
|
||||
"axum_session",
|
||||
"axum_session_auth",
|
||||
"axum_session_surreal",
|
||||
"axum_typed_multipart",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"config",
|
||||
"futures",
|
||||
"json-stream-parser",
|
||||
"lettre",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"minijinja",
|
||||
"minijinja-autoreload",
|
||||
"minijinja-contrib",
|
||||
"mockall",
|
||||
"plotly",
|
||||
"reqwest",
|
||||
"scraper",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"surrealdb",
|
||||
"tempfile",
|
||||
"text-splitter",
|
||||
"thiserror",
|
||||
"tiktoken-rs",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
64
Cargo.toml
64
Cargo.toml
@@ -1,54 +1,16 @@
|
||||
[package]
|
||||
name = "zettle_db"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
[workspace]
|
||||
members = [
|
||||
"crates/main",
|
||||
"crates/common",
|
||||
"crates/api-router"
|
||||
, "crates/html-router"]
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.94"
|
||||
async-openai = "0.24.1"
|
||||
async-stream = "0.3.6"
|
||||
axum = { version = "0.7.5", features = ["multipart", "macros"] }
|
||||
axum-htmx = "0.6.0"
|
||||
axum_session = "0.14.4"
|
||||
axum_session_auth = "0.14.1"
|
||||
axum_session_surreal = "0.2.1"
|
||||
axum_typed_multipart = "0.12.1"
|
||||
chrono = { version = "0.4.39", features = ["serde"] }
|
||||
chrono-tz = "0.10.1"
|
||||
config = "0.15.4"
|
||||
futures = "0.3.31"
|
||||
json-stream-parser = "0.1.4"
|
||||
lettre = { version = "0.11.11", features = ["rustls-tls"] }
|
||||
mime = "0.3.17"
|
||||
mime_guess = "2.0.5"
|
||||
minijinja = { version = "2.5.0", features = ["loader", "multi_template"] }
|
||||
minijinja-autoreload = "2.5.0"
|
||||
minijinja-contrib = { version = "2.6.0", features = ["datetime", "timezone"] }
|
||||
mockall = "0.13.0"
|
||||
plotly = "0.12.1"
|
||||
reqwest = {version = "0.12.12", features = ["charset", "json"]}
|
||||
scraper = "0.22.0"
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
serde_json = "1.0.128"
|
||||
sha2 = "0.10.8"
|
||||
surrealdb = "2.0.4"
|
||||
tempfile = "3.12.0"
|
||||
text-splitter = "0.18.1"
|
||||
thiserror = "1.0.63"
|
||||
tiktoken-rs = "0.6.0"
|
||||
[workspace.dependencies]
|
||||
tokio = { version = "1.40.0", features = ["full"] }
|
||||
tower-http = { version = "0.6.2", features = ["fs"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
axum = { version = "0.7.5", features = ["multipart", "macros"] }
|
||||
serde_json = "1.0.128"
|
||||
thiserror = "1.0.63"
|
||||
anyhow = "1.0.94"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
url = { version = "2.5.2", features = ["serde"] }
|
||||
uuid = { version = "1.10.0", features = ["v4", "serde"] }
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "server"
|
||||
path = "src/bin/server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "worker"
|
||||
path = "src/bin/worker.rs"
|
||||
|
||||
|
||||
1053
assets/style.css
1053
assets/style.css
File diff suppressed because it is too large
Load Diff
18
crates/api-router/Cargo.toml
Normal file
18
crates/api-router/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "api-router"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# Workspace dependencies
|
||||
tokio = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
axum = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
|
||||
tempfile = "3.12.0"
|
||||
futures = "0.3.31"
|
||||
axum_typed_multipart = "0.12.1"
|
||||
|
||||
common = { path = "../common" }
|
||||
33
crates/api-router/src/api_state.rs
Normal file
33
crates/api-router/src/api_state.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::{ingress::jobqueue::JobQueue, storage::db::SurrealDbClient, utils::config::AppConfig};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApiState {
|
||||
pub surreal_db_client: Arc<SurrealDbClient>,
|
||||
pub job_queue: Arc<JobQueue>,
|
||||
}
|
||||
|
||||
impl ApiState {
|
||||
pub async fn new(config: &AppConfig) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let surreal_db_client = Arc::new(
|
||||
SurrealDbClient::new(
|
||||
&config.surrealdb_address,
|
||||
&config.surrealdb_username,
|
||||
&config.surrealdb_password,
|
||||
&config.surrealdb_namespace,
|
||||
&config.surrealdb_database,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
surreal_db_client.ensure_initialized().await?;
|
||||
|
||||
let app_state = ApiState {
|
||||
surreal_db_client: surreal_db_client.clone(),
|
||||
job_queue: Arc::new(JobQueue::new(surreal_db_client)),
|
||||
};
|
||||
|
||||
Ok(app_state)
|
||||
}
|
||||
}
|
||||
25
crates/api-router/src/lib.rs
Normal file
25
crates/api-router/src/lib.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use api_state::ApiState;
|
||||
use axum::{
|
||||
extract::{DefaultBodyLimit, FromRef},
|
||||
middleware::from_fn_with_state,
|
||||
routing::post,
|
||||
Router,
|
||||
};
|
||||
use middleware_api_auth::api_auth;
|
||||
use routes::ingress::ingress_data;
|
||||
|
||||
pub mod api_state;
|
||||
mod middleware_api_auth;
|
||||
mod routes;
|
||||
|
||||
/// Router for API functionality, version 1
|
||||
pub fn api_routes_v1<S>(app_state: &ApiState) -> Router<S>
|
||||
where
|
||||
S: Clone + Send + Sync + 'static,
|
||||
ApiState: FromRef<S>,
|
||||
{
|
||||
Router::new()
|
||||
.route("/ingress", post(ingress_data))
|
||||
.layer(DefaultBodyLimit::max(1024 * 1024 * 1024))
|
||||
.route_layer(from_fn_with_state(app_state.clone(), api_auth))
|
||||
}
|
||||
@@ -4,12 +4,12 @@ use axum::{
|
||||
response::Response,
|
||||
};
|
||||
|
||||
use crate::{error::ApiError, storage::types::user::User};
|
||||
use common::{error::ApiError, storage::types::user::User};
|
||||
|
||||
use super::AppState;
|
||||
use crate::api_state::ApiState;
|
||||
|
||||
pub async fn api_auth(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<ApiState>,
|
||||
mut request: Request,
|
||||
next: Next,
|
||||
) -> Result<Response, ApiError> {
|
||||
@@ -1,15 +1,16 @@
|
||||
use crate::{
|
||||
error::{ApiError, AppError},
|
||||
ingress::types::ingress_input::{create_ingress_objects, IngressInput},
|
||||
server::AppState,
|
||||
storage::types::{file_info::FileInfo, user::User},
|
||||
};
|
||||
use axum::{extract::State, http::StatusCode, response::IntoResponse, Extension};
|
||||
use axum_typed_multipart::{FieldData, TryFromMultipart, TypedMultipart};
|
||||
use common::{
|
||||
error::{ApiError, AppError},
|
||||
ingress::ingress_input::{create_ingress_objects, IngressInput},
|
||||
storage::types::{file_info::FileInfo, user::User},
|
||||
};
|
||||
use futures::{future::try_join_all, TryFutureExt};
|
||||
use tempfile::NamedTempFile;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::api_state::ApiState;
|
||||
|
||||
#[derive(Debug, TryFromMultipart)]
|
||||
pub struct IngressParams {
|
||||
pub content: Option<String>,
|
||||
@@ -21,7 +22,7 @@ pub struct IngressParams {
|
||||
}
|
||||
|
||||
pub async fn ingress_data(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<ApiState>,
|
||||
Extension(user): Extension<User>,
|
||||
TypedMultipart(input): TypedMultipart<IngressParams>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
1
crates/api-router/src/routes/mod.rs
Normal file
1
crates/api-router/src/routes/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod ingress;
|
||||
47
crates/common/Cargo.toml
Normal file
47
crates/common/Cargo.toml
Normal file
@@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# Workspace dependencies
|
||||
tokio = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
axum = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
async-openai = "0.24.1"
|
||||
async-stream = "0.3.6"
|
||||
axum-htmx = "0.6.0"
|
||||
axum_session = "0.14.4"
|
||||
axum_session_auth = "0.14.1"
|
||||
axum_session_surreal = "0.2.1"
|
||||
axum_typed_multipart = "0.12.1"
|
||||
chrono = { version = "0.4.39", features = ["serde"] }
|
||||
chrono-tz = "0.10.1"
|
||||
config = "0.15.4"
|
||||
futures = "0.3.31"
|
||||
json-stream-parser = "0.1.4"
|
||||
lettre = { version = "0.11.11", features = ["rustls-tls"] }
|
||||
mime = "0.3.17"
|
||||
mime_guess = "2.0.5"
|
||||
minijinja = { version = "2.5.0", features = ["loader", "multi_template"] }
|
||||
minijinja-autoreload = "2.5.0"
|
||||
minijinja-contrib = { version = "2.6.0", features = ["datetime", "timezone"] }
|
||||
mockall = "0.13.0"
|
||||
plotly = "0.12.1"
|
||||
reqwest = {version = "0.12.12", features = ["charset", "json"]}
|
||||
scraper = "0.22.0"
|
||||
sha2 = "0.10.8"
|
||||
surrealdb = "2.0.4"
|
||||
tempfile = "3.12.0"
|
||||
text-splitter = "0.18.1"
|
||||
tiktoken-rs = "0.6.0"
|
||||
tower-http = { version = "0.6.2", features = ["fs"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
url = { version = "2.5.2", features = ["serde"] }
|
||||
uuid = { version = "1.10.0", features = ["v4", "serde"] }
|
||||
|
||||
@@ -15,21 +15,17 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::{content_processor::ContentProcessor, types::ingress_object::IngressObject};
|
||||
use super::{content_processor::ContentProcessor, ingress_object::IngressObject};
|
||||
|
||||
pub struct JobQueue {
|
||||
pub db: Arc<SurrealDbClient>,
|
||||
pub openai_client: Arc<async_openai::Client<async_openai::config::OpenAIConfig>>,
|
||||
}
|
||||
|
||||
pub const MAX_ATTEMPTS: u32 = 3;
|
||||
|
||||
impl JobQueue {
|
||||
pub fn new(
|
||||
db: Arc<SurrealDbClient>,
|
||||
openai_client: Arc<async_openai::Client<async_openai::config::OpenAIConfig>>,
|
||||
) -> Self {
|
||||
Self { db, openai_client }
|
||||
pub fn new(db: Arc<SurrealDbClient>) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
/// Creates a new job and stores it in the database
|
||||
@@ -147,6 +143,7 @@ impl JobQueue {
|
||||
&self,
|
||||
job: Job,
|
||||
processor: &ContentProcessor,
|
||||
openai_client: Arc<async_openai::Client<async_openai::config::OpenAIConfig>>,
|
||||
) -> Result<(), AppError> {
|
||||
let current_attempts = match job.status {
|
||||
JobStatus::InProgress { attempts, .. } => attempts + 1,
|
||||
@@ -163,7 +160,7 @@ impl JobQueue {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let text_content = job.content.to_text_content(&self.openai_client).await?;
|
||||
let text_content = job.content.to_text_content(&openai_client).await?;
|
||||
|
||||
match processor.process(&text_content).await {
|
||||
Ok(_) => {
|
||||
@@ -1,3 +1,6 @@
|
||||
pub mod analysis;
|
||||
pub mod content_processor;
|
||||
pub mod ingress_input;
|
||||
pub mod ingress_object;
|
||||
pub mod jobqueue;
|
||||
pub mod queue_task;
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ingress::types::ingress_object::IngressObject;
|
||||
use crate::ingress::ingress_object::IngressObject;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -1,6 +1,6 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{ingress::types::ingress_object::IngressObject, stored_object};
|
||||
use crate::{ingress::ingress_object::IngressObject, stored_object};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum JobStatus {
|
||||
35
crates/html-router/Cargo.toml
Normal file
35
crates/html-router/Cargo.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "html-router"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# Workspace dependencies
|
||||
tokio = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
axum = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
|
||||
axum-htmx = "0.6.0"
|
||||
axum_session = "0.14.4"
|
||||
axum_session_auth = "0.14.1"
|
||||
axum_session_surreal = "0.2.1"
|
||||
axum_typed_multipart = "0.12.1"
|
||||
futures = "0.3.31"
|
||||
tempfile = "3.12.0"
|
||||
async-stream = "0.3.6"
|
||||
json-stream-parser = "0.1.4"
|
||||
minijinja = { version = "2.5.0", features = ["loader", "multi_template"] }
|
||||
minijinja-autoreload = "2.5.0"
|
||||
minijinja-contrib = { version = "2.6.0", features = ["datetime", "timezone"] }
|
||||
plotly = "0.12.1"
|
||||
surrealdb = "2.0.4"
|
||||
tower-http = { version = "0.6.2", features = ["fs"] }
|
||||
chrono-tz = "0.10.1"
|
||||
async-openai = "0.24.1"
|
||||
|
||||
|
||||
|
||||
common = { path = "../common" }
|
||||
@@ -1,21 +1,17 @@
|
||||
use crate::ingress::jobqueue::JobQueue;
|
||||
use crate::storage::db::SurrealDbClient;
|
||||
use crate::utils::config::AppConfig;
|
||||
use crate::utils::mailer::Mailer;
|
||||
use axum_session::SessionStore;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use common::ingress::jobqueue::JobQueue;
|
||||
use common::storage::db::SurrealDbClient;
|
||||
use common::utils::config::AppConfig;
|
||||
use common::utils::mailer::Mailer;
|
||||
use minijinja::{path_loader, Environment};
|
||||
use minijinja_autoreload::AutoReloader;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use surrealdb::engine::any::Any;
|
||||
|
||||
pub mod middleware_analytics;
|
||||
pub mod middleware_api_auth;
|
||||
pub mod routes;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub struct HtmlState {
|
||||
pub surreal_db_client: Arc<SurrealDbClient>,
|
||||
pub openai_client: Arc<async_openai::Client<async_openai::config::OpenAIConfig>>,
|
||||
pub templates: Arc<AutoReloader>,
|
||||
@@ -24,10 +20,10 @@ pub struct AppState {
|
||||
pub session_store: Arc<SessionStore<SessionSurrealPool<Any>>>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
impl HtmlState {
|
||||
pub async fn new(config: &AppConfig) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let reloader = AutoReloader::new(move |notifier| {
|
||||
let template_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("templates");
|
||||
let template_path = get_templates_dir();
|
||||
let mut env = Environment::new();
|
||||
env.set_loader(path_loader(&template_path));
|
||||
|
||||
@@ -54,7 +50,7 @@ impl AppState {
|
||||
|
||||
let session_store = Arc::new(surreal_db_client.create_session_store().await?);
|
||||
|
||||
let app_state = AppState {
|
||||
let app_state = HtmlState {
|
||||
surreal_db_client: surreal_db_client.clone(),
|
||||
templates: Arc::new(reloader),
|
||||
openai_client: openai_client.clone(),
|
||||
@@ -63,10 +59,30 @@ impl AppState {
|
||||
&config.smtp_relayer,
|
||||
&config.smtp_password,
|
||||
)?),
|
||||
job_queue: Arc::new(JobQueue::new(surreal_db_client, openai_client)),
|
||||
job_queue: Arc::new(JobQueue::new(surreal_db_client)),
|
||||
session_store,
|
||||
};
|
||||
|
||||
Ok(app_state)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_workspace_root() -> PathBuf {
|
||||
// Starts from CARGO_MANIFEST_DIR (e.g., /project/crates/html-router/)
|
||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
// Navigate up to /path/to/project/crates
|
||||
let crates_dir = manifest_dir
|
||||
.parent()
|
||||
.expect("Failed to find parent of manifest directory");
|
||||
|
||||
// Navigate up to workspace root
|
||||
crates_dir
|
||||
.parent()
|
||||
.expect("Failed to find workspace root")
|
||||
.to_path_buf()
|
||||
}
|
||||
|
||||
pub fn get_templates_dir() -> PathBuf {
|
||||
get_workspace_root().join("templates")
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
use api::{
|
||||
ingress::ingress_data,
|
||||
ingress_task::{delete_queue_task, get_queue_tasks},
|
||||
query::query_handler,
|
||||
queue_length::queue_length_handler,
|
||||
};
|
||||
pub mod html_state;
|
||||
mod middleware_analytics;
|
||||
mod routes;
|
||||
|
||||
use axum::{
|
||||
extract::DefaultBodyLimit,
|
||||
extract::FromRef,
|
||||
middleware::from_fn_with_state,
|
||||
routing::{delete, get, patch, post},
|
||||
Router,
|
||||
};
|
||||
use axum_session::{SessionLayer, SessionStore};
|
||||
use axum_session::SessionLayer;
|
||||
use axum_session_auth::{AuthConfig, AuthSessionLayer};
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use html::{
|
||||
use common::storage::types::user::User;
|
||||
use html_state::HtmlState;
|
||||
use middleware_analytics::analytics_middleware;
|
||||
use routes::{
|
||||
account::{delete_account, set_api_key, show_account_page, update_timezone},
|
||||
admin_panel::{show_admin_panel, toggle_registration_status},
|
||||
chat::{
|
||||
@@ -40,29 +41,12 @@ use html::{
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
use crate::storage::types::user::User;
|
||||
|
||||
use super::{middleware_analytics::analytics_middleware, middleware_api_auth::api_auth, AppState};
|
||||
|
||||
pub mod api;
|
||||
pub mod html;
|
||||
|
||||
/// Router for API functionality, version 1
|
||||
pub fn api_routes_v1(app_state: &AppState) -> Router<AppState> {
|
||||
Router::new()
|
||||
// Ingress routes
|
||||
.route("/ingress", post(ingress_data))
|
||||
.route("/message_count", get(queue_length_handler))
|
||||
.route("/queue", get(get_queue_tasks))
|
||||
.route("/queue/:delivery_tag", delete(delete_queue_task))
|
||||
.layer(DefaultBodyLimit::max(1024 * 1024 * 1024))
|
||||
// Query routes
|
||||
.route("/query", post(query_handler))
|
||||
.route_layer(from_fn_with_state(app_state.clone(), api_auth))
|
||||
}
|
||||
|
||||
/// Router for HTML endpoints
|
||||
pub fn html_routes(app_state: &AppState) -> Router<AppState> {
|
||||
pub fn html_routes<S>(app_state: &HtmlState) -> Router<S>
|
||||
where
|
||||
S: Clone + Send + Sync + 'static,
|
||||
HtmlState: FromRef<S>,
|
||||
{
|
||||
Router::new()
|
||||
.route("/", get(index_handler))
|
||||
.route("/gdpr/accept", post(accept_gdpr))
|
||||
@@ -6,12 +6,12 @@ use axum::{
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::engine::any::Any;
|
||||
|
||||
use crate::storage::types::analytics::Analytics;
|
||||
use common::storage::types::analytics::Analytics;
|
||||
|
||||
use super::AppState;
|
||||
use crate::html_state::HtmlState;
|
||||
|
||||
pub async fn analytics_middleware(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
session: axum_session::Session<SessionSurrealPool<Any>>,
|
||||
request: Request,
|
||||
next: Next,
|
||||
@@ -10,14 +10,14 @@ use axum_session_surreal::SessionSurrealPool;
|
||||
use chrono_tz::TZ_VARIANTS;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::{AppError, HtmlError},
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::{db::delete_item, types::user::User},
|
||||
};
|
||||
|
||||
use super::render_block;
|
||||
use crate::{html_state::HtmlState, page_data};
|
||||
|
||||
use super::{render_block, render_template};
|
||||
|
||||
page_data!(AccountData, "auth/account_settings.html", {
|
||||
user: User,
|
||||
@@ -25,7 +25,7 @@ page_data!(AccountData, "auth/account_settings.html", {
|
||||
});
|
||||
|
||||
pub async fn show_account_page(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated
|
||||
@@ -46,7 +46,7 @@ pub async fn show_account_page(
|
||||
}
|
||||
|
||||
pub async fn set_api_key(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated
|
||||
@@ -83,7 +83,7 @@ pub async fn set_api_key(
|
||||
}
|
||||
|
||||
pub async fn delete_account(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated
|
||||
@@ -109,7 +109,7 @@ pub struct UpdateTimezoneForm {
|
||||
}
|
||||
|
||||
pub async fn update_timezone(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<UpdateTimezoneForm>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -7,14 +7,14 @@ use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::HtmlError,
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::types::{analytics::Analytics, system_settings::SystemSettings, user::User},
|
||||
};
|
||||
|
||||
use super::render_block;
|
||||
use crate::{html_state::HtmlState, page_data};
|
||||
|
||||
use super::{render_block, render_template};
|
||||
|
||||
page_data!(AdminPanelData, "auth/admin_panel.html", {
|
||||
user: User,
|
||||
@@ -24,7 +24,7 @@ page_data!(AdminPanelData, "auth/admin_panel.html", {
|
||||
});
|
||||
|
||||
pub async fn show_admin_panel(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated and admin
|
||||
@@ -82,7 +82,7 @@ pub struct RegistrationToggleData {
|
||||
}
|
||||
|
||||
pub async fn toggle_registration_status(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(input): Form<RegistrationToggleInput>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -21,14 +21,13 @@ use surrealdb::{engine::any::Any, Surreal};
|
||||
use tokio::sync::{mpsc::channel, Mutex};
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
retrieval::{
|
||||
combined_knowledge_entity_retrieval,
|
||||
query_helper::{
|
||||
create_chat_request, create_user_message, format_entities_json, LLMResponseFormat,
|
||||
},
|
||||
},
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::{
|
||||
db::{get_item, store_item, SurrealDbClient},
|
||||
types::{
|
||||
@@ -38,6 +37,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{html_state::HtmlState, routes::render_template};
|
||||
|
||||
// Error handling function
|
||||
fn create_error_stream(
|
||||
message: impl Into<String>,
|
||||
@@ -87,7 +88,7 @@ pub struct QueryParams {
|
||||
}
|
||||
|
||||
pub async fn get_response_stream(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Query(params): Query<QueryParams>,
|
||||
) -> Sse<Pin<Box<dyn Stream<Item = Result<Event, axum::Error>> + Send>>> {
|
||||
@@ -12,10 +12,8 @@ use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::{AppError, HtmlError},
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::{
|
||||
db::{get_item, store_item},
|
||||
types::{
|
||||
@@ -26,6 +24,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{html_state::HtmlState, page_data, routes::render_template};
|
||||
|
||||
// Update your ChatStartParams struct to properly deserialize the references
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ChatStartParams {
|
||||
@@ -52,7 +52,7 @@ page_data!(ChatData, "chat/base.html", {
|
||||
});
|
||||
|
||||
pub async fn show_initialized_chat(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<ChatStartParams>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -116,7 +116,7 @@ pub async fn show_initialized_chat(
|
||||
}
|
||||
|
||||
pub async fn show_chat_base(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
info!("Displaying empty chat start");
|
||||
@@ -151,7 +151,7 @@ pub struct NewMessageForm {
|
||||
|
||||
pub async fn show_existing_chat(
|
||||
Path(conversation_id): Path<String>,
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
info!("Displaying initialized chat with id: {}", conversation_id);
|
||||
@@ -189,7 +189,7 @@ pub async fn show_existing_chat(
|
||||
|
||||
pub async fn new_user_message(
|
||||
Path(conversation_id): Path<String>,
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<NewMessageForm>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -241,7 +241,7 @@ pub async fn new_user_message(
|
||||
}
|
||||
|
||||
pub async fn new_chat_user_message(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<NewMessageForm>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -8,17 +8,18 @@ use serde::Serialize;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::{AppError, HtmlError},
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::{
|
||||
db::get_item,
|
||||
types::{knowledge_entity::KnowledgeEntity, user::User},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{html_state::HtmlState, routes::render_template};
|
||||
|
||||
pub async fn show_reference_tooltip(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(reference_id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -6,13 +6,13 @@ use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::HtmlError,
|
||||
page_data,
|
||||
server::AppState,
|
||||
storage::types::{text_content::TextContent, user::User},
|
||||
};
|
||||
|
||||
use crate::{html_state::HtmlState, page_data};
|
||||
|
||||
use super::render_template;
|
||||
|
||||
page_data!(ContentPageData, "content/base.html", {
|
||||
@@ -21,7 +21,7 @@ page_data!(ContentPageData, "content/base.html", {
|
||||
});
|
||||
|
||||
pub async fn show_content_page(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated
|
||||
@@ -53,7 +53,7 @@ pub struct TextContentEditModal {
|
||||
}
|
||||
|
||||
pub async fn show_text_content_edit_form(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -77,7 +77,7 @@ pub async fn show_text_content_edit_form(
|
||||
}
|
||||
|
||||
pub async fn patch_text_content(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -3,12 +3,11 @@ use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{
|
||||
error::HtmlError,
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::types::user::User,
|
||||
};
|
||||
use common::{error::HtmlError, storage::types::user::User};
|
||||
|
||||
use crate::{html_state::HtmlState, page_data};
|
||||
|
||||
use super::render_template;
|
||||
|
||||
page_data!(DocumentationData, "do_not_use_this", {
|
||||
user: Option<User>,
|
||||
@@ -16,7 +15,7 @@ page_data!(DocumentationData, "do_not_use_this", {
|
||||
});
|
||||
|
||||
pub async fn show_privacy_policy(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
@@ -32,7 +31,7 @@ pub async fn show_privacy_policy(
|
||||
}
|
||||
|
||||
pub async fn show_get_started(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
@@ -47,7 +46,7 @@ pub async fn show_get_started(
|
||||
Ok(output.into_response())
|
||||
}
|
||||
pub async fn show_mobile_friendly(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
@@ -63,7 +62,7 @@ pub async fn show_mobile_friendly(
|
||||
}
|
||||
|
||||
pub async fn show_documentation_index(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let output = render_template(
|
||||
@@ -3,7 +3,7 @@ use axum_session::Session;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::engine::any::Any;
|
||||
|
||||
use crate::error::HtmlError;
|
||||
use common::error::HtmlError;
|
||||
|
||||
pub async fn accept_gdpr(
|
||||
session: Session<SessionSurrealPool<Any>>,
|
||||
@@ -9,13 +9,8 @@ use surrealdb::{engine::any::Any, Surreal};
|
||||
use tokio::join;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::{AppError, HtmlError},
|
||||
page_data,
|
||||
server::{
|
||||
routes::html::{render_block, render_template},
|
||||
AppState,
|
||||
},
|
||||
storage::{
|
||||
db::{delete_item, get_item},
|
||||
types::{
|
||||
@@ -26,6 +21,10 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{html_state::HtmlState, page_data, routes::render_template};
|
||||
|
||||
use super::render_block;
|
||||
|
||||
page_data!(IndexData, "index/index.html", {
|
||||
gdpr_accepted: bool,
|
||||
user: Option<User>,
|
||||
@@ -34,7 +33,7 @@ page_data!(IndexData, "index/index.html", {
|
||||
});
|
||||
|
||||
pub async fn index_handler(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
session: Session<SessionSurrealPool<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -92,7 +91,7 @@ pub struct LatestTextContentData {
|
||||
}
|
||||
|
||||
pub async fn delete_text_content(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -153,7 +152,7 @@ pub async fn delete_text_content(
|
||||
|
||||
// Helper function to get and validate text content
|
||||
async fn get_and_validate_text_content(
|
||||
state: &AppState,
|
||||
state: &HtmlState,
|
||||
id: &str,
|
||||
user: &User,
|
||||
) -> Result<TextContent, HtmlError> {
|
||||
@@ -184,7 +183,7 @@ pub struct ActiveJobsData {
|
||||
}
|
||||
|
||||
pub async fn delete_job(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -219,7 +218,7 @@ pub async fn delete_job(
|
||||
}
|
||||
|
||||
pub async fn show_active_jobs(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let user = match auth.current_user {
|
||||
@@ -10,17 +10,18 @@ use surrealdb::{engine::any::Any, Surreal};
|
||||
use tempfile::NamedTempFile;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::{AppError, HtmlError, IntoHtmlError},
|
||||
ingress::types::ingress_input::{create_ingress_objects, IngressInput},
|
||||
page_data,
|
||||
server::{
|
||||
routes::html::{index::ActiveJobsData, render_block},
|
||||
AppState,
|
||||
},
|
||||
ingress::ingress_input::{create_ingress_objects, IngressInput},
|
||||
storage::types::{file_info::FileInfo, user::User},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
html_state::HtmlState,
|
||||
page_data,
|
||||
routes::{index::ActiveJobsData, render_block},
|
||||
};
|
||||
|
||||
use super::render_template;
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -29,7 +30,7 @@ pub struct ShowIngressFormData {
|
||||
}
|
||||
|
||||
pub async fn show_ingress_form(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
if !auth.is_authenticated() {
|
||||
@@ -80,7 +81,7 @@ page_data!(IngressFormData, "ingress_form.html", {
|
||||
});
|
||||
|
||||
pub async fn process_ingress_form(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
TypedMultipart(input): TypedMultipart<IngressParams>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -13,10 +13,8 @@ use plotly::{
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
use common::{
|
||||
error::{AppError, HtmlError},
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::{
|
||||
db::delete_item,
|
||||
types::{
|
||||
@@ -27,6 +25,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{html_state::HtmlState, page_data, routes::render_template};
|
||||
|
||||
page_data!(KnowledgeBaseData, "knowledge/base.html", {
|
||||
entities: Vec<KnowledgeEntity>,
|
||||
relationships: Vec<KnowledgeRelationship>,
|
||||
@@ -35,7 +35,7 @@ page_data!(KnowledgeBaseData, "knowledge/base.html", {
|
||||
});
|
||||
|
||||
pub async fn show_knowledge_page(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
// Early return if the user is not authenticated
|
||||
@@ -152,7 +152,7 @@ pub struct EntityData {
|
||||
}
|
||||
|
||||
pub async fn show_edit_knowledge_entity_form(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -201,7 +201,7 @@ pub struct PatchKnowledgeEntityParams {
|
||||
}
|
||||
|
||||
pub async fn patch_knowledge_entity(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<PatchKnowledgeEntityParams>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -246,7 +246,7 @@ pub async fn patch_knowledge_entity(
|
||||
}
|
||||
|
||||
pub async fn delete_knowledge_entity(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -288,7 +288,7 @@ pub struct RelationshipTableData {
|
||||
}
|
||||
|
||||
pub async fn delete_knowledge_relationship(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -333,7 +333,7 @@ pub struct SaveKnowledgeRelationshipInput {
|
||||
}
|
||||
|
||||
pub async fn save_knowledge_relationship(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<SaveKnowledgeRelationshipInput>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use axum::response::Html;
|
||||
use minijinja_autoreload::AutoReloader;
|
||||
|
||||
use crate::error::{HtmlError, IntoHtmlError};
|
||||
use common::error::{HtmlError, IntoHtmlError};
|
||||
|
||||
pub mod account;
|
||||
pub mod admin_panel;
|
||||
@@ -75,7 +75,7 @@ where
|
||||
macro_rules! page_data {
|
||||
($name:ident, $template_name:expr, {$($(#[$attr:meta])* $field:ident: $ty:ty),*$(,)?}) => {
|
||||
use serde::{Serialize, Deserialize};
|
||||
use $crate::server::routes::html::PageData;
|
||||
use $crate::routes::PageData;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct $name {
|
||||
@@ -8,12 +8,9 @@ use serde::{Deserialize, Serialize};
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::HtmlError,
|
||||
retrieval::query_helper::get_answer_with_references,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::types::user::User,
|
||||
};
|
||||
use common::{error::HtmlError, storage::types::user::User};
|
||||
|
||||
use crate::{html_state::HtmlState, routes::render_template};
|
||||
#[derive(Deserialize)]
|
||||
pub struct SearchParams {
|
||||
query: String,
|
||||
@@ -27,7 +24,7 @@ pub struct AnswerData {
|
||||
}
|
||||
|
||||
pub async fn search_result_handler(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
Query(query): Query<SearchParams>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -9,7 +9,9 @@ use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{error::HtmlError, page_data, server::AppState, storage::types::user::User};
|
||||
use common::{error::HtmlError, storage::types::user::User};
|
||||
|
||||
use crate::{html_state::HtmlState, page_data};
|
||||
|
||||
use super::{render_block, render_template};
|
||||
|
||||
@@ -23,7 +25,7 @@ pub struct SignupParams {
|
||||
page_data!(ShowSignInForm, "auth/signin_form.html", {});
|
||||
|
||||
pub async fn show_signin_form(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
HxBoosted(boosted): HxBoosted,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -48,7 +50,7 @@ pub async fn show_signin_form(
|
||||
}
|
||||
|
||||
pub async fn authenticate_user(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<SignupParams>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -3,7 +3,7 @@ use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{error::ApiError, storage::types::user::User};
|
||||
use common::{error::ApiError, storage::types::user::User};
|
||||
|
||||
pub async fn sign_out_user(
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
@@ -10,7 +10,9 @@ use axum_session_surreal::SessionSurrealPool;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
|
||||
use crate::{error::HtmlError, server::AppState, storage::types::user::User};
|
||||
use common::{error::HtmlError, storage::types::user::User};
|
||||
|
||||
use crate::html_state::HtmlState;
|
||||
|
||||
use super::{render_block, render_template};
|
||||
|
||||
@@ -21,13 +23,8 @@ pub struct SignupParams {
|
||||
pub timezone: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PageData {
|
||||
// name: String,
|
||||
}
|
||||
|
||||
pub async fn show_signup_form(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
HxBoosted(boosted): HxBoosted,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
@@ -35,24 +32,15 @@ pub async fn show_signup_form(
|
||||
return Ok(Redirect::to("/").into_response());
|
||||
}
|
||||
let output = match boosted {
|
||||
true => render_block(
|
||||
"auth/signup_form.html",
|
||||
"body",
|
||||
PageData {},
|
||||
state.templates.clone(),
|
||||
)?,
|
||||
false => render_template(
|
||||
"auth/signup_form.html",
|
||||
PageData {},
|
||||
state.templates.clone(),
|
||||
)?,
|
||||
true => render_block("auth/signup_form.html", "body", {}, state.templates.clone())?,
|
||||
false => render_template("auth/signup_form.html", {}, state.templates.clone())?,
|
||||
};
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
|
||||
pub async fn process_signup_and_show_verification(
|
||||
State(state): State<AppState>,
|
||||
State(state): State<HtmlState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<SignupParams>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
58
crates/main/Cargo.toml
Normal file
58
crates/main/Cargo.toml
Normal file
@@ -0,0 +1,58 @@
|
||||
[package]
|
||||
name = "main"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
axum = { workspace = true }
|
||||
|
||||
async-openai = "0.24.1"
|
||||
async-stream = "0.3.6"
|
||||
axum-htmx = "0.6.0"
|
||||
axum_session = "0.14.4"
|
||||
axum_session_auth = "0.14.1"
|
||||
axum_session_surreal = "0.2.1"
|
||||
axum_typed_multipart = "0.12.1"
|
||||
chrono = { version = "0.4.39", features = ["serde"] }
|
||||
chrono-tz = "0.10.1"
|
||||
config = "0.15.4"
|
||||
futures = "0.3.31"
|
||||
json-stream-parser = "0.1.4"
|
||||
lettre = { version = "0.11.11", features = ["rustls-tls"] }
|
||||
mime = "0.3.17"
|
||||
mime_guess = "2.0.5"
|
||||
minijinja = { version = "2.5.0", features = ["loader", "multi_template"] }
|
||||
minijinja-autoreload = "2.5.0"
|
||||
minijinja-contrib = { version = "2.6.0", features = ["datetime", "timezone"] }
|
||||
mockall = "0.13.0"
|
||||
plotly = "0.12.1"
|
||||
reqwest = {version = "0.12.12", features = ["charset", "json"]}
|
||||
scraper = "0.22.0"
|
||||
sha2 = "0.10.8"
|
||||
surrealdb = "2.0.4"
|
||||
tempfile = "3.12.0"
|
||||
text-splitter = "0.18.1"
|
||||
tiktoken-rs = "0.6.0"
|
||||
tower-http = { version = "0.6.2", features = ["fs"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
url = { version = "2.5.2", features = ["serde"] }
|
||||
uuid = { version = "1.10.0", features = ["v4", "serde"] }
|
||||
|
||||
# Reference to api-router
|
||||
api-router = { path = "../api-router" }
|
||||
html-router = { path = "../html-router" }
|
||||
common = { path = "../common" }
|
||||
|
||||
[[bin]]
|
||||
name = "server"
|
||||
path = "src/server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "worker"
|
||||
path = "src/worker.rs"
|
||||
47
crates/main/src/server.rs
Normal file
47
crates/main/src/server.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use api_router::{api_routes_v1, api_state::ApiState};
|
||||
use axum::{extract::FromRef, Router};
|
||||
use common::utils::config::get_config;
|
||||
use html_router::{html_routes, html_state::HtmlState};
|
||||
use tracing::info;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
|
||||
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Set up tracing
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(EnvFilter::from_default_env())
|
||||
.try_init()
|
||||
.ok();
|
||||
|
||||
// Get config
|
||||
let config = get_config()?;
|
||||
|
||||
// Set up router states
|
||||
let html_state = HtmlState::new(&config).await?;
|
||||
let api_state = ApiState {
|
||||
surreal_db_client: html_state.surreal_db_client.clone(),
|
||||
job_queue: html_state.job_queue.clone(),
|
||||
};
|
||||
|
||||
// Create Axum router
|
||||
let app = Router::new()
|
||||
.nest("/api/v1", api_routes_v1(&api_state))
|
||||
.nest("/", html_routes(&html_state))
|
||||
.with_state(AppState {
|
||||
api_state,
|
||||
html_state,
|
||||
});
|
||||
|
||||
info!("Listening on 0.0.0.0:3000");
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||
axum::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, FromRef)]
|
||||
struct AppState {
|
||||
api_state: ApiState,
|
||||
html_state: HtmlState,
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::StreamExt;
|
||||
use surrealdb::Action;
|
||||
use tracing::{error, info};
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
use zettle_db::{
|
||||
use common::{
|
||||
ingress::{
|
||||
content_processor::ContentProcessor,
|
||||
jobqueue::{JobQueue, MAX_ATTEMPTS},
|
||||
@@ -15,6 +11,10 @@ use zettle_db::{
|
||||
},
|
||||
utils::config::get_config,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use surrealdb::Action;
|
||||
use tracing::{error, info};
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -40,9 +40,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let openai_client = Arc::new(async_openai::Client::new());
|
||||
|
||||
let job_queue = JobQueue::new(surreal_db_client.clone(), openai_client.clone());
|
||||
let job_queue = JobQueue::new(surreal_db_client.clone());
|
||||
|
||||
let content_processor = ContentProcessor::new(surreal_db_client, openai_client).await?;
|
||||
let content_processor = ContentProcessor::new(surreal_db_client, openai_client.clone()).await?;
|
||||
|
||||
loop {
|
||||
// First, check for any unfinished jobs
|
||||
@@ -52,7 +52,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!("Found {} unfinished jobs", unfinished_jobs.len());
|
||||
|
||||
for job in unfinished_jobs {
|
||||
job_queue.process_job(job, &content_processor).await?;
|
||||
job_queue
|
||||
.process_job(job, &content_processor, openai_client.clone())
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +70,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
match notification.action {
|
||||
Action::Create => {
|
||||
if let Err(e) = job_queue
|
||||
.process_job(notification.data, &content_processor)
|
||||
.process_job(
|
||||
notification.data,
|
||||
&content_processor,
|
||||
openai_client.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Error processing job: {}", e);
|
||||
@@ -95,7 +101,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
JobStatus::Error(_) if attempts < MAX_ATTEMPTS => {
|
||||
// This is a retry after an error
|
||||
if let Err(e) = job_queue
|
||||
.process_job(current_job, &content_processor)
|
||||
.process_job(
|
||||
current_job,
|
||||
&content_processor,
|
||||
openai_client.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Error processing job retry: {}", e);
|
||||
@@ -114,7 +124,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
JobStatus::Created => {
|
||||
// Shouldn't happen with Update action, but process if it does
|
||||
if let Err(e) = job_queue
|
||||
.process_job(notification.data, &content_processor)
|
||||
.process_job(
|
||||
notification.data,
|
||||
&content_processor,
|
||||
openai_client.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Error processing job: {}", e);
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:cachix/devenv-nixpkgs/rolling";
|
||||
systems.url = "github:nix-systems/default";
|
||||
devenv.url = "github:cachix/devenv";
|
||||
devenv.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
nixConfig = {
|
||||
extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=";
|
||||
extra-substituters = "https://devenv.cachix.org";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
devenv,
|
||||
systems,
|
||||
...
|
||||
} @ inputs: let
|
||||
forEachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||
in {
|
||||
packages = forEachSystem (system: {
|
||||
devenv-up = self.devShells.${system}.default.config.procfileScript;
|
||||
});
|
||||
|
||||
devShells =
|
||||
forEachSystem
|
||||
(system: let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
default = devenv.lib.mkShell {
|
||||
inherit inputs pkgs;
|
||||
modules = [
|
||||
{
|
||||
# https://devenv.sh/reference/options/
|
||||
enterShell = ''
|
||||
echo "run devenv up -d to start and monitor services"
|
||||
'';
|
||||
|
||||
languages.rust.enable = true;
|
||||
|
||||
services = {
|
||||
redis = {
|
||||
enable = true;
|
||||
};
|
||||
rabbitmq = {
|
||||
enable = true;
|
||||
plugins = ["tracing"];
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
Your proposed structure for the API sounds solid and modular, making it easier to manage files and their relationships with other data. Here’s a breakdown of how this can work and how it could be used with an iOS shortcut or a custom application:
|
||||
|
||||
API Structure
|
||||
File Management Endpoints:
|
||||
|
||||
POST /file: Accepts a file upload and returns a unique identifier (ID) for that file.
|
||||
PUT /file/{id}: Updates the metadata of the file identified by {id}.
|
||||
DELETE /file/{id}: Deletes the file and its associated metadata from the database.
|
||||
Data Ingress Endpoint:
|
||||
|
||||
POST /ingress: Accepts a JSON body containing references (IDs) to files and other necessary data, linking them in the database as needed.
|
||||
Using with iOS Shortcuts
|
||||
You can create shortcuts to interact with your API endpoints without the need for a full-fledged application. Here's how:
|
||||
|
||||
File Upload Shortcut:
|
||||
|
||||
Use the "Get File" action to select a file from the device.
|
||||
Use the "Post" action to send a multipart/form-data request to the /file endpoint.
|
||||
Parse the response to get the returned file ID.
|
||||
Data Ingress Shortcut:
|
||||
|
||||
Use the "Ask for Input" action to gather the necessary fields for the ingress (like instructions, category, etc.) and the file ID(s).
|
||||
Use another "Post" action to send this data to the /ingress endpoint as JSON.
|
||||
Developing a CLI Tool
|
||||
A CLI tool could also be developed for easier interaction with your API. This tool could:
|
||||
|
||||
Upload Files: Handle file uploads and return file IDs.
|
||||
Link Data: Accept user input for instructions, category, and linked file IDs, then submit this data to the /ingress endpoint.
|
||||
Additional Considerations
|
||||
Error Handling: Ensure that both the upload and ingress endpoints handle errors gracefully and provide meaningful messages.
|
||||
Documentation: Create clear API documentation to guide users in constructing requests correctly, whether they are using a shortcut, CLI, or custom application.
|
||||
Authentication: Consider adding authentication to your API endpoints for security, especially if sensitive data is being handled.
|
||||
This approach gives you the flexibility to support various clients, streamline interactions, and keep the server-side implementation clean and manageable. Would you like more specifics on any part of this setup?
|
||||
@@ -1,5 +0,0 @@
|
||||
{% extends 'body_base.html' %}
|
||||
{% block main %}
|
||||
<main class="container justify-center flex-grow flex mx-auto mt-4 p-5">
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('tailwindcss'),
|
||||
require('autoprefixer'),
|
||||
require('cssnano')({
|
||||
preset: 'default', // Minify CSS
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
use axum::Router;
|
||||
use tracing::info;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
use zettle_db::{
|
||||
server::{
|
||||
routes::{api_routes_v1, html_routes},
|
||||
AppState,
|
||||
},
|
||||
utils::config::get_config,
|
||||
};
|
||||
|
||||
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Set up tracing
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(EnvFilter::from_default_env())
|
||||
.try_init()
|
||||
.ok();
|
||||
|
||||
let config = get_config()?;
|
||||
|
||||
let app_state = AppState::new(&config).await?;
|
||||
|
||||
// Create Axum router
|
||||
let app = Router::new()
|
||||
.nest("/api/v1", api_routes_v1(&app_state))
|
||||
.nest("/", html_routes(&app_state))
|
||||
.with_state(app_state);
|
||||
|
||||
info!("Listening on 0.0.0.0:3000");
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||
axum::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod analysis;
|
||||
pub mod content_processor;
|
||||
pub mod jobqueue;
|
||||
pub mod types;
|
||||
@@ -1,34 +0,0 @@
|
||||
use crate::{error::ApiError, server::AppState, storage::types::user::User};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
Extension, Json,
|
||||
};
|
||||
|
||||
pub async fn get_queue_tasks(
|
||||
State(state): State<AppState>,
|
||||
Extension(user): Extension<User>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let user_tasks = state
|
||||
.job_queue
|
||||
.get_user_jobs(&user.id)
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
Ok(Json(user_tasks))
|
||||
}
|
||||
|
||||
pub async fn delete_queue_task(
|
||||
State(state): State<AppState>,
|
||||
Extension(user): Extension<User>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
state
|
||||
.job_queue
|
||||
.delete_job(&id, &user.id)
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod ingress;
|
||||
pub mod ingress_task;
|
||||
pub mod query;
|
||||
pub mod queue_length;
|
||||
@@ -1,34 +0,0 @@
|
||||
use crate::{
|
||||
error::ApiError, retrieval::query_helper::get_answer_with_references, server::AppState,
|
||||
storage::types::user::User,
|
||||
};
|
||||
use axum::{extract::State, response::IntoResponse, Extension, Json};
|
||||
use serde::Deserialize;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct QueryInput {
|
||||
query: String,
|
||||
}
|
||||
|
||||
pub async fn query_handler(
|
||||
State(state): State<AppState>,
|
||||
Extension(user): Extension<User>,
|
||||
Json(query): Json<QueryInput>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
info!("Received input: {:?}", query);
|
||||
info!("{:?}", user);
|
||||
|
||||
let answer = get_answer_with_references(
|
||||
&state.surreal_db_client,
|
||||
&state.openai_client,
|
||||
&query.query,
|
||||
&user.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(
|
||||
Json(serde_json::json!({"answer": answer.content, "references": answer.references}))
|
||||
.into_response(),
|
||||
)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use axum::{extract::State, http::StatusCode, response::IntoResponse};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::{ApiError, AppError},
|
||||
server::AppState,
|
||||
storage::{db::get_all_stored_items, types::job::Job},
|
||||
};
|
||||
|
||||
pub async fn queue_length_handler(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
info!("Getting queue length");
|
||||
|
||||
let queue_length = get_all_stored_items::<Job>(&state.surreal_db_client)
|
||||
.await
|
||||
.map_err(AppError::from)?
|
||||
.len();
|
||||
|
||||
info!("Queue length: {}", queue_length);
|
||||
|
||||
state
|
||||
.mailer
|
||||
.send_email_verification("per@starks.cloud", "1001010", &state.templates)
|
||||
.map_err(AppError::from)?;
|
||||
|
||||
// Return the queue length with a 200 OK status
|
||||
Ok((StatusCode::OK, queue_length.to_string()))
|
||||
}
|
||||
Reference in New Issue
Block a user