diff --git a/src/handlers/attachments.rs b/src/handlers/attachments.rs index 3175f7d..6891fdb 100644 --- a/src/handlers/attachments.rs +++ b/src/handlers/attachments.rs @@ -4,7 +4,7 @@ use axum::{ extract::{Multipart, Path, Query, State}, http::{header, StatusCode}, response::Response, - Json, + Extension, Json, }; use chrono::Utc; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; @@ -25,6 +25,7 @@ use crate::{ attachment::{AttachmentDB, AttachmentResponse}, cipher::{Cipher, CipherDBModel}, }, + BaseUrl, }; const ATTACHMENTS_BUCKET: &str = "ATTACHMENTS_BUCKET"; @@ -104,6 +105,7 @@ async fn touch_cipher_updated_at(db: &D1Database, cipher_id: &str) -> Result<(), pub async fn create_attachment_v2( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Path(cipher_id): Path, Json(payload): Json, ) -> Result, AppError> { @@ -158,7 +160,7 @@ pub async fn create_attachment_v2( // Return upload URL pointing to local upload endpoint let url = upload_url(&cipher_id, &attachment_id); let mut cipher_response: Cipher = cipher.into(); - hydrate_cipher_attachments(&db, &env, &mut cipher_response, &claims.sub).await?; + hydrate_cipher_attachments(&db, &env, &base_url, &mut cipher_response, &claims.sub).await?; touch_cipher_updated_at(&db, &cipher_id).await?; db::touch_user_updated_at(&db, &claims.sub).await?; @@ -253,6 +255,7 @@ pub async fn upload_attachment_v2_data( pub async fn upload_attachment_legacy( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Path(cipher_id): Path, mut multipart: Multipart, ) -> Result, AppError> { @@ -309,7 +312,7 @@ pub async fn upload_attachment_legacy( // 返回最新的 Cipher(含附件) let mut cipher_response: Cipher = cipher.into(); - hydrate_cipher_attachments(&db, &env, &mut cipher_response, &claims.sub).await?; + hydrate_cipher_attachments(&db, &env, &base_url, &mut cipher_response, &claims.sub).await?; Ok(Json(cipher_response)) } @@ -319,6 +322,7 @@ pub async fn upload_attachment_legacy( pub async fn get_attachment( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Path((cipher_id, attachment_id)): Path<(String, String)>, ) -> Result, AppError> { let _bucket = require_bucket(&env)?; @@ -333,7 +337,7 @@ pub async fn get_attachment( )); } - let url = download_url(&env, &cipher_id, &attachment_id, &claims.sub)?; + let url = download_url(&env, &base_url, &cipher_id, &attachment_id, &claims.sub)?; Ok(Json(attachment.to_response(url))) } @@ -449,6 +453,7 @@ pub async fn download_attachment( pub async fn hydrate_cipher_attachments( db: &D1Database, env: &Env, + base_url: &str, cipher: &mut Cipher, user_id: &str, ) -> Result<(), AppError> { @@ -457,7 +462,7 @@ pub async fn hydrate_cipher_attachments( return Ok(()); } - let mut map = load_attachment_map(db, env, &[cipher.id.clone()], user_id).await?; + let mut map = load_attachment_map(db, env, base_url, &[cipher.id.clone()], user_id).await?; if let Some(list) = map.remove(&cipher.id) { if !list.is_empty() { cipher.attachments = Some(list); @@ -470,6 +475,7 @@ pub async fn hydrate_cipher_attachments( pub async fn hydrate_ciphers_attachments( db: &D1Database, env: &Env, + base_url: &str, ciphers: &mut [Cipher], user_id: &str, ) -> Result<(), AppError> { @@ -481,7 +487,7 @@ pub async fn hydrate_ciphers_attachments( } let ids: Vec = ciphers.iter().map(|c| c.id.clone()).collect(); - let mut map = load_attachment_map(db, env, &ids, user_id).await?; + let mut map = load_attachment_map(db, env, base_url, &ids, user_id).await?; for cipher in ciphers.iter_mut() { if let Some(list) = map.remove(&cipher.id) { @@ -509,13 +515,15 @@ fn upload_url(cipher_id: &str, attachment_id: &str) -> String { fn download_url( env: &Env, + base_url: &str, cipher_id: &str, attachment_id: &str, user_id: &str, ) -> Result { let token = build_download_token(env, user_id, cipher_id, attachment_id)?; + let normalized_base = base_url.trim_end_matches('/'); Ok(format!( - "/api/ciphers/{cipher_id}/attachment/{attachment_id}/download?token={token}" + "{normalized_base}/api/ciphers/{cipher_id}/attachment/{attachment_id}/download?token={token}" )) } @@ -562,6 +570,7 @@ async fn fetch_attachment(db: &D1Database, attachment_id: &str) -> Result Result>, AppError> { @@ -583,7 +592,13 @@ async fn load_attachment_map( let mut map: HashMap> = HashMap::new(); for attachment in attachments { - let url = download_url(env, &attachment.cipher_id, &attachment.id, user_id)?; + let url = download_url( + env, + base_url, + &attachment.cipher_id, + &attachment.id, + user_id, + )?; map.entry(attachment.cipher_id.clone()) .or_default() .push(attachment.to_response(url)); diff --git a/src/handlers/ciphers.rs b/src/handlers/ciphers.rs index 5d650d5..74c76db 100644 --- a/src/handlers/ciphers.rs +++ b/src/handlers/ciphers.rs @@ -1,5 +1,5 @@ use super::get_batch_size; -use axum::{extract::State, Json}; +use axum::{extract::State, Extension, Json}; use chrono::{DateTime, Utc}; use log; // Used for warning logs on parse failures use serde::Deserialize; @@ -17,6 +17,7 @@ use crate::models::cipher::{ MoveCipherData, PartialCipherData, }; use crate::models::user::{PasswordOrOtpData, User}; +use crate::BaseUrl; use axum::extract::Path; /// Helper to fetch a cipher by id for a user or return NotFound. @@ -37,6 +38,7 @@ async fn fetch_cipher_for_user( pub async fn create_cipher( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Json(payload): Json, ) -> Result, AppError> { let db = db::get_db(&env)?; @@ -95,7 +97,8 @@ pub async fn create_cipher( .run() .await?; - attachments::hydrate_cipher_attachments(&db, env.as_ref(), &mut cipher, &claims.sub).await?; + attachments::hydrate_cipher_attachments(&db, env.as_ref(), &base_url, &mut cipher, &claims.sub) + .await?; db::touch_user_updated_at(&db, &claims.sub).await?; Ok(Json(cipher)) @@ -105,6 +108,7 @@ pub async fn create_cipher( pub async fn update_cipher( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Path(id): Path, Json(payload): Json, ) -> Result, AppError> { @@ -206,7 +210,8 @@ pub async fn update_cipher( .run() .await?; - attachments::hydrate_cipher_attachments(&db, env.as_ref(), &mut cipher, &claims.sub).await?; + attachments::hydrate_cipher_attachments(&db, env.as_ref(), &base_url, &mut cipher, &claims.sub) + .await?; db::touch_user_updated_at(&db, &claims.sub).await?; Ok(Json(cipher)) @@ -217,6 +222,7 @@ pub async fn update_cipher( pub async fn list_ciphers( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, ) -> Result, AppError> { let db = db::get_db(&env)?; @@ -233,7 +239,14 @@ pub async fn list_ciphers( let mut ciphers: Vec = ciphers_db.into_iter().map(|c| c.into()).collect(); - attachments::hydrate_ciphers_attachments(&db, env.as_ref(), &mut ciphers, &claims.sub).await?; + attachments::hydrate_ciphers_attachments( + &db, + env.as_ref(), + &base_url, + &mut ciphers, + &claims.sub, + ) + .await?; Ok(Json(CipherListResponse { data: ciphers, @@ -247,13 +260,15 @@ pub async fn list_ciphers( pub async fn get_cipher( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Path(id): Path, ) -> Result, AppError> { let db = db::get_db(&env)?; let cipher = fetch_cipher_for_user(&db, &id, &claims.sub).await?; let mut cipher: Cipher = cipher.into(); - attachments::hydrate_cipher_attachments(&db, env.as_ref(), &mut cipher, &claims.sub).await?; + attachments::hydrate_cipher_attachments(&db, env.as_ref(), &base_url, &mut cipher, &claims.sub) + .await?; Ok(Json(cipher)) } @@ -263,9 +278,10 @@ pub async fn get_cipher( pub async fn get_cipher_details( claims: Claims, state: State>, + extension: Extension, id: Path, ) -> Result, AppError> { - get_cipher(claims, state, id).await + get_cipher(claims, state, extension, id).await } /// PUT/POST /api/ciphers/{id}/partial @@ -273,6 +289,7 @@ pub async fn get_cipher_details( pub async fn update_cipher_partial( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Path(id): Path, Json(payload): Json, ) -> Result, AppError> { @@ -317,7 +334,8 @@ pub async fn update_cipher_partial( let cipher = fetch_cipher_for_user(&db, &id, user_id).await?; let mut cipher: Cipher = cipher.into(); - attachments::hydrate_cipher_attachments(&db, env.as_ref(), &mut cipher, &claims.sub).await?; + attachments::hydrate_cipher_attachments(&db, env.as_ref(), &base_url, &mut cipher, &claims.sub) + .await?; Ok(Json(cipher)) } @@ -449,6 +467,7 @@ pub async fn hard_delete_ciphers_bulk( pub async fn restore_cipher( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Path(id): Path, ) -> Result, AppError> { let db = db::get_db(&env)?; @@ -479,7 +498,8 @@ pub async fn restore_cipher( .ok_or(AppError::NotFound("Cipher not found".to_string()))?; let mut cipher: Cipher = cipher_db.into(); - attachments::hydrate_cipher_attachments(&db, env.as_ref(), &mut cipher, &claims.sub).await?; + attachments::hydrate_cipher_attachments(&db, env.as_ref(), &base_url, &mut cipher, &claims.sub) + .await?; db::touch_user_updated_at(&db, &claims.sub).await?; @@ -500,6 +520,7 @@ pub struct BulkRestoreResponse { pub async fn restore_ciphers_bulk( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Json(payload): Json, ) -> Result, AppError> { let db = db::get_db(&env)?; @@ -546,8 +567,14 @@ pub async fn restore_ciphers_bulk( .map(|cipher| cipher.into()) .collect(); - attachments::hydrate_ciphers_attachments(&db, env.as_ref(), &mut restored_ciphers, &claims.sub) - .await?; + attachments::hydrate_ciphers_attachments( + &db, + env.as_ref(), + &base_url, + &mut restored_ciphers, + &claims.sub, + ) + .await?; db::touch_user_updated_at(&db, &claims.sub).await?; @@ -565,6 +592,7 @@ pub async fn restore_ciphers_bulk( pub async fn create_cipher_simple( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, Json(payload): Json, ) -> Result, AppError> { let db = db::get_db(&env)?; @@ -617,7 +645,8 @@ pub async fn create_cipher_simple( .run() .await?; - attachments::hydrate_cipher_attachments(&db, env.as_ref(), &mut cipher, &claims.sub).await?; + attachments::hydrate_cipher_attachments(&db, env.as_ref(), &base_url, &mut cipher, &claims.sub) + .await?; db::touch_user_updated_at(&db, &claims.sub).await?; Ok(Json(cipher)) diff --git a/src/handlers/sync.rs b/src/handlers/sync.rs index 64cf8a8..4f8369d 100644 --- a/src/handlers/sync.rs +++ b/src/handlers/sync.rs @@ -1,4 +1,4 @@ -use axum::{extract::State, Json}; +use axum::{extract::State, Extension, Json}; use serde_json::Value; use std::sync::Arc; use worker::Env; @@ -14,12 +14,14 @@ use crate::{ sync::{Profile, SyncResponse}, user::User, }, + BaseUrl, }; #[worker::send] pub async fn get_sync_data( claims: Claims, State(env): State>, + Extension(BaseUrl(base_url)): Extension, ) -> Result, AppError> { let user_id = claims.sub; let db = db::get_db(&env)?; @@ -65,7 +67,8 @@ pub async fn get_sync_data( .collect::>(); let mut ciphers = ciphers; - attachments::hydrate_ciphers_attachments(&db, env.as_ref(), &mut ciphers, &user_id).await?; + attachments::hydrate_ciphers_attachments(&db, env.as_ref(), &base_url, &mut ciphers, &user_id) + .await?; let profile = Profile::from_user(user)?;