Files
backend-local/src/local/print/create.rs

82 lines
3.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use super::models::*;
use axum::{extract::{Multipart, State}, http::StatusCode, Json};
use std::{path::Path, sync::atomic::Ordering};
use tempfile::NamedTempFile;
use tokio::fs;
use tracing::info;
pub async fn create_print(
State(store): State<JobStore>,
mut multipart: Multipart,
) -> Result<Json<JobView>, (StatusCode, String)> {
info!("📥 Получен запрос на печать");
let mut settings_list: Vec<Option<PrintSettings>> = Vec::new();
let mut files_data: Vec<(String, bytes::Bytes)> = Vec::new();
while let Some(field) = multipart.next_field().await.unwrap_or(None) {
let field_name = field.name().unwrap_or("").to_string();
if field_name == "settings" {
let parsed = match field.text().await {
Ok(text) => serde_json::from_str::<PrintSettings>(&text).ok(),
Err(_) => None,
};
settings_list.push(parsed);
} else if field_name == "files" {
let file_name = field.file_name().unwrap_or("unknown").to_string();
if let Ok(data) = field.bytes().await {
files_data.push((file_name, data));
}
}
}
if files_data.is_empty() {
return Err((StatusCode::BAD_REQUEST, "Нет файлов".into()));
}
let mut job_files: Vec<JobFile> = Vec::new();
for (i, (file_name, data)) in files_data.into_iter().enumerate() {
let ext = Path::new(&file_name).extension().and_then(|e| e.to_str()).unwrap_or("").to_lowercase();
let temp = if ext.is_empty() { NamedTempFile::new() } else { NamedTempFile::with_suffix(&format!(".{}", ext)) };
if let Ok(temp) = temp {
let path = temp.into_temp_path();
if fs::write(&*path, &data).await.is_ok() {
let settings = settings_list.get(i).cloned().flatten();
job_files.push(JobFile {
name: file_name,
path,
settings,
status: FileStatus::Queued,
error: None,
});
}
}
}
if job_files.is_empty() {
return Err((StatusCode::BAD_REQUEST, "Не удалось сохранить файлы".into()));
}
// ВСЕГДА начинаем с WaitingStart.
// 1. Если первый файл односторонний - он просто поедет печататься.
// 2. Если первый файл двусторонний - лоток и так пуст, очистка не нужна.
// Очистка (AwaitingClearOutput) будет запрошена ТОЛЬКО при переходе
// от предыдущего файла к следующему двустороннему в advance.rs
let initial_phase = JobPhase::WaitingStart;
let job_id = format!("job-{}", JOB_COUNTER.fetch_add(1, Ordering::SeqCst));
let job = Job {
id: job_id.clone(),
files: job_files,
current: Some(0),
phase: initial_phase,
cups_id: None,
duplex_first_pass_done: false,
};
let view = job.view();
store.lock().unwrap().insert(job_id.clone(), job);
Ok(Json(view))
}