fix: generate full url for attachment download
This commit is contained in:
parent
28c791337a
commit
4930e747ed
3 changed files with 68 additions and 21 deletions
|
|
@ -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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Path(cipher_id): Path<String>,
|
||||
Json(payload): Json<AttachmentCreateRequest>,
|
||||
) -> Result<Json<AttachmentUploadResponse>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Path(cipher_id): Path<String>,
|
||||
mut multipart: Multipart,
|
||||
) -> Result<Json<Cipher>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Path((cipher_id, attachment_id)): Path<(String, String)>,
|
||||
) -> Result<Json<AttachmentResponse>, 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<String> = 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<String, AppError> {
|
||||
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<Attach
|
|||
async fn load_attachment_map(
|
||||
db: &D1Database,
|
||||
env: &Env,
|
||||
base_url: &str,
|
||||
cipher_ids: &[String],
|
||||
user_id: &str,
|
||||
) -> Result<HashMap<String, Vec<AttachmentResponse>>, AppError> {
|
||||
|
|
@ -583,7 +592,13 @@ async fn load_attachment_map(
|
|||
let mut map: HashMap<String, Vec<AttachmentResponse>> = 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));
|
||||
|
|
|
|||
|
|
@ -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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Json(payload): Json<CreateCipherRequest>,
|
||||
) -> Result<Json<Cipher>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Path(id): Path<String>,
|
||||
Json(payload): Json<CipherRequestData>,
|
||||
) -> Result<Json<Cipher>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
) -> Result<Json<CipherListResponse>, AppError> {
|
||||
let db = db::get_db(&env)?;
|
||||
|
||||
|
|
@ -233,7 +239,14 @@ pub async fn list_ciphers(
|
|||
|
||||
let mut ciphers: Vec<Cipher> = 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<Cipher>, 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<Arc<Env>>,
|
||||
extension: Extension<BaseUrl>,
|
||||
id: Path<String>,
|
||||
) -> Result<Json<Cipher>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Path(id): Path<String>,
|
||||
Json(payload): Json<PartialCipherData>,
|
||||
) -> Result<Json<Cipher>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<Cipher>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Json(payload): Json<CipherIdsData>,
|
||||
) -> Result<Json<BulkRestoreResponse>, 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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
Json(payload): Json<CipherRequestData>,
|
||||
) -> Result<Json<Cipher>, 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))
|
||||
|
|
|
|||
|
|
@ -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<Arc<Env>>,
|
||||
Extension(BaseUrl(base_url)): Extension<BaseUrl>,
|
||||
) -> Result<Json<SyncResponse>, AppError> {
|
||||
let user_id = claims.sub;
|
||||
let db = db::get_db(&env)?;
|
||||
|
|
@ -65,7 +67,8 @@ pub async fn get_sync_data(
|
|||
.collect::<Vec<Cipher>>();
|
||||
|
||||
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)?;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue