mirror of
https://github.com/perstarkse/minne.git
synced 2026-01-19 08:16:56 +01:00
255 lines
8.0 KiB
Rust
255 lines
8.0 KiB
Rust
use std::sync::Arc;
|
|
|
|
use async_openai::error::OpenAIError;
|
|
use axum::{
|
|
http::StatusCode,
|
|
response::{Html, IntoResponse, Response},
|
|
Json,
|
|
};
|
|
use minijinja::context;
|
|
use minijinja_autoreload::AutoReloader;
|
|
use serde::Serialize;
|
|
use serde_json::json;
|
|
use thiserror::Error;
|
|
use tokio::task::JoinError;
|
|
|
|
use crate::{storage::types::file_info::FileError, utils::mailer::EmailError};
|
|
|
|
// Core internal errors
|
|
#[derive(Error, Debug)]
|
|
pub enum AppError {
|
|
#[error("Database error: {0}")]
|
|
Database(#[from] surrealdb::Error),
|
|
#[error("OpenAI error: {0}")]
|
|
OpenAI(#[from] OpenAIError),
|
|
#[error("File error: {0}")]
|
|
File(#[from] FileError),
|
|
#[error("Email error: {0}")]
|
|
Email(#[from] EmailError),
|
|
#[error("Not found: {0}")]
|
|
NotFound(String),
|
|
#[error("Validation error: {0}")]
|
|
Validation(String),
|
|
#[error("Authorization error: {0}")]
|
|
Auth(String),
|
|
#[error("LLM parsing error: {0}")]
|
|
LLMParsing(String),
|
|
#[error("Task join error: {0}")]
|
|
Join(#[from] JoinError),
|
|
#[error("Graph mapper error: {0}")]
|
|
GraphMapper(String),
|
|
#[error("IoError: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
#[error("Minijina error: {0}")]
|
|
MiniJinja(#[from] minijinja::Error),
|
|
#[error("Reqwest error: {0}")]
|
|
Reqwest(#[from] reqwest::Error),
|
|
#[error("Tiktoken error: {0}")]
|
|
Tiktoken(#[from] anyhow::Error),
|
|
#[error("Ingress Processing error: {0}")]
|
|
Processing(String),
|
|
}
|
|
|
|
// API-specific errors
|
|
#[derive(Debug, Serialize)]
|
|
pub enum ApiError {
|
|
InternalError(String),
|
|
ValidationError(String),
|
|
NotFound(String),
|
|
Unauthorized(String),
|
|
}
|
|
|
|
impl From<AppError> for ApiError {
|
|
fn from(err: AppError) -> Self {
|
|
match err {
|
|
AppError::Database(_) | AppError::OpenAI(_) | AppError::Email(_) => {
|
|
tracing::error!("Internal error: {:?}", err);
|
|
ApiError::InternalError("Internal server error".to_string())
|
|
}
|
|
AppError::NotFound(msg) => ApiError::NotFound(msg),
|
|
AppError::Validation(msg) => ApiError::ValidationError(msg),
|
|
AppError::Auth(msg) => ApiError::Unauthorized(msg),
|
|
_ => ApiError::InternalError("Internal server error".to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IntoResponse for ApiError {
|
|
fn into_response(self) -> Response {
|
|
let (status, body) = match self {
|
|
ApiError::InternalError(message) => (
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
json!({
|
|
"error": message,
|
|
"status": "error"
|
|
}),
|
|
),
|
|
ApiError::ValidationError(message) => (
|
|
StatusCode::BAD_REQUEST,
|
|
json!({
|
|
"error": message,
|
|
"status": "error"
|
|
}),
|
|
),
|
|
ApiError::NotFound(message) => (
|
|
StatusCode::NOT_FOUND,
|
|
json!({
|
|
"error": message,
|
|
"status": "error"
|
|
}),
|
|
),
|
|
ApiError::Unauthorized(message) => (
|
|
StatusCode::UNAUTHORIZED,
|
|
json!({
|
|
"error": message,
|
|
"status": "error"
|
|
}),
|
|
), // ... other matches
|
|
};
|
|
(status, Json(body)).into_response()
|
|
}
|
|
}
|
|
|
|
pub type TemplateResult<T> = Result<T, HtmlError>;
|
|
|
|
// Helper trait for converting to HtmlError with templates
|
|
pub trait IntoHtmlError {
|
|
fn with_template(self, templates: Arc<AutoReloader>) -> HtmlError;
|
|
}
|
|
// // Implement for AppError
|
|
impl IntoHtmlError for AppError {
|
|
fn with_template(self, templates: Arc<AutoReloader>) -> HtmlError {
|
|
HtmlError::new(self, templates)
|
|
}
|
|
}
|
|
// // Implement for minijinja::Error directly
|
|
impl IntoHtmlError for minijinja::Error {
|
|
fn with_template(self, templates: Arc<AutoReloader>) -> HtmlError {
|
|
HtmlError::from_template_error(self, templates)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct ErrorContext {
|
|
#[allow(dead_code)]
|
|
templates: Arc<AutoReloader>,
|
|
}
|
|
|
|
impl ErrorContext {
|
|
pub fn new(templates: Arc<AutoReloader>) -> Self {
|
|
Self { templates }
|
|
}
|
|
}
|
|
|
|
pub enum HtmlError {
|
|
ServerError(Arc<AutoReloader>),
|
|
NotFound(Arc<AutoReloader>),
|
|
Unauthorized(Arc<AutoReloader>),
|
|
BadRequest(String, Arc<AutoReloader>),
|
|
Template(String, Arc<AutoReloader>),
|
|
}
|
|
|
|
impl HtmlError {
|
|
pub fn new(error: AppError, templates: Arc<AutoReloader>) -> Self {
|
|
match error {
|
|
AppError::NotFound(_msg) => HtmlError::NotFound(templates),
|
|
AppError::Auth(_msg) => HtmlError::Unauthorized(templates),
|
|
AppError::Validation(msg) => HtmlError::BadRequest(msg, templates),
|
|
_ => {
|
|
tracing::error!("Internal error: {:?}", error);
|
|
HtmlError::ServerError(templates)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn from_template_error(error: minijinja::Error, templates: Arc<AutoReloader>) -> Self {
|
|
tracing::error!("Template error: {:?}", error);
|
|
HtmlError::Template(error.to_string(), templates)
|
|
}
|
|
}
|
|
|
|
impl IntoResponse for HtmlError {
|
|
fn into_response(self) -> Response {
|
|
let (status, context, templates) = match self {
|
|
HtmlError::ServerError(templates) | HtmlError::Template(_, templates) => (
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
context! {
|
|
status_code => 500,
|
|
title => "Internal Server Error",
|
|
error => "Internal Server Error",
|
|
description => "Something went wrong on our end."
|
|
},
|
|
templates,
|
|
),
|
|
HtmlError::NotFound(templates) => (
|
|
StatusCode::NOT_FOUND,
|
|
context! {
|
|
status_code => 404,
|
|
title => "Page Not Found",
|
|
error => "Not Found",
|
|
description => "The page you're looking for doesn't exist or was removed."
|
|
},
|
|
templates,
|
|
),
|
|
HtmlError::Unauthorized(templates) => (
|
|
StatusCode::UNAUTHORIZED,
|
|
context! {
|
|
status_code => 401,
|
|
title => "Unauthorized",
|
|
error => "Access Denied",
|
|
description => "You need to be logged in to access this page."
|
|
},
|
|
templates,
|
|
),
|
|
HtmlError::BadRequest(msg, templates) => (
|
|
StatusCode::BAD_REQUEST,
|
|
context! {
|
|
status_code => 400,
|
|
title => "Bad Request",
|
|
error => "Bad Request",
|
|
description => msg
|
|
},
|
|
templates,
|
|
),
|
|
};
|
|
|
|
let html = match templates.acquire_env() {
|
|
Ok(env) => match env.get_template("errors/error.html") {
|
|
Ok(tmpl) => match tmpl.render(context) {
|
|
Ok(output) => output,
|
|
Err(e) => {
|
|
tracing::error!("Template render error: {:?}", e);
|
|
Self::fallback_html()
|
|
}
|
|
},
|
|
Err(e) => {
|
|
tracing::error!("Template get error: {:?}", e);
|
|
Self::fallback_html()
|
|
}
|
|
},
|
|
Err(e) => {
|
|
tracing::error!("Environment acquire error: {:?}", e);
|
|
Self::fallback_html()
|
|
}
|
|
};
|
|
|
|
(status, Html(html)).into_response()
|
|
}
|
|
}
|
|
|
|
impl HtmlError {
|
|
fn fallback_html() -> String {
|
|
r#"
|
|
<html>
|
|
<body>
|
|
<div class="container mx-auto p-4">
|
|
<h1 class="text-4xl text-error">Error</h1>
|
|
<p class="mt-4">Sorry, something went wrong displaying this page.</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"#
|
|
.to_string()
|
|
}
|
|
}
|