Restore startup banner output
This commit is contained in:
@@ -95,6 +95,7 @@ Agents should insert at least:
|
||||
- Improve error messages and error‑mapping logic.
|
||||
- Add missing context fields to logs.
|
||||
- Ensure compliance with this standard.
|
||||
- Preserve the startup `println!` banner and metadata in `src/backend/src/main.rs` for debug visibility.
|
||||
|
||||
### Agents **may not**:
|
||||
- Modify business logic, algorithms, backend behavior, or API responses (other than error messages).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use argon2::{
|
||||
Argon2,
|
||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
|
||||
password_hash::{PasswordHasher, SaltString, rand_core::OsRng},
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
@@ -10,6 +10,6 @@ pub fn hash_password(password: &str) -> Result<String, password_hash::Error> {
|
||||
let hash = argon2
|
||||
.hash_password(password.as_bytes(), &salt)?
|
||||
.to_string();
|
||||
debug!("Hashed password {}", hash);
|
||||
debug!("password hashed");
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
use crate::{
|
||||
domain::{
|
||||
user::{self, InternalUser},
|
||||
user_prems::UserPermissions,
|
||||
},
|
||||
domain::{user::InternalUser, user_prems::UserPermissions},
|
||||
infra::db,
|
||||
prelude::*,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{Json, http::StatusCode};
|
||||
use axum::http::StatusCode;
|
||||
use uuid::Uuid;
|
||||
use validator::Validate;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::{
|
||||
domain::user::{InternalNewUser, NewUser, User},
|
||||
@@ -20,29 +17,33 @@ use crate::{
|
||||
};
|
||||
|
||||
pub async fn create(state: Arc<AppState>, new_user: NewUser) -> Result<User, StatusCode> {
|
||||
debug!("create user started");
|
||||
|
||||
new_user.validate().map_err(|e| {
|
||||
error!("User validation failed: {e}");
|
||||
error!(error = %e, "user validation failed");
|
||||
StatusCode::BAD_REQUEST
|
||||
})?;
|
||||
|
||||
let internal = InternalNewUser::try_from(new_user).map_err(|e| {
|
||||
error!("Conversion to InternalUser failed: {e}");
|
||||
error!(error = %e, "convert to internal user failed");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let created_user = db::user::create(&state.db_pool, internal)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to create new user: {e}");
|
||||
error!(error = %e, "create user failed");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
info!(user_uuid = %created_user.uuid, "user created");
|
||||
Ok(User::from(created_user))
|
||||
}
|
||||
|
||||
pub async fn get_all(state: Arc<AppState>) -> Result<Vec<User>, StatusCode> {
|
||||
debug!("fetch all users started");
|
||||
let users = db::user::get_safe_all(&state.db_pool).await.map_err(|e| {
|
||||
error!("Failed to fetch all users: {e}");
|
||||
error!(error = %e, "fetch all users failed");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
@@ -53,16 +54,18 @@ pub async fn get_safe_by_uuid(
|
||||
state: Arc<AppState>,
|
||||
uuid: Uuid,
|
||||
) -> Result<Option<User>, StatusCode> {
|
||||
debug!(user_uuid = %uuid, "fetch user by uuid started");
|
||||
let user = db::user::get_safe_by_uuid(&state.db_pool, uuid.clone())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to fetch user: {e}");
|
||||
error!(error = %e, user_uuid = %uuid, "fetch user failed");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn get_by_uuid(state: Arc<AppState>, uuid: Uuid) -> anyhow::Result<Option<InternalUser>> {
|
||||
debug!(user_uuid = %uuid, "fetch internal user started");
|
||||
let mut user = match db::user::get_by_uuid(&state.db_pool, uuid)
|
||||
.await
|
||||
.context("failed to fetch user by uuid")?
|
||||
@@ -93,23 +96,26 @@ pub async fn set_perms(
|
||||
state: Arc<AppState>,
|
||||
user_perms: UserPermissions,
|
||||
) -> Result<(), StatusCode> {
|
||||
debug!(user_uuid = %user_perms.uuid, "assign permissions started");
|
||||
let exists = db::user::exists_by_uuid(&state.db_pool, user_perms.uuid)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to verify user exists: {e}");
|
||||
error!(error = %e, user_uuid = %user_perms.uuid, "verify user exists failed");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if !exists {
|
||||
warn!(user_uuid = %user_perms.uuid, "assign permissions skipped for missing user");
|
||||
return Err(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
db::perms::create(&state.db_pool, user_perms)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to create user permissions entry: {e}");
|
||||
error!(error = %e, "create user permissions entry failed");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
info!("user permissions assigned");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use axum::response::IntoResponse;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::prelude::FromRow;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use serde::{Deserialize, de::value};
|
||||
use serde::Deserialize;
|
||||
use sqlx::prelude::FromRow;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
@@ -5,19 +5,24 @@ use std::time::Duration;
|
||||
|
||||
use sqlx::{PgPool, postgres::PgPoolOptions};
|
||||
|
||||
use crate::prelude::*;
|
||||
use anyhow::Result;
|
||||
|
||||
pub async fn connect(database_url: &str) -> Result<PgPool> {
|
||||
debug!("open postgres pool started");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(10)
|
||||
.acquire_timeout(Duration::from_secs(5))
|
||||
.connect(database_url)
|
||||
.await?;
|
||||
|
||||
debug!("open postgres pool completed");
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
pub async fn migrate(pool: &PgPool) -> Result<()> {
|
||||
debug!("database migration started");
|
||||
sqlx::migrate!().run(pool).await?;
|
||||
debug!("database migration completed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ use anyhow::Result;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::domain::{user::User, user_prems::UserPermissions};
|
||||
use crate::{domain::user_prems::UserPermissions, prelude::*};
|
||||
|
||||
pub async fn create(pool: &PgPool, new_perms: UserPermissions) -> Result<UserPermissions> {
|
||||
debug!(user_uuid = %new_perms.uuid, "insert user permissions started");
|
||||
let perms = sqlx::query_as::<_, UserPermissions>(
|
||||
r#"
|
||||
INSERT INTO user_permissions (uuid, root, manage_users, login)
|
||||
@@ -19,10 +20,12 @@ pub async fn create(pool: &PgPool, new_perms: UserPermissions) -> Result<UserPer
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
debug!(user_uuid = %perms.uuid, "insert user permissions completed");
|
||||
Ok(perms)
|
||||
}
|
||||
|
||||
pub async fn get_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<Option<UserPermissions>> {
|
||||
debug!(user_uuid = %uuid, "fetch user permissions by uuid started");
|
||||
let perms = sqlx::query_as::<_, UserPermissions>(
|
||||
r#"
|
||||
SELECT uuid, root, manage_users, login
|
||||
@@ -34,10 +37,12 @@ pub async fn get_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<Option<UserPermiss
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
debug!(user_uuid = %uuid, "fetch user permissions by uuid completed");
|
||||
Ok(perms)
|
||||
}
|
||||
|
||||
pub async fn exists_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<bool> {
|
||||
debug!(user_uuid = %uuid, "check user permissions existence started");
|
||||
let exists = sqlx::query_scalar::<_, bool>(
|
||||
r#"
|
||||
SELECT EXISTS(
|
||||
@@ -51,5 +56,6 @@ pub async fn exists_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<bool> {
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
debug!(user_uuid = %uuid, "check user permissions existence completed");
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub async fn create(pool: &PgPool, new_user: InternalNewUser) -> Result<InternalUser> {
|
||||
debug!(user_uuid = %new_user.uuid, "insert user started");
|
||||
let user = sqlx::query_as::<_, InternalUser>(
|
||||
r#"
|
||||
INSERT INTO users (uuid, username, email, password_hash, first_name, last_name)
|
||||
@@ -23,10 +24,12 @@ pub async fn create(pool: &PgPool, new_user: InternalNewUser) -> Result<Internal
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
debug!(user_uuid = %user.uuid, "insert user completed");
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn get_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<Option<InternalUser>> {
|
||||
debug!(user_uuid = %uuid, "fetch user by uuid started");
|
||||
let user = sqlx::query_as::<_, InternalUser>(
|
||||
r#"
|
||||
SELECT uuid, username, email, password_hash, first_name, last_name FROM users WHERE uuid = $1
|
||||
@@ -36,10 +39,12 @@ pub async fn get_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<Option<InternalUse
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
debug!(user_uuid = %uuid, "fetch user by uuid completed");
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn get_safe_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<Option<User>> {
|
||||
debug!(user_uuid = %uuid, "fetch safe user by uuid started");
|
||||
let user = sqlx::query_as::<_, User>(
|
||||
r#"
|
||||
SELECT uuid, username, email, first_name, last_name FROM users WHERE uuid = $1
|
||||
@@ -49,10 +54,12 @@ pub async fn get_safe_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<Option<User>>
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
debug!(user_uuid = %uuid, "fetch safe user by uuid completed");
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn get_all(pool: &PgPool) -> Result<Vec<InternalUser>> {
|
||||
debug!("fetch all internal users started");
|
||||
let users = sqlx::query_as::<_, InternalUser>(
|
||||
r#"
|
||||
SELECT uuid, username, email, password_hash, first_name, last_name
|
||||
@@ -63,10 +70,12 @@ pub async fn get_all(pool: &PgPool) -> Result<Vec<InternalUser>> {
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
debug!("fetch all internal users completed");
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
pub async fn get_safe_all(pool: &PgPool) -> Result<Vec<User>> {
|
||||
debug!("fetch all safe users started");
|
||||
let users = sqlx::query_as::<_, User>(
|
||||
r#"
|
||||
SELECT uuid, username, email, first_name, last_name
|
||||
@@ -77,10 +86,12 @@ pub async fn get_safe_all(pool: &PgPool) -> Result<Vec<User>> {
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
debug!("fetch all safe users completed");
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
pub async fn exists_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<bool> {
|
||||
debug!(user_uuid = %uuid, "check user existence started");
|
||||
let exists = sqlx::query_scalar::<_, bool>(
|
||||
r#"
|
||||
SELECT EXISTS(
|
||||
@@ -94,5 +105,6 @@ pub async fn exists_by_uuid(pool: &PgPool, uuid: Uuid) -> Result<bool> {
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
debug!(user_uuid = %uuid, "check user existence completed");
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::{Ok, Result};
|
||||
use rustymine_daemon::{config::AppCfg, router, state::AppState};
|
||||
use tracing::{Level, debug, error, info, warn};
|
||||
use tracing::{Level, debug, info};
|
||||
|
||||
pub const APP_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -38,6 +38,20 @@ async fn main() -> Result<()> {
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
|
||||
debug!(logo = ASCII_LOGO, "render application banner");
|
||||
info!(
|
||||
app = APP_NAME,
|
||||
version = APP_VERSION,
|
||||
build_mode = BUILD_MODE,
|
||||
"starting application"
|
||||
);
|
||||
info!(
|
||||
git_hash = GIT_HASH,
|
||||
git_suffix = GIT_SUFFIX,
|
||||
build_date = BUILD_DATE,
|
||||
"build metadata"
|
||||
);
|
||||
|
||||
let db_path: String = "postgres://rustymine:minecraft@localhost:5432/rustymine_dev".to_string();
|
||||
let config = AppCfg {
|
||||
db_path: db_path.clone(),
|
||||
@@ -48,7 +62,9 @@ async fn main() -> Result<()> {
|
||||
let app_result = router::init_router(state.clone()).await;
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||
info!(listen_addr = "0.0.0.0:3000", "http server binding started");
|
||||
|
||||
axum::serve(listener, app_result).await?;
|
||||
info!("http server stopped");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
use axum::{
|
||||
Router,
|
||||
extract::Request,
|
||||
http::{Method, header::AUTHORIZATION},
|
||||
middleware::Next,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use tracing::debug;
|
||||
|
||||
pub async fn auth(request: Request, next: Next) -> impl IntoResponse {
|
||||
debug!("auth_middleware entry");
|
||||
let method = request.method().clone();
|
||||
let uri = request.uri().path().to_owned();
|
||||
|
||||
debug!(%method, uri, "auth middleware started");
|
||||
let response = next.run(request).await;
|
||||
debug!("auth_middleware exit");
|
||||
let status = response.status();
|
||||
debug!(%method, uri, %status, "auth middleware completed");
|
||||
response
|
||||
}
|
||||
|
||||
pub fn cors() -> CorsLayer {
|
||||
debug!("Generating CorsLayer");
|
||||
debug!("build cors layer");
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods([Method::GET, Method::POST])
|
||||
|
||||
@@ -8,12 +8,15 @@ use axum::{
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
use std::sync::Arc;
|
||||
use tower::{Layer, ServiceBuilder};
|
||||
use tower::ServiceBuilder;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::state::AppState;
|
||||
|
||||
pub async fn init_router(app_state: Arc<AppState>) -> Router {
|
||||
Router::new()
|
||||
info!("router initialization started");
|
||||
|
||||
let router = Router::new()
|
||||
.route(
|
||||
"/api/ping",
|
||||
get(ping).layer(
|
||||
@@ -33,9 +36,13 @@ pub async fn init_router(app_state: Arc<AppState>) -> Router {
|
||||
)
|
||||
.route("/api/users/{uuid}", get(user_routes::get_uuid))
|
||||
.layer(ServiceBuilder::new().layer(middleware::cors()))
|
||||
.with_state(app_state.clone())
|
||||
.with_state(app_state.clone());
|
||||
|
||||
info!("router initialization completed");
|
||||
router
|
||||
}
|
||||
|
||||
async fn ping() -> Result<Json<Value>, StatusCode> {
|
||||
debug!("ping request received");
|
||||
Ok(Json(json!({ "response": "pong"})))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
core,
|
||||
domain::user::{InternalNewUser, NewUser, User},
|
||||
domain::user::{NewUser, User},
|
||||
state::AppState,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@@ -18,12 +18,16 @@ pub async fn create(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(new_user): Json<NewUser>,
|
||||
) -> Result<Json<User>, StatusCode> {
|
||||
debug!("create user route started");
|
||||
let user = core::user_routines::create(state, new_user).await?;
|
||||
info!("create user route completed");
|
||||
Ok(Json(user))
|
||||
}
|
||||
|
||||
pub async fn get_all(State(state): State<Arc<AppState>>) -> Result<Json<Vec<User>>, StatusCode> {
|
||||
debug!("list users route started");
|
||||
let users = core::user_routines::get_all(state).await?;
|
||||
debug!(user_count = users.len(), "list users route completed");
|
||||
Ok(Json(users))
|
||||
}
|
||||
|
||||
@@ -31,6 +35,8 @@ pub async fn get_uuid(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(uuid): Path<Uuid>,
|
||||
) -> Result<Json<Option<User>>, StatusCode> {
|
||||
debug!(user_uuid = %uuid, "get user by uuid route started");
|
||||
let user = core::user_routines::get_safe_by_uuid(state, uuid).await?;
|
||||
debug!("get user by uuid route completed");
|
||||
Ok(Json(user))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::prelude::*;
|
||||
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{config::AppCfg, domain::user::InternalNewUser, infra::db};
|
||||
use crate::{config::AppCfg, infra::db};
|
||||
|
||||
pub struct AppState {
|
||||
pub db_pool: PgPool,
|
||||
@@ -12,23 +12,25 @@ pub struct AppState {
|
||||
|
||||
impl AppState {
|
||||
pub async fn new(config: &AppCfg) -> Self {
|
||||
debug!("Initiating new AppState");
|
||||
debug!("init app state");
|
||||
debug!("establish database connection");
|
||||
let db_pool = db::connect(&config.db_path)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to connect to database: {e}");
|
||||
error!(error = %e, "connect to database failed");
|
||||
exit(20);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
debug!("run database migrations");
|
||||
db::migrate(&db_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to migrade database: {e}");
|
||||
error!(error = %e, "database migration failed");
|
||||
exit(22);
|
||||
})
|
||||
.unwrap();
|
||||
info!("DB connect and migrate sucessful");
|
||||
info!("database ready after connect and migrate");
|
||||
Self { db_pool: db_pool }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user