Deleted incoming host logic
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
.git
|
||||
.postgres
|
||||
postgres
|
||||
incoming
|
||||
backend/uploads
|
||||
backend/logs
|
||||
backend/.venv
|
||||
|
||||
@@ -11,6 +11,3 @@ POSTGRES_DB=mail_summary
|
||||
# Windows: ./backend/uploads
|
||||
# Linux (Synology): /volume1/09_container_manager/13_mail_summary/backend/uploads
|
||||
UPLOAD_HOST=./backend/uploads
|
||||
|
||||
# Host folder mounted into backend as /app/incoming (for multi-GB zips).
|
||||
INCOMING_HOST=C:/Users/Administrator/Downloads/20260719
|
||||
|
||||
@@ -19,6 +19,3 @@
|
||||
|
||||
# postgres
|
||||
/postgres
|
||||
|
||||
# large mailbox drops (optional local folder)
|
||||
/incoming
|
||||
|
||||
@@ -10,4 +10,3 @@ LLM_MAX_MAILS=80
|
||||
LLM_BODY_CHARS=3000
|
||||
|
||||
UPLOAD_DIR=/app/uploads
|
||||
INCOMING_DIR=/app/incoming
|
||||
|
||||
+1
-54
@@ -16,7 +16,7 @@ from fastapi import FastAPI, File, Form, HTTPException, UploadFile
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from config import CORS_ORIGINS, INCOMING_DIR, LLM_MODEL, UPLOAD_DIR
|
||||
from config import CORS_ORIGINS, LLM_MODEL, UPLOAD_DIR
|
||||
from db.pool import close_pool, init_pool, pool_enabled
|
||||
from job_repository import create_job, ensure_tables, get_job, get_report, list_jobs
|
||||
from llm_client import ping_llm
|
||||
@@ -35,7 +35,6 @@ STAGING_DIR = UPLOAD_DIR / "staging"
|
||||
async def lifespan(app: FastAPI):
|
||||
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
STAGING_DIR.mkdir(parents=True, exist_ok=True)
|
||||
INCOMING_DIR.mkdir(parents=True, exist_ok=True)
|
||||
await init_pool()
|
||||
await ensure_tables()
|
||||
yield
|
||||
@@ -69,7 +68,6 @@ class UploadResponse(BaseModel):
|
||||
mailbox: str
|
||||
filename: str
|
||||
size_bytes: int
|
||||
source: str # upload | incoming
|
||||
|
||||
|
||||
def _staging_path(upload_id: str) -> Path:
|
||||
@@ -91,29 +89,6 @@ def _read_staging_meta(upload_id: str) -> dict:
|
||||
return json.loads(meta_path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def _attach_source(src: Path, stage_dir: Path, mailbox: str, filename: str) -> Path:
|
||||
dest = stage_dir / f"{mailbox}_{filename}"
|
||||
if dest.exists() or dest.is_symlink():
|
||||
dest.unlink()
|
||||
try:
|
||||
dest.symlink_to(src)
|
||||
except OSError:
|
||||
shutil.copy2(src, dest)
|
||||
return dest
|
||||
|
||||
|
||||
def _find_incoming_match(filename: str, size_bytes: int) -> Path | None:
|
||||
candidate = INCOMING_DIR / Path(filename).name
|
||||
if not candidate.is_file():
|
||||
return None
|
||||
try:
|
||||
if candidate.stat().st_size != size_bytes:
|
||||
return None
|
||||
except OSError:
|
||||
return None
|
||||
return candidate.resolve()
|
||||
|
||||
|
||||
def _attach_staging_to_job(upload_id: str, job_dir: Path, mailbox: str) -> str:
|
||||
"""Copy/link staged zip into a job dir, keeping staging for re-analysis."""
|
||||
meta = _read_staging_meta(upload_id)
|
||||
@@ -147,33 +122,6 @@ async def health() -> HealthResponse:
|
||||
)
|
||||
|
||||
|
||||
@app.post("/api/uploads/resolve", response_model=UploadResponse)
|
||||
async def api_resolve_upload(
|
||||
mailbox: Mailbox = Form(...),
|
||||
filename: str = Form(...),
|
||||
size_bytes: int = Form(...),
|
||||
) -> UploadResponse:
|
||||
"""If the selected file already exists on the incoming mount, stage it without re-upload."""
|
||||
match = _find_incoming_match(filename, size_bytes)
|
||||
if not match:
|
||||
raise HTTPException(status_code=404, detail="incoming_match_not_found")
|
||||
|
||||
upload_id = uuid.uuid4().hex
|
||||
stage_dir = _staging_path(upload_id)
|
||||
stage_dir.mkdir(parents=True, exist_ok=True)
|
||||
safe_name = Path(filename).name
|
||||
_attach_source(match, stage_dir, mailbox, safe_name)
|
||||
meta = {
|
||||
"upload_id": upload_id,
|
||||
"mailbox": mailbox,
|
||||
"filename": safe_name,
|
||||
"size_bytes": size_bytes,
|
||||
"source": "incoming",
|
||||
}
|
||||
_write_staging_meta(stage_dir, meta)
|
||||
return UploadResponse(**meta)
|
||||
|
||||
|
||||
@app.post("/api/uploads", response_model=UploadResponse)
|
||||
async def api_upload_zip(
|
||||
mailbox: Mailbox = Form(...),
|
||||
@@ -207,7 +155,6 @@ async def api_upload_zip(
|
||||
"mailbox": mailbox,
|
||||
"filename": safe_name,
|
||||
"size_bytes": size,
|
||||
"source": "upload",
|
||||
}
|
||||
_write_staging_meta(stage_dir, meta)
|
||||
return UploadResponse(**meta)
|
||||
|
||||
@@ -34,6 +34,3 @@ LLM_BODY_CHARS = int(os.getenv("LLM_BODY_CHARS", "3000"))
|
||||
|
||||
UPLOAD_DIR = Path(os.getenv("UPLOAD_DIR", str(BASE_DIR / "uploads"))).resolve()
|
||||
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
INCOMING_DIR = Path(os.getenv("INCOMING_DIR", str(BASE_DIR.parent / "incoming"))).resolve()
|
||||
INCOMING_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -37,8 +37,6 @@ services:
|
||||
- ./backend/logs:/app/logs
|
||||
# Host upload dir (Windows: ./backend/uploads, Linux Synology: set UPLOAD_HOST in .env).
|
||||
- ${UPLOAD_HOST:-./backend/uploads}:/app/uploads
|
||||
# Drop multi-GB mailbox zips here (or point INCOMING_HOST at another folder).
|
||||
- ${INCOMING_HOST:-./incoming}:/app/incoming:ro
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
+2
-38
@@ -28,10 +28,6 @@ async function request(path, options = {}) {
|
||||
return data;
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function getHealth() {
|
||||
return request("/api/health");
|
||||
}
|
||||
@@ -51,25 +47,6 @@ export function createJob(formData) {
|
||||
});
|
||||
}
|
||||
|
||||
/** Try to stage a file that already exists on the server incoming mount. */
|
||||
export async function resolveUpload(mailbox, filename, sizeBytes) {
|
||||
const form = new FormData();
|
||||
form.append("mailbox", mailbox);
|
||||
form.append("filename", filename);
|
||||
form.append("size_bytes", String(sizeBytes));
|
||||
const response = await fetch(`${API_BASE}/api/uploads/resolve`, {
|
||||
method: "POST",
|
||||
body: form,
|
||||
});
|
||||
if (response.status === 404) return null;
|
||||
const text = await response.text();
|
||||
const data = text ? JSON.parse(text) : {};
|
||||
if (!response.ok) {
|
||||
throw new Error(data.detail || "resolve_failed");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Upload zip with progress via XHR. */
|
||||
export function uploadZip(file, mailbox, onProgress) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -109,21 +86,8 @@ export function uploadZip(file, mailbox, onProgress) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stage a mailbox zip:
|
||||
* 1) resolve against server incoming mount (no re-upload)
|
||||
* 2) otherwise upload with real progress
|
||||
*/
|
||||
export async function stageMailboxZip(file, mailbox, onProgress) {
|
||||
onProgress?.(2);
|
||||
const resolved = await resolveUpload(mailbox, file.name, file.size);
|
||||
if (resolved) {
|
||||
for (const pct of [25, 55, 80, 100]) {
|
||||
onProgress?.(pct);
|
||||
await sleep(70);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
/** Stage a mailbox zip by uploading it to the server. */
|
||||
export function stageMailboxZip(file, mailbox, onProgress) {
|
||||
return uploadZip(file, mailbox, onProgress);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user