backend
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user