some changes

This commit is contained in:
Mateusz Gruszczyński
2026-07-30 23:57:35 +02:00
parent c5ca8e8d0b
commit 4edc511272
20 changed files with 686 additions and 65 deletions
+88 -9
View File
@@ -99,7 +99,11 @@ pub async fn upload_pad_file(
stored = format!("{stem}-{}{}", db::random_suffix(6), ext);
key = crate::storage::object_key("pads", pad.id, &file_token, &stored);
}
let url = format!("/f/{}/{}", file_token, stored);
let stored_url = crate::file_urls::stored_file_path(&file_token, &stored);
let public_url = crate::file_urls::public_file_url(
state.files_public_url.as_deref(),
&stored_url,
);
let mime = mime_guess::from_path(&stored)
.first_or_octet_stream()
.to_string();
@@ -109,8 +113,16 @@ pub async fn upload_pad_file(
.put(&key, bytes.clone().into(), &mime, &cache_control)
.await
.map_err(|_| ApiError::internal("Failed to save the file"))?;
db::register_pad_file(&state.db, pad.id, &stored, &url, &mime, bytes.len() as i64).await?;
Ok(Json(serde_json::json!({"name": stored, "url": url, "mime_type": mime})))
db::register_pad_file(
&state.db,
pad.id,
&stored,
&stored_url,
&mime,
bytes.len() as i64,
)
.await?;
Ok(Json(serde_json::json!({"name": stored, "url": public_url, "mime_type": mime})))
}
pub(super) fn content_references_file(content: &str, filename: &str, url: &str) -> bool {
@@ -136,6 +148,26 @@ pub(super) fn content_references_file(content: &str, filename: &str, url: &str)
false
}
pub(super) fn content_references_stored_file(
content: &str,
filename: &str,
stored_url: &str,
public_base: Option<&str>,
) -> bool {
if content_references_file(content, filename, stored_url) {
return true;
}
let canonical = crate::file_urls::canonical_file_path(stored_url);
if canonical
.as_deref()
.is_some_and(|url| url != stored_url && content.contains(url))
{
return true;
}
let public_url = crate::file_urls::public_file_url(public_base, stored_url);
public_url != stored_url && content.contains(&public_url)
}
pub async fn pad_files(
State(state): State<SharedState>,
headers: HeaderMap,
@@ -153,7 +185,12 @@ pub async fn pad_files(
.await?;
let mut files = db::list_pad_files(&state.db, pad.id).await?;
for file in &mut files {
let attached = content_references_file(&pad.content, &file.filename, &file.url);
let attached = content_references_stored_file(
&pad.content,
&file.filename,
&file.url,
state.files_public_url.as_deref(),
);
if attached != file.is_attached {
db::set_pad_file_attached(&state.db, file.id, attached).await?;
file.is_attached = attached;
@@ -164,6 +201,10 @@ pub async fn pad_files(
};
}
file.created_at = db::normalize_timestamp(&file.created_at);
file.url = crate::file_urls::public_file_url(
state.files_public_url.as_deref(),
&file.url,
);
}
Ok(Json(files))
}
@@ -298,7 +339,11 @@ pub async fn upload_note_file(
stored = format!("{stem}-{}{}", db::random_suffix(6), ext);
key = crate::storage::object_key("notes", note.id, &file_token, &stored);
}
let url = format!("/f/{}/{}", file_token, stored);
let stored_url = crate::file_urls::stored_file_path(&file_token, &stored);
let public_url = crate::file_urls::public_file_url(
state.files_public_url.as_deref(),
&stored_url,
);
let mime = mime_guess::from_path(&stored)
.first_or_octet_stream()
.to_string();
@@ -308,8 +353,16 @@ pub async fn upload_note_file(
.put(&key, bytes.clone().into(), &mime, &cache_control)
.await
.map_err(|_| ApiError::internal("Failed to save the file"))?;
db::register_note_file(&state.db, note.id, &stored, &url, &mime, bytes.len() as i64).await?;
Ok(Json(serde_json::json!({"name": stored, "url": url, "mime_type": mime})))
db::register_note_file(
&state.db,
note.id,
&stored,
&stored_url,
&mime,
bytes.len() as i64,
)
.await?;
Ok(Json(serde_json::json!({"name": stored, "url": public_url, "mime_type": mime})))
}
pub async fn delete_note(
@@ -382,7 +435,12 @@ pub async fn note_files(
.await?;
let mut files = db::list_note_files(&state.db, note.id).await?;
for file in &mut files {
let attached = content_references_file(&note.content, &file.filename, &file.url);
let attached = content_references_stored_file(
&note.content,
&file.filename,
&file.url,
state.files_public_url.as_deref(),
);
if attached != file.is_attached {
db::set_note_file_attached(&state.db, file.id, attached).await?;
file.is_attached = attached;
@@ -393,6 +451,10 @@ pub async fn note_files(
};
}
file.created_at = db::normalize_timestamp(&file.created_at);
file.url = crate::file_urls::public_file_url(
state.files_public_url.as_deref(),
&file.url,
);
}
Ok(Json(files))
}
@@ -580,7 +642,7 @@ fn sanitize_filename(value: &str) -> String {
#[cfg(test)]
mod tests {
use super::is_safe_inline_image_mime;
use super::{content_references_stored_file, is_safe_inline_image_mime};
#[test]
fn only_raster_images_are_inline() {
@@ -590,4 +652,21 @@ mod tests {
assert!(!is_safe_inline_image_mime("text/html"));
assert!(!is_safe_inline_image_mime("application/xml"));
}
#[test]
fn attachment_references_survive_origin_changes() {
let stored = "/f/token/image.png";
assert!(content_references_stored_file(
"![diagram](https://old-files.example.com/f/token/image.png)",
"image.png",
stored,
None,
));
assert!(content_references_stored_file(
"![diagram](/f/token/image.png)",
"image.png",
"https://old-files.example.com/f/token/image.png",
Some("https://new-files.example.com"),
));
}
}
+16 -12
View File
@@ -162,16 +162,6 @@ pub struct MarkdownFileReference {
mime_type: String,
}
impl From<db::NoteFile> for MarkdownFileReference {
fn from(file: db::NoteFile) -> Self {
Self {
filename: file.filename,
url: file.url,
mime_type: file.mime_type,
}
}
}
pub(crate) async fn markdown_file_references(
state: &SharedState,
pad_id: Option<i64>,
@@ -189,10 +179,24 @@ pub(crate) async fn markdown_file_references(
.into_iter()
.filter(|file| {
content
.map(|value| files::content_references_file(value, &file.filename, &file.url))
.map(|value| {
files::content_references_stored_file(
value,
&file.filename,
&file.url,
state.files_public_url.as_deref(),
)
})
.unwrap_or(true)
})
.map(Into::into)
.map(|file| MarkdownFileReference {
filename: file.filename,
url: crate::file_urls::public_file_url(
state.files_public_url.as_deref(),
&file.url,
),
mime_type: file.mime_type,
})
.collect())
}