Implemented GET and PATCH handler

This commit is contained in:
Dennis Nemec
2025-10-06 00:27:27 +02:00
parent 98fb621108
commit 721fc171d0
7 changed files with 222 additions and 22 deletions

View File

@ -10,6 +10,11 @@ COPY Cargo.toml Cargo.lock ./
# Copy source code
COPY src ./src
# Install runtime dependencies (if needed, e.g., for SSL)
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates libssl-dev pkg-config && \
rm -rf /var/lib/apt/lists/*
# Build for release
RUN cargo build --release
@ -18,7 +23,7 @@ FROM debian:bookworm-slim
# Install runtime dependencies (if needed, e.g., for SSL)
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
apt-get install -y --no-install-recommends ca-certificates libssl-dev && \
rm -rf /var/lib/apt/lists/*
# Create non-root user

View File

@ -23,7 +23,7 @@ services:
dockerfile: Dockerfile
container_name: rust-microservice
ports:
- "3000:8080"
- "3000:3000"
environment:
- REDIS_URL=redis://redis:6379
- RUST_LOG=info

View File

@ -95,6 +95,182 @@ pub async fn handle_post(
}
}
pub async fn handle_get(
Extension(state): Extension<Arc<AppState>>,
request: Request<Body>,
) -> impl IntoResponse {
let cloned_state = state.clone();
let (mut parts, body) = request.into_parts();
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let mut forwarded_request = cloned_state
.gsd_service
.forward_get_request(Request::from_parts(
parts.clone(),
Body::from(body_bytes.clone()),
))
.await;
if forwarded_request.is_err() {
error!(
"Failed to forward post: {:?}",
forwarded_request.err().unwrap()
);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let content_type = forwarded_request.as_ref().unwrap().headers().get("Content-Type").unwrap().to_str().unwrap();
if content_type == "application/json" {
let content_text = forwarded_request.unwrap().text().await;
if content_text.is_err() {
error!("Failed to read content text: {:?}", content_text.err());
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let content = serde_json::from_str::<GSDResponseDTO>(content_text.as_ref().unwrap().as_str());
if content.is_err() {
error!("Failed to read content json: {:?}", content.err());
error!("Content: {:?}", content_text);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let content_unwrapped = content.unwrap();
// Invalid session
if content_unwrapped.status.is_some()
&& content_unwrapped.status.unwrap().internal_status == "201"
{
info!("Session invalid. Re-negotiate new session");
match cloned_state.gsd_service.get_session().await {
Ok(session) => {
set_and_log_session(&cloned_state, session.clone()).await;
parts.headers.remove("sessionId");
parts.headers.insert(
"sessionId",
HeaderValue::from_str(session.clone().as_str()).unwrap(),
);
forwarded_request = cloned_state
.gsd_service
.forward_get_request(Request::from_parts(
parts.clone(),
Body::from(body_bytes.clone()),
))
.await;
if let Err(e) = &forwarded_request {
error!("Redis: failed to forward patch: {:?}", e);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
forwarded_request
.unwrap()
.text()
.await
.unwrap()
.into_response()
}
Err(error) => {
error!("Error getting session: {:?}", error);
StatusCode::UNAUTHORIZED.into_response()
}
}
} else {
content_text.unwrap().into_response()
}
} else {
forwarded_request.unwrap().bytes().await.unwrap().into_response()
}
}
pub async fn handle_patch(
Extension(state): Extension<Arc<AppState>>,
request: Request<Body>,
) -> impl IntoResponse {
let cloned_state = state.clone();
let (mut parts, body) = request.into_parts();
let body_bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
let mut forwarded_request = cloned_state
.gsd_service
.forward_patch_request(Request::from_parts(
parts.clone(),
Body::from(body_bytes.clone()),
))
.await;
if forwarded_request.is_err() {
error!(
"Failed to forward post: {:?}",
forwarded_request.err().unwrap()
);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let content_text = forwarded_request.unwrap().text().await;
if content_text.is_err() {
error!("Failed to read content text: {:?}", content_text.err());
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let content = serde_json::from_str::<GSDResponseDTO>(content_text.as_ref().unwrap().as_str());
if content.is_err() {
error!("Failed to read content json: {:?}", content.err());
error!("Content: {:?}", content_text);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
let content_unwrapped = content.unwrap();
// Invalid session
if content_unwrapped.status.is_some()
&& content_unwrapped.status.unwrap().internal_status == "201"
{
info!("Session invalid. Re-negotiate new session");
match cloned_state.gsd_service.get_session().await {
Ok(session) => {
set_and_log_session(&cloned_state, session.clone()).await;
parts.headers.remove("sessionId");
parts.headers.insert(
"sessionId",
HeaderValue::from_str(session.clone().as_str()).unwrap(),
);
forwarded_request = cloned_state
.gsd_service
.forward_patch_request(Request::from_parts(
parts.clone(),
Body::from(body_bytes.clone()),
))
.await;
if let Err(e) = &forwarded_request {
error!("Redis: failed to forward post: {:?}", e);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
forwarded_request
.unwrap()
.text()
.await
.unwrap()
.into_response()
}
Err(error) => {
error!("Error getting session: {:?}", error);
StatusCode::UNAUTHORIZED.into_response()
}
}
} else {
content_text.unwrap().into_response()
}
}
pub async fn userinfo(request: Request<Body>) -> impl IntoResponse {
let access_token_string = &request.headers().get("authorization").unwrap().to_str().unwrap().to_string()[7..];
let user = decode_payload_unchecked::<User>(access_token_string).unwrap();

View File

@ -16,7 +16,7 @@ pub struct GSDResponseDTO {
#[derive(serde::Deserialize, serde::Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct GSDLoginResponseDataDTO {
pub session_id: String,
pub session_id: Option<String>,
}
#[derive(serde::Deserialize, serde::Serialize, Debug)]

View File

@ -62,10 +62,10 @@ impl GSDService {
match response_dto.data {
Some(data) => {
info!(
"Session: successfully obtained session with session id {}",
&data.session_id
"Session: successfully obtained session with session id {:?}",
&data.session_id.as_ref()
);
Ok(data.session_id.clone())
Ok(data.session_id.unwrap())
}
None => {
error!("Session: failed to obtain session id. No session id in request found.");
@ -89,6 +89,36 @@ impl GSDService {
.await
.map_err(|e| GSDServiceError::RequestError(e.to_string()))
}
pub async fn forward_patch_request(
&self,
request: Request<Body>,
) -> Result<Response, GSDServiceError> {
let (parts, body) = request.into_parts();
reqwest::Client::new()
.patch(format!("{}{}", self.host_url.clone(), parts.uri))
.headers(parts.headers)
.body(axum::body::to_bytes(body, usize::MAX).await.unwrap())
.send()
.await
.map_err(|e| GSDServiceError::RequestError(e.to_string()))
}
pub async fn forward_get_request(
&self,
request: Request<Body>,
) -> Result<Response, GSDServiceError> {
let (parts, body) = request.into_parts();
reqwest::Client::new()
.get(format!("{}{}", self.host_url.clone(), parts.uri))
.headers(parts.headers)
.body(axum::body::to_bytes(body, usize::MAX).await.unwrap())
.send()
.await
.map_err(|e| GSDServiceError::RequestError(e.to_string()))
}
}
impl From<&Config> for GSDService {

View File

@ -1,9 +1,9 @@
use crate::api::{handle_post, userinfo};
use crate::api::{handle_get, handle_patch, handle_post, userinfo};
use crate::config::load_config;
use crate::middleware::AppState;
use crate::repository::RedisRepository;
use crate::util::initialize_logging;
use axum::routing::{get, post};
use axum::routing::{get, patch, post};
use axum::{Extension, Router};
use axum_keycloak_auth::instance::{KeycloakAuthInstance, KeycloakConfig};
use axum_keycloak_auth::layer::KeycloakAuthLayer;
@ -53,6 +53,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let auth_router = auth::router(state.clone());
let proxy_router = Router::new()
.route("/{*wildcard}", post(handle_post))
.route("/{*wildcard}", get(handle_get))
.route("/{*wildcard}", patch(handle_patch))
.route("/userinfo", get(userinfo))
.route_layer(Extension(state.clone()))
.route_layer(axum::middleware::from_fn_with_state(

View File

@ -20,14 +20,6 @@ pub struct AppState {
pub frontend_url: String,
}
pub async fn auth_middleware(
State(_state): State<Arc<AppState>>,
request: Request,
next: Next,
) -> Response {
next.run(request).await
}
/// Middleware to validate session and refresh tokens if needed
pub async fn session_auth_middleware(
jar: CookieJar,
@ -131,7 +123,6 @@ pub async fn gsd_decorate_header(
next: Next,
) -> Response {
let state_cloned = state.clone();
info!("Gsd decorate header");
let session = state_cloned.repository.get_session().await;
match session {
@ -172,10 +163,6 @@ pub async fn gsd_decorate_header(
HeaderValue::from_str(state_cloned.config.gsd_app_key.as_str()).unwrap(),
);
let response = next.run(request).await;
info!("Response: {:?}", response);
response
next.run(request).await
}