-
Requested application data is not configured correctly. View/enable debug logs for more details. In response in postmanYour issue may already be reported! Please search on the Actix Web issue tracker before creating one. Expected BehaviorIt Should Crate The Requested Resorce in SurrealDB (kv rockdb storage) Current Behaviorreturning ContextI was building Auth Service for our National Level School (Education Management System) //! main.rs
use auth_service::{configuration::get_configuration, startup::Application};
use tracing::{error, info};
use tracing_subscriber::FmtSubscriber;
#[tokio::main]
async fn main() {
env_logger::init();
let subscriber = FmtSubscriber::builder()
.with_max_level(tracing::Level::INFO)
.finish();
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
let configuration = get_configuration().expect("Failed to read configuration.");
let application = Application::build(configuration.clone())
.await
.expect("Failed to build the app");
info!(
"🚀 Server started successfully at : http://{}:{} [{}]",
configuration.application.host, configuration.application.port, configuration.application.secret
);
if let Err(e) = application.run_until_stopped().await {
error!("Server failed: {}", e);
}
} //!startapp.rs
use crate::routes::auth_service_factory;
use crate::{
configuration::Settings,
structs::{descriptors::SurrealDescriptor, models::TokenCache},
surrealdb::connection::connect_surrealdb,
};
use actix_cors::Cors;
use actix_web::{
App, Error as ActixError, HttpServer,
body::MessageBody,
dev::{Server, ServiceFactory, ServiceRequest, ServiceResponse},
http::header::{self, HeaderName},
middleware::Logger,
web,
};
use shared_libs::errors::{NanoServiceError, NanoServiceErrorStatus};
use std::{net::TcpListener as StdTcpListener, sync::Arc};
pub struct Application {
port: u16,
server: Server,
}
impl Application {
pub async fn build(configuration: Settings) -> Result<Self, NanoServiceError> {
let address = format!(
"{}:{}",
configuration.application.host, configuration.application.port
);
let listener = StdTcpListener::bind(address).map_err(|e| {
NanoServiceError::new(
format!("Failed to bind to address: {}", e),
NanoServiceErrorStatus::NetworkError,
)
})?;
let port = listener.local_addr().unwrap().port();
let server = run(listener, configuration).await?;
Ok(Self { port, server })
}
pub fn port(&self) -> u16 {
self.port
}
pub async fn run_until_stopped(self) -> Result<(), std::io::Error> {
self.server.await
}
}
pub async fn run(
listener: StdTcpListener,
configuration: Settings,
) -> Result<Server, NanoServiceError> {
let token_cache = Arc::new(TokenCache::default());
let secret = Arc::new(configuration.application.secret);
// Connect to SurrealDB
let surreal = connect_surrealdb(&configuration.database)
.await
.map_err(|e| {
NanoServiceError::new(
format!("Failed to connect to DB: {e}"),
NanoServiceErrorStatus::SurrealDbErr,
)
})?;
let descriptor = Arc::new(SurrealDescriptor { db: Ok(surreal) });
let server = HttpServer::new(move || {
bootload_app(
Arc::clone(&descriptor),
Arc::clone(&secret),
Arc::clone(&token_cache),
)
})
.listen(listener)
.map_err(|e| {
NanoServiceError::new(
format!("Failed to bind to listener: {e}"),
NanoServiceErrorStatus::NetworkError,
)
})?
.run();
Ok(server)
}
pub fn bootload_app(
db_descriptor: Arc<SurrealDescriptor>,
secret: Arc<String>,
token_cache: Arc<TokenCache>,
) -> App<
impl ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<impl MessageBody>,
Error = ActixError,
InitError = (),
>,
> {
let cors = Cors::default()
.allow_any_origin()
.allowed_methods(vec!["GET", "POST", "DELETE"])
.allowed_headers(vec![
header::CONTENT_TYPE,
header::AUTHORIZATION,
header::ACCEPT,
HeaderName::from_static("x-user-agent"),
])
.supports_credentials();
App::new()
.wrap(Logger::default())
.wrap(cors)
.app_data(web::Data::new(db_descriptor))
.app_data(web::Data::new(token_cache))
// .app_data(web::Data::new(secret))
.app_data(web::Data::new((*secret).clone())) // clone actual String, not Arc
.configure(auth_service_factory)
}
//!routes\mod.rs
pub mod health;
use crate::handlers::login;
use crate::handlers::register;
use actix_web::web::{ServiceConfig, scope};
pub fn auth_service_factory(app: &mut ServiceConfig) {
app.service(
scope("api/v1/auth")
.service(login::login)
.service(register::register_user),
);
}
//!handlers\register.rs
use crate::helpers::{
service_error::AppError, token::generate_tokens, username::is_username_valid,
};
use crate::profile::helpers::device_info::get_user_agent;
use crate::structs::responses::UserSignupResponse;
use crate::structs::{
descriptors::SurrealDescriptor, requests::UserLoginRequest, responses::GenericResponse,
};
use actix_web::{HttpRequest, HttpResponse, Responder, post, web};
use argon2::{Argon2, PasswordHasher, password_hash::SaltString};
use rand::{Rng, distributions::Alphanumeric, rngs::OsRng};
use serde_json::json;
use shared_libs::mock_time::now;
use surrealdb::Object;
use tracing::{error,info};
#[post("/signup")]
pub async fn register_user(
req: HttpRequest,
body: web::Json<UserLoginRequest>,
db: web::Data<SurrealDescriptor>,
secret: web::Data<String>,
) -> Result<impl Responder, AppError> {
info!("Hit Regstration service {:#?} ", secret);
println!("{:?}", secret);
let body = body.into_inner();
let username_lower = body.username.to_lowercase();
// check if user alrady exists
let existing_user: Option<Object> = match db.db.as_ref() {
Ok(db) => db
.query("SELECT * FROM user WHERE username = $username")
.bind(("username", username_lower.clone()))
.await
.and_then(|mut response| response.take(0))
.map_err(|err| {
error!("Database Error [{}]", err);
HttpResponse::InternalServerError().json(AppError::DatabaseQuery.to_response())
})?,
Err(err) => {
error!("Database connection error: {}", err);
return Ok(HttpResponse::InternalServerError()
.json(AppError::DatabaseConnection.to_response()));
}
};
// chacking exixting user
if existing_user.is_some() {
let error_response = GenericResponse {
code: "USER_ALREADY_EXISTS".to_string(),
message: format!("User with username: {} already exists", username_lower),
};
return Ok(HttpResponse::Conflict().json(error_response));
}
// validating user for more standard usecase
if let Some(exception) = is_username_valid(&body.username) {
return Ok(HttpResponse::BadRequest().json(exception.to_response()));
}
// validating user password usecase
if let Some(exception) = is_username_valid(&body.username) {
return Ok(HttpResponse::BadRequest().json(exception.to_response()));
}
// Hash the password
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = match argon2.hash_password(body.password.as_bytes(), &salt) {
Ok(hash) => hash.to_string(),
Err(err) => {
error!("Error: {}", err);
return Ok(
HttpResponse::InternalServerError().json(AppError::PasswordHash.to_response())
);
}
};
// Generate recovery codes
let mut clear_recovery_codes = Vec::new();
let mut hashed_recovery_codes = Vec::new();
for _ in 1..5 {
let code: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(16)
.map(char::from)
.collect();
clear_recovery_codes.push(code.clone());
let hashed_code = match argon2.hash_password(code.as_bytes(), &salt) {
Ok(hash) => hash.to_string(),
Err(e) => {
error!("Error: {}", e);
return Ok(HttpResponse::InternalServerError().json(GenericResponse {
code: "RECOVERY_CODE_HASH".to_string(),
message: "Failed to hash recovery code".to_string(),
}));
}
};
hashed_recovery_codes.push(hashed_code);
}
let user_id = surrealdb::Uuid::new_v4();
let created_at = now();
let updated_at = now();
// Create the user in SurrealDB
let db = match db.db.as_ref() {
Ok(db) => db,
Err(e) => {
error!("Database connection error: {}", e);
return Ok(HttpResponse::InternalServerError()
.json(AppError::DatabaseConnection.to_response()));
}
};
let _user: Option<Object> = db
.create(("user", user_id))
.content(json!({
"id": user_id,
"username": username_lower,
"password": password_hash,
"otp_verified": false,
"otp_base32": None::<String>,
"otp_auth_url": None::<String>,
"created_at": created_at,
"updated_at": updated_at,
"recovery_codes": hashed_recovery_codes.join(";"),
"password_is_expired": false,
"is_admin": false,
}))
.await
.map_err(|e| {
error!("Error creating user: {}", e);
HttpResponse::InternalServerError().json(GenericResponse {
code: "USER_CREATION".to_string(),
message: "Failed to create user".to_string(),
})
})?;
let parsed_device_info = get_user_agent(req).await;
let (access_token, refresh_token) = match generate_tokens(
db,
secret.as_bytes(),
user_id,
false, // is_admin
parsed_device_info,
)
.await
{
Ok((access_token, refresh_token)) => (access_token, refresh_token),
Err(e) => {
error!("Error: {}", e);
return Ok(
HttpResponse::InternalServerError().json(AppError::TokenGeneration.to_response())
);
}
};
let json_respose = UserSignupResponse {
code: "USER_SIGNED_UP".to_string(),
recovery_codes: clear_recovery_codes,
access_token,
refresh_token,
};
Ok(HttpResponse::Created().json(json_respose))
} this is applications simple code that will help you to study the root cause for Requested application data is not configured correctly.(500 Internal Service Error) Your Environment
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
pub fn bootload_app(
db_descriptor: Arc<SurrealDescriptor>,
...
.app_data(web::Data::new(db_descriptor)) I suspect this is your issue, currently this sets up app data with the type |
Beta Was this translation helpful? Give feedback.
I suspect this is your issue, currently this sets up app data with the type
Data<Arc<SurrealDescriptor>>
. You will want to use theFrom<Arc<T>>
impl like.app_data(web::Data::from(db_descriptor))
in order to get aData<SurrealDescriptor>
in the app data.