Skip to main content
Glama

microsandbox

by microsandbox
error.rs14 kB
//! Error handling module for the microsandbox server. //! //! This module provides comprehensive error handling functionality including: //! - Custom error types for server operations //! - Error codes and responses for API communication //! - Authentication and authorization error handling //! - Validation error handling //! //! The module implements: //! - Error types with detailed error messages //! - HTTP status code mapping //! - Serializable error responses for API clients //! - Structured error codes for frontend handling use axum::{ http::StatusCode, response::{IntoResponse, Response}, Json, }; use microsandbox_utils::MicrosandboxUtilsError; use serde::{Deserialize, Serialize}; use thiserror::Error; use tracing::error; //-------------------------------------------------------------------------------------------------- // Types //-------------------------------------------------------------------------------------------------- /// The result of microsandbox-server operations in general. pub type MicrosandboxServerResult<T> = Result<T, MicrosandboxServerError>; /// The result of server-related operations. pub type ServerResult<T> = Result<T, ServerError>; /// Error returned when an unexpected internal error occurs #[derive(Error, Debug)] pub enum MicrosandboxServerError { /// Error returned when the server fails to start #[error("Server failed to start: {0}")] StartError(String), /// Error returned when the server fails to stop #[error("Server failed to stop: {0}")] StopError(String), /// Error returned when the server key fails to generate #[error("Server key failed to generate: {0}")] KeyGenError(String), /// Error returned when the server configuration fails #[error("Server configuration failed: {0}")] ConfigError(String), /// Error returned when an I/O error occurs #[error(transparent)] IoError(#[from] std::io::Error), /// Error returned from the microsandbox-utils crate #[error(transparent)] Utils(#[from] MicrosandboxUtilsError), } /// Represents all possible errors that can occur in the application #[derive(Error, Debug)] pub enum ServerError { /// Error returned when authentication fails #[error("Authentication failed: {0}")] Authentication(AuthenticationError), /// Error returned when a user doesn't have permission to access a resource #[error("Authorization failed: {0}")] AuthorizationError(AuthorizationError), /// Error returned when a requested resource is not found #[error("Resource not found: {0}")] NotFound(String), /// Error returned when a database operation fails #[error("Database error: {0}")] DatabaseError(String), /// Error returned when request validation fails (e.g., invalid input format) #[error("Validation error: {0}")] ValidationError(ValidationError), /// Error returned when an unexpected internal error occurs #[error("Internal server error: {0}")] InternalError(String), } /// Error code structure to be sent to frontend #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub enum ErrorCode { // Authentication error codes /// Error returned when credentials provided don't match our records InvalidCredentials = 1001, /// Error returned when a user attempts to login with an unconfirmed email EmailNotConfirmed = 1002, /// Error returned when there have been too many failed login attempts TooManyLoginAttempts = 1003, /// Error returned when an authentication token is invalid InvalidToken = 1004, /// Error returned when an authentication token has expired ExpiredToken = 1005, /// Error returned when a token is required but not provided TokenRequired = 1006, /// Error returned when attempting to register with an email that already exists EmailAlreadyExists = 1007, /// Error returned when a user tries to sign in with password but should use Google UseGoogleLogin = 1008, /// Error returned when a user tries to sign in with password but should use GitHub UseGithubLogin = 1009, /// Error returned when a user tries to use OAuth but should use email/password UseEmailLogin = 1010, /// Error returned when a user's email is not verified with their OAuth provider EmailNotVerified = 1011, // Validation error codes /// Error returned when input fails validation rules InvalidInput = 2001, /// Error returned when password doesn't meet strength requirements PasswordTooWeak = 2002, /// Error returned when email format is invalid EmailInvalid = 2003, /// Error returned when a confirmation token is invalid or has expired InvalidOrExpiredConfirmationToken = 2004, // Authorization error codes /// Error returned when a user is denied access to a resource AccessDenied = 3001, /// Error returned when a user doesn't have sufficient permissions for an action InsufficientPermissions = 3002, // Resource error codes /// Error returned when a requested resource cannot be found ResourceNotFound = 4001, // Server error codes /// Error returned when a database operation fails DatabaseError = 5001, /// Error returned when an unexpected server error occurs InternalServerError = 5002, } /// Represents different types of authentication failures #[derive(Error, Debug)] pub enum AuthenticationError { /// Security-sensitive authentication failures that shouldn't reveal details #[error("Invalid credentials")] InvalidCredentials(String), /// User-facing authentication errors that can be shown directly #[error("{0}")] ClientError(String), /// Email not confirmed #[error("Email not confirmed")] EmailNotConfirmed, /// Too many login attempts #[error("Too many login attempts")] TooManyAttempts, /// Invalid or expired token #[error("Invalid or expired token")] InvalidToken(String), /// Email already registered #[error("Email already registered")] EmailAlreadyExists, /// Should use Google login instead #[error("Use Google login")] UseGoogleLogin, /// Should use GitHub login instead #[error("Use GitHub login")] UseGithubLogin, /// Should use email/password login instead #[error("Use email/password login")] UseEmailLogin, /// Email not verified with provider #[error("Email not verified")] EmailNotVerified, } /// Represents validation errors #[derive(Error, Debug)] pub enum ValidationError { /// Generic validation error #[error("{0}")] InvalidInput(String), /// Password not strong enough #[error("Password is too weak")] PasswordTooWeak(String), /// Email format invalid #[error("Email is invalid")] EmailInvalid(String), /// Invalid or expired confirmation token #[error("Invalid or expired confirmation token")] InvalidConfirmationToken, } /// Represents authorization errors #[derive(Error, Debug)] pub enum AuthorizationError { /// Access denied #[error("Access denied")] AccessDenied(String), /// Insufficient permissions #[error("Insufficient permissions")] InsufficientPermissions(String), } /// Response structure for errors #[derive(Serialize)] struct ErrorResponse { error: String, code: Option<u32>, } //-------------------------------------------------------------------------------------------------- // Trait Implementations //-------------------------------------------------------------------------------------------------- impl IntoResponse for ServerError { /// Converts the ServerError into an HTTP response with appropriate status code /// and JSON error message. /// /// ## Returns /// /// Returns an HTTP response containing: /// - Appropriate HTTP status code based on the error type /// - JSON body with an "error" field containing the error message /// - And an optional "code" field with a numeric error code for the frontend fn into_response(self) -> Response { // Log the actual error with details error!(error = ?self, "API error occurred"); let (status, error_message, error_code) = match self { ServerError::Authentication(auth_error) => { match auth_error { AuthenticationError::InvalidCredentials(_details) => { // Generic message for security-sensitive auth failures error!(details = ?_details, "Authentication error"); (StatusCode::UNAUTHORIZED, "Invalid credentials".to_string(), Some(ErrorCode::InvalidCredentials as u32)) } AuthenticationError::ClientError(details) => { // Safe to show these messages to users error!(details = ?details, "User-facing authentication error"); (StatusCode::UNAUTHORIZED, details, None) } AuthenticationError::EmailNotConfirmed => { (StatusCode::UNAUTHORIZED, "Email not confirmed".to_string(), Some(ErrorCode::EmailNotConfirmed as u32)) } AuthenticationError::TooManyAttempts => { (StatusCode::TOO_MANY_REQUESTS, "Too many login attempts, please try again later".to_string(), Some(ErrorCode::TooManyLoginAttempts as u32)) } AuthenticationError::InvalidToken(details) => { error!(details = ?details, "Invalid token"); (StatusCode::UNAUTHORIZED, "Invalid or expired token".to_string(), Some(ErrorCode::InvalidToken as u32)) } AuthenticationError::EmailAlreadyExists => { (StatusCode::CONFLICT, "Email already registered".to_string(), Some(ErrorCode::EmailAlreadyExists as u32)) } AuthenticationError::UseGoogleLogin => { (StatusCode::UNAUTHORIZED, "This email is registered with Google. Please use 'Sign in with Google' instead.".to_string(), Some(ErrorCode::UseGoogleLogin as u32)) } AuthenticationError::UseGithubLogin => { (StatusCode::UNAUTHORIZED, "This email is registered with GitHub. Please use 'Sign in with GitHub' instead.".to_string(), Some(ErrorCode::UseGithubLogin as u32)) } AuthenticationError::UseEmailLogin => { (StatusCode::UNAUTHORIZED, "This email is already registered. Please login with your password.".to_string(), Some(ErrorCode::UseEmailLogin as u32)) } AuthenticationError::EmailNotVerified => { (StatusCode::UNAUTHORIZED, "Email not verified with the provider".to_string(), Some(ErrorCode::EmailNotVerified as u32)) } } } ServerError::AuthorizationError(auth_error) => match auth_error { AuthorizationError::AccessDenied(details) => { error!(details = ?details, "Access denied"); ( StatusCode::FORBIDDEN, "Access denied".to_string(), Some(ErrorCode::AccessDenied as u32), ) } AuthorizationError::InsufficientPermissions(details) => { error!(details = ?details, "Insufficient permissions"); ( StatusCode::FORBIDDEN, "Insufficient permissions".to_string(), Some(ErrorCode::InsufficientPermissions as u32), ) } }, ServerError::NotFound(details) => ( StatusCode::NOT_FOUND, details, Some(ErrorCode::ResourceNotFound as u32), ), ServerError::DatabaseError(details) => { error!(details = ?details, "Database error"); ( StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string(), Some(ErrorCode::DatabaseError as u32), ) } ServerError::ValidationError(validation_error) => match validation_error { ValidationError::InvalidInput(details) => ( StatusCode::BAD_REQUEST, details, Some(ErrorCode::InvalidInput as u32), ), ValidationError::PasswordTooWeak(details) => ( StatusCode::BAD_REQUEST, details, Some(ErrorCode::PasswordTooWeak as u32), ), ValidationError::EmailInvalid(details) => ( StatusCode::BAD_REQUEST, details, Some(ErrorCode::EmailInvalid as u32), ), ValidationError::InvalidConfirmationToken => ( StatusCode::BAD_REQUEST, "Invalid or expired confirmation token".to_string(), Some(ErrorCode::InvalidOrExpiredConfirmationToken as u32), ), }, ServerError::InternalError(details) => { error!(details = ?details, "Internal error"); ( StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string(), Some(ErrorCode::InternalServerError as u32), ) } }; let body = Json(ErrorResponse { error: error_message, code: error_code, }); (status, body).into_response() } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/microsandbox/microsandbox'

If you have feedback or need assistance with the MCP directory API, please join our Discord server