Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
lslezak committed Apr 4, 2024
1 parent e9482ab commit 230e910
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 61 deletions.
66 changes: 63 additions & 3 deletions rust/agama-server/src/web/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use super::{
state::ServiceState,
};
use axum::{
body::Body,
extract::State,
http::{header::SET_COOKIE, HeaderMap},
http::{header, HeaderMap, HeaderValue, StatusCode},
response::IntoResponse,
Json,
};
use axum_extra::extract::cookie::CookieJar;
use pam::Client;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
Expand Down Expand Up @@ -62,7 +64,7 @@ pub async fn login(
let mut headers = HeaderMap::new();
let cookie = format!("agamaToken={}; HttpOnly", &token);
headers.insert(
SET_COOKIE,
header::SET_COOKIE,
cookie.parse().expect("could not build a valid cookie"),
);

Expand All @@ -76,7 +78,7 @@ pub async fn logout(_claims: TokenClaims) -> Result<impl IntoResponse, AuthError
let mut headers = HeaderMap::new();
let cookie = "agamaToken=deleted; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:00 GMT".to_string();
headers.insert(
SET_COOKIE,
header::SET_COOKIE,
cookie.parse().expect("could not build a valid cookie"),
);
Ok(headers)
Expand All @@ -90,3 +92,61 @@ pub async fn logout(_claims: TokenClaims) -> Result<impl IntoResponse, AuthError
pub async fn session(_claims: TokenClaims) -> Result<(), AuthError> {
Ok(())
}

// builds a response tuple for translation redirection
fn redirect_to_file(file: &str) -> (StatusCode, HeaderMap, Body) {
tracing::info!("Redirecting to translation file: {}", file);

let mut response_headers = HeaderMap::new();
// translation found, redirect to the real file
response_headers.insert(
header::LOCATION,
// if the file exists then the name is a valid value and unwrapping is safe
HeaderValue::from_str(file).unwrap(),
);

(
StatusCode::TEMPORARY_REDIRECT,
response_headers,
Body::empty(),
)
}

// handle the /po.js request
// the requested language (locale) is sent in the "agamaLang" HTTP cookie
// this reimplements the Cockpit translation support
pub async fn po(State(state): State<ServiceState>, jar: CookieJar) -> impl IntoResponse {
if let Some(cookie) = jar.get("agamaLang") {
tracing::info!("Language cookie: {}", cookie.value());
// try parsing the cookie
if let Some((lang, region)) = cookie.value().split_once('-') {
// first try language + country
let target_file = format!("po.{}_{}.js", lang, region.to_uppercase());
if state.public_dir.join(&target_file).exists() {
return redirect_to_file(&target_file);
} else {
// then try the language only
let target_file = format!("po.{}.js", lang);
if state.public_dir.join(&target_file).exists() {
return redirect_to_file(&target_file);
};
}
} else {
// use the cookie as is
let target_file = format!("po.{}.js", cookie.value());
if state.public_dir.join(&target_file).exists() {
return redirect_to_file(&target_file);
}
}
}

tracing::info!("Translation not found");
// fallback, return empty javascript translations if the language is not supported
let mut response_headers = HeaderMap::new();
response_headers.insert(
header::CONTENT_TYPE,
HeaderValue::from_static("text/javascript"),
);

(StatusCode::OK, response_headers, Body::empty())
}
61 changes: 3 additions & 58 deletions rust/agama-server/src/web/service.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use super::http::{login, logout, session};
use super::{auth::TokenClaims, config::ServiceConfig, state::ServiceState, EventsSender};
use axum::{
body::Body,
extract::Request,
http::{header, HeaderMap, HeaderValue, StatusCode},
middleware,
response::IntoResponse,
routing::{get, post},
Router,
};
use axum_extra::extract::cookie::CookieJar;
use std::{
convert::Infallible,
path::{Path, PathBuf},
Expand Down Expand Up @@ -77,6 +74,7 @@ impl MainServiceBuilder {
let state = ServiceState {
config: self.config,
events: self.events,
public_dir: self.public_dir.clone(),
};

let api_router = self
Expand All @@ -87,65 +85,12 @@ impl MainServiceBuilder {
.route("/ping", get(super::http::ping))
.route("/auth", post(login).get(session).delete(logout));

tracing::info!("Serving static files from {}", self.public_dir.display());
let serve = ServeDir::new(self.public_dir);

// handle the /po.js request
// the requested language (locale) is sent in the "agamaLanguage" HTTP cookie
// this reimplements the Cockpit translation support
async fn po(jar: CookieJar) -> impl IntoResponse {
let mut response_headers = HeaderMap::new();

if let Some(cookie) = jar.get("agamaLanguage") {
let mut target_file = String::new();
let mut found = false;
// FIXME: this does not work, the public_dir setting is not accessible :-/
// when using something like PathBuf::from("/usr/share/cockpit/agama") here
// it works just fine....
let prefix = self.public_dir;

// try parsing the cookie
if let Some((lang, region)) = cookie.value().split_once('-') {
// first try the full locale
target_file = format!("po.{}_{}.js", lang, region.to_uppercase());
found = prefix.join(&target_file).exists();

if !found {
// then try the language only
target_file = format!("po.{}.js", lang);
found = prefix.join(&target_file).exists();
}
}

if !found {
// use the full cookie without parsing
target_file = format!("po.{}.js", cookie.value());
found = prefix.join(&target_file).exists();
}

if found {
// translation found, redirect to the real file
response_headers.insert(
header::LOCATION,
// if the file exists then the name is a valid header value and unwrapping is safe
HeaderValue::from_str(&target_file).unwrap()
);

return (StatusCode::TEMPORARY_REDIRECT, response_headers, Body::empty())
}
}

// fallback, return empty javascript translations if the language is not supported
response_headers.insert(
header::CONTENT_TYPE,
HeaderValue::from_static("text/javascript"),
);

(StatusCode::OK, response_headers, Body::empty())
}

Router::new()
.nest_service("/", serve)
.route("/po.js", get(po))
.route("/po.js", get(super::http::po))
.nest("/api", api_router)
.layer(TraceLayer::new_for_http())
.layer(CompressionLayer::new().br(true))
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-server/src/web/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Implements the web service state.

use super::{config::ServiceConfig, EventsSender};
use std::path::PathBuf;

/// Web service state.
///
Expand All @@ -9,4 +10,5 @@ use super::{config::ServiceConfig, EventsSender};
pub struct ServiceState {
pub config: ServiceConfig,
pub events: EventsSender,
pub public_dir: PathBuf,
}

0 comments on commit 230e910

Please sign in to comment.