This commit is contained in:
2025-10-12 14:07:10 +02:00
parent 797a641263
commit 6724634870
4 changed files with 94 additions and 10 deletions
+15
View File
@@ -11,6 +11,7 @@ RUN apt-get update \
libpq-dev \
libjpeg-dev \
libpng-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
COPY Cargo.toml Cargo.lock ./
@@ -28,11 +29,25 @@ WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
libssl3 \
libpq5 \
libjpeg62-turbo \
libpng16-16 \
ocrmypdf \
tesseract-ocr \
ghostscript \
qpdf \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /usr/local/lib \
&& curl -fsSL https://github.com/bblanchon/pdfium-binaries/releases/latest/download/pdfium-linux-arm64.tgz -o /tmp/pdfium.tgz \
&& mkdir -p /tmp/pdfium \
&& tar -xzf /tmp/pdfium.tgz -C /tmp/pdfium --strip-components=1 \
&& pdfium_so="$(find /tmp/pdfium -name libpdfium.so -type f | head -n1)" \
&& [ -n "${pdfium_so}" ] \
&& mv "${pdfium_so}" /usr/local/lib/libpdfium.so \
&& ldconfig \
&& rm -rf /tmp/pdfium.tgz /tmp/pdfium \
&& useradd --system --create-home --uid 10001 appuser
COPY --from=builder /app/target/release/backend /usr/local/bin/papercrate-backend
+69
View File
@@ -145,6 +145,11 @@ pub struct BulkMoveRequest {
pub folder_id: Option<Uuid>,
}
#[derive(Deserialize)]
pub struct UpdateDocumentRequest {
pub title: Option<String>,
}
#[derive(Serialize)]
pub struct BulkMoveResponse {
pub updated: usize,
@@ -627,6 +632,70 @@ pub async fn delete_document(
Ok(StatusCode::NO_CONTENT)
}
pub async fn update_document(
State(state): State<AppState>,
Path(document_id): Path<Uuid>,
user: AuthenticatedUser,
Json(payload): Json<UpdateDocumentRequest>,
) -> AppResult<Json<DocumentDetailResponse>> {
let mut conn = state.db()?;
let mut document: Document = documents::table.find(document_id).first(&mut conn)?;
if document.deleted_at.is_some() {
return Err(AppError::not_found());
}
let new_title = match payload.title {
Some(ref title) => {
let trimmed = title.trim();
if trimmed.is_empty() {
return Err(AppError::bad_request("title must not be empty"));
}
Some(trimmed.to_string())
}
None => None,
};
if new_title.is_none() {
return Err(AppError::bad_request("no changes provided"));
}
if let Some(title) = new_title {
let now = Utc::now().naive_utc();
diesel::update(documents::table.find(document_id))
.set((documents::title.eq(title), documents::updated_at.eq(now)))
.execute(&mut conn)?;
document = documents::table.find(document_id).first(&mut conn)?;
}
let current_version: DocumentVersion = document_versions::table
.filter(document_versions::document_id.eq(document_id))
.filter(document_versions::version_number.eq(document.current_version))
.first(&mut conn)?;
let tags_map = load_tags_for_documents(&mut conn, &[document_id])?;
let version_id = current_version.id;
drop(conn);
let assets = load_asset_responses(&state, version_id).await?;
let thumbnail = assets
.iter()
.find(|asset| asset.asset_type == "thumbnail")
.cloned();
Ok(Json(DocumentDetailResponse {
document: to_document_response(
&state,
user.user_id,
document,
tags_map.get(&document_id).cloned(),
thumbnail,
)?,
current_version: to_version_response(current_version),
assets,
}))
}
pub async fn move_document(
State(state): State<AppState>,
Path(document_id): Path<Uuid>,
+3 -1
View File
@@ -64,7 +64,9 @@ pub fn create_router(state: AppState) -> Router<()> {
)
.route(
"/:id",
get(documents::get_document).delete(documents::delete_document),
get(documents::get_document)
.delete(documents::delete_document)
.patch(documents::update_document),
)
.route("/:id/download", get(documents::download_document))
.route(
+7 -9
View File
@@ -8,12 +8,6 @@ use async_trait::async_trait;
use axum::body::Body;
use axum::http::{Method, Request, StatusCode};
use axum::Router;
use diesel::connection::SimpleConnection;
use diesel::prelude::*;
use diesel::PgConnection;
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use http_body_util::BodyExt;
use once_cell::sync::Lazy;
use backend::auth::jwt::JwtService;
use backend::config::AppConfig;
use backend::db::{self, PgPool};
@@ -21,6 +15,12 @@ use backend::models::{Job, NewUser};
use backend::routes;
use backend::state::AppState;
use backend::storage::ObjectStorage;
use diesel::connection::SimpleConnection;
use diesel::prelude::*;
use diesel::PgConnection;
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use http_body_util::BodyExt;
use once_cell::sync::Lazy;
use rand::rngs::OsRng;
use serde::Serialize;
use tokio::sync::Mutex;
@@ -238,9 +238,7 @@ impl TestApp {
pub async fn jobs_by_type(&self, ty: &str) -> Result<Vec<Job>> {
let ty = ty.to_string();
self.with_conn(move |conn| {
use backend::schema::jobs::dsl::{
job_type as job_type_col, jobs as jobs_table,
};
use backend::schema::jobs::dsl::{job_type as job_type_col, jobs as jobs_table};
let rows = jobs_table
.filter(job_type_col.eq(&ty))
.load::<Job>(conn)