mirror of
https://github.com/kristoferssolo/Axium.git
synced 2025-10-21 16:00:34 +00:00
99 lines
3.7 KiB
Rust
99 lines
3.7 KiB
Rust
use axum::{extract::{Extension, State}, Json};
|
|
use axum::http::StatusCode;
|
|
use chrono::{Duration, Utc};
|
|
use serde_json::json;
|
|
use sqlx::postgres::PgPool;
|
|
use tracing::{error, info};
|
|
use validator::Validate;
|
|
|
|
use crate::utils::auth::{generate_api_key, hash_password};
|
|
use crate::models::user::User;
|
|
use crate::database::apikeys::{check_existing_api_key_count, insert_api_key_into_db};
|
|
use crate::models::apikey::{ApiKeyInsertBody, ApiKeyInsertResponse};
|
|
|
|
// --- Route Handler ---
|
|
|
|
// Define the API endpoint
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/apikeys",
|
|
tag = "apikey",
|
|
security(
|
|
("jwt_token" = [])
|
|
),
|
|
request_body = ApiKeyInsertBody,
|
|
responses(
|
|
(status = 200, description = "API key created successfully", body = ApiKeyInsertResponse),
|
|
(status = 400, description = "Validation error", body = String),
|
|
(status = 401, description = "Unauthorized", body = serde_json::Value),
|
|
(status = 500, description = "Internal server error", body = String)
|
|
)
|
|
)]
|
|
pub async fn post_apikey(
|
|
State(pool): State<PgPool>,
|
|
Extension(user): Extension<User>,
|
|
Json(api_key_request): Json<ApiKeyInsertBody>
|
|
) -> Result<Json<ApiKeyInsertResponse>, (StatusCode, Json<serde_json::Value>)> {
|
|
// Validate input
|
|
if let Err(errors) = api_key_request.validate() {
|
|
let error_messages: Vec<String> = errors
|
|
.field_errors()
|
|
.iter()
|
|
.flat_map(|(_, errors)| errors.iter().map(|e| e.message.clone().unwrap_or_default().to_string()))
|
|
.collect();
|
|
return Err((
|
|
StatusCode::BAD_REQUEST,
|
|
Json(json!({ "error": error_messages.join(", ") }))
|
|
));
|
|
}
|
|
|
|
info!("Received request to create API key for user: {}", user.id);
|
|
|
|
// Check if the user already has 5 or more API keys
|
|
let existing_keys_count = match check_existing_api_key_count(&pool, user.id).await {
|
|
Ok(count) => count,
|
|
Err(err) => {
|
|
error!("Failed to check the amount of API keys for user {}: {}", user.id, err);
|
|
return Err((
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(json!({ "error": "Could not check the amount of API keys registered." }))
|
|
));
|
|
}
|
|
};
|
|
|
|
if existing_keys_count >= 5 {
|
|
info!("User {} already has 5 API keys.", user.id);
|
|
return Err((
|
|
StatusCode::BAD_REQUEST,
|
|
Json(json!({ "error": "You already have 5 API keys. Please delete an existing key before creating a new one." }))
|
|
));
|
|
}
|
|
|
|
let current_date = Utc::now().naive_utc();
|
|
let description = api_key_request.description
|
|
.unwrap_or_else(|| format!("API key created on {}", current_date.format("%Y-%m-%d")));
|
|
|
|
let expiration_date = api_key_request.expiration_date
|
|
.and_then(|date| date.parse::<chrono::NaiveDate>().ok())
|
|
.unwrap_or_else(|| (current_date + Duration::days(365 * 2)).date());
|
|
|
|
let api_key = generate_api_key();
|
|
let key_hash = hash_password(&api_key).expect("Failed to hash password.");
|
|
|
|
match insert_api_key_into_db(&pool, key_hash, description, expiration_date, user.id).await {
|
|
Ok(mut api_key_response) => {
|
|
info!("Successfully created API key for user: {}", user.id);
|
|
// Restore generated api_key to response. It is not stored in database for security reasons.
|
|
api_key_response.api_key = api_key;
|
|
Ok(Json(api_key_response))
|
|
}
|
|
Err(err) => {
|
|
error!("Error creating API key for user {}: {}", user.id, err);
|
|
Err((
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(json!({ "error": format!("Error creating API key: {}.", err) }))
|
|
))
|
|
}
|
|
}
|
|
}
|