Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/handlers/icons.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use axum::{
extract::{Path, State},
http::StatusCode,
response::Response,
};
use std::sync::Arc;
use worker::Env;

use crate::error::AppError;

#[worker::send]
pub async fn get_icon(
State(_env): State<Arc<Env>>,
Path(path): Path<String>,
) -> Result<Response, AppError> {
let domain = path
.strip_suffix("/icon.png")
.unwrap_or(&path)
.to_string();

let target_url = format!("https://vault.bitwarden.com/icons/{}/icon.png", domain);

let request = worker::Request::new(&target_url, worker::Method::Get)
.map_err(|_| AppError::Internal)?;

let mut upstream_response = worker::Fetch::Request(request)
.send()
.await
.map_err(|e| {
log::error!("Failed to fetch icon from Bitwarden: {:?}", e);
AppError::Internal
})?;

let status = upstream_response.status_code();

let body_bytes = upstream_response
.bytes()
.await
.map_err(|e| {
log::error!("Failed to read icon response body: {:?}", e);
AppError::Internal
})?;

let mut response = Response::new(axum::body::Body::from(body_bytes));
*response.status_mut() = StatusCode::from_u16(status)
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);

if let Ok(Some(content_type)) = upstream_response.headers().get("content-type") {
if let Ok(header_value) = axum::http::HeaderValue::from_str(&content_type) {
response.headers_mut().insert(
axum::http::header::CONTENT_TYPE,
header_value,
);
}
}

response.headers_mut().insert(
axum::http::header::CACHE_CONTROL,
axum::http::HeaderValue::from_static("public, max-age=604800, immutable"),
);

Ok(response)
}
1 change: 1 addition & 0 deletions src/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pub mod two_factor;
pub mod devices;
pub mod sends;
pub mod usage;
pub mod icons;
3 changes: 2 additions & 1 deletion src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ use axum::extract::DefaultBodyLimit;
use std::sync::Arc;
use worker::Env;

use crate::handlers::{accounts, ciphers, config, identity, sync, folders, import, two_factor, devices, sends, usage};
use crate::handlers::{accounts, ciphers, config, identity, sync, folders, import, two_factor, devices, sends, usage, icons};

pub fn api_router(env: Env) -> Router {
let app_state = Arc::new(env);

Router::new()
.route("/demo.html", get(|| async { Html(include_str!("../static/demo.html")) }))
.route("/icons/{*path}", get(icons::get_icon))
// Identity/Auth routes
.route("/identity/accounts/prelogin", post(accounts::prelogin))
.route("/api/accounts/prelogin", post(accounts::prelogin))
Expand Down
2 changes: 1 addition & 1 deletion wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@
"assets": {
"directory": "./static/web-vault",
"not_found_handling": "single-page-application",
"run_worker_first": ["/api/*", "/identity/*", "/sends/*", "/demo.html"]
"run_worker_first": ["/api/*", "/identity/*", "/sends/*", "/demo.html", "/icons/*"]
}
}