diff --git a/packetserver/http/config.py b/packetserver/http/config.py index 2e542cc..0eb8ff0 100644 --- a/packetserver/http/config.py +++ b/packetserver/http/config.py @@ -6,7 +6,7 @@ class Settings(BaseSettings): """ # Define your settings fields with type hints and optional default values name: str = "PacketServer" - zeo_file: str + zeo_file: str = "" operator: str | None = None debug_mode: bool = False log_level: str = "info" diff --git a/packetserver/http/routers/jobs.py b/packetserver/http/routers/jobs.py index 21f12db..dd1220b 100644 --- a/packetserver/http/routers/jobs.py +++ b/packetserver/http/routers/jobs.py @@ -16,7 +16,9 @@ from packetserver.http.database import DbDependency from packetserver.server.jobs import Job, JobStatus from packetserver.http.server import templates from packetserver.server.db import get_user_db_json -from packetserver.server.jobs import RunnerFile +from packetserver.server.jobs import RunnerFile, add_object_to_file_list +from packetserver.server.objects import Object +from packetserver.server.users import User router = APIRouter(prefix="/api/v1", tags=["jobs"]) dashboard_router = APIRouter(tags=["jobs"]) @@ -54,6 +56,7 @@ class JobCreate(BaseModel): cmd: Union[str, List[str]] env: Optional[Dict[str, str]] = None files: Optional[Dict[str, str]] = None + objs: Optional[List[str]] = None @router.get("/jobs", response_model=List[JobSummary]) async def list_user_jobs( @@ -173,8 +176,10 @@ async def create_job_from_form( files: List[UploadFile] = File(default=[]), include_db: Optional[str] = Form(None), shell_mode: Optional[str] = Form(None), + objs: Optional[str] = Form(None), current_user: HttpUser = Depends(get_current_http_user) ): + logging.debug("new job form post received") # Build env dict from parallel lists env = {} for k, v in zip(env_keys, env_values): @@ -203,20 +208,26 @@ async def create_job_from_form( # get_user_db_json needs the raw ZODB.DB instance user_db_bytes = get_user_db_json(username_lower,db) # Base64-encode for payload consistency - b64_db = base64.b64encode(user_db_bytes).decode('ascii') + b64_db = base64.b64encode(user_db_bytes).decode('utf-8') + logging.debug(f"DB base64: f{b64_db}") files_dict["user-db.json.gz"] = b64_db logging.debug(f"Injected user-db.json.gz for {current_user.username}") except Exception as e: logging.error(f"Failed to generate user-db.json.gz: {e}") raise HTTPException(status_code=500, detail="Failed to include user database") + objs_list = None + if objs: + objs_list = [uuid_str.strip() for uuid_str in objs.split(",") if uuid_str.strip()] + # Prepare payload for the existing API payload = { "cmd": cmd_args, "env": env if env else None, - "files": files_dict if files_dict else None + "files": files_dict if files_dict else None, + "objs": objs_list, } - + logging.debug("Calling internal API to create the job") # Call the API internally response = await create_job( payload=JobCreate(**{k: v for k, v in payload.items() if v is not None}), @@ -225,6 +236,7 @@ async def create_job_from_form( ) # Redirect to the new job detail page + logging.debug("Job queued") return RedirectResponse(url=f"/jobs/{response.id}", status_code=303) @dashboard_router.get("/jobs/{jid}", response_class=HTMLResponse) @@ -252,7 +264,7 @@ async def create_job( current_user: HttpUser = Depends(get_current_http_user) ): username = current_user.username.upper().strip() - + logging.debug(f"New job create function called for '{username}'") try: # Process files: convert base64 dict to list of RunnerFile runner_files = [] @@ -263,8 +275,26 @@ async def create_job( runner_files.append(RunnerFile(filename, data=data_bytes)) except Exception as e: raise HTTPException(status_code=400, detail=f"Invalid base64 for file {filename}") - + # Handle attached objects by UUID + logging.debug("handling objects") + if payload.objs: + logging.debug("Adding objects to files list") + for obj_uuid_str in payload.objs: + try: + with db.transaction() as conn: + add_object_to_file_list(obj_uuid_str, runner_files, username, conn) + except KeyError as ke: + raise HTTPException(status_code=404, detail=f"Object not found: {str(ke)}") + except Exception as exc: # Catches permission issues or invalid UUID + raise HTTPException(status_code=403 if "private" in str(exc).lower() else 400, + detail=f"Cannot attach object {obj_uuid_str}: {str(exc)}") + logging.debug("Creating job instance now") # Create the Job instance + if type(payload.cmd) is str: + payload.cmd = payload.cmd.replace('\r', '') + else: + for i in range(0,len(payload.cmd)): + payload.cmd[i] = payload.cmd[i].replace('\r', '') new_job = Job( cmd=payload.cmd, owner=username, @@ -277,7 +307,7 @@ async def create_job( new_jid = new_job.queue(root) logging.info(f"User {username} queued job {new_jid}: {payload.cmd} with {len(runner_files)} files") - + logging.debug("New job created.") return JobSummary( id=new_jid, cmd=new_job.cmd, diff --git a/packetserver/http/routers/objects.py b/packetserver/http/routers/objects.py index 55bc14b..530d38a 100644 --- a/packetserver/http/routers/objects.py +++ b/packetserver/http/routers/objects.py @@ -91,7 +91,7 @@ async def upload_object( if force_text: try: text_content = content.decode('utf-8', errors='strict') - object_data = text_content # str → will set binary=False + object_data = text_content.replace('\r', '') # str → will set binary=False except UnicodeDecodeError: raise HTTPException(status_code=400, detail="Content is not valid UTF-8 and cannot be forced as text") else: @@ -169,6 +169,7 @@ async def create_text_object( # Validate text if not text: raise HTTPException(status_code=400, detail="Text content cannot be empty") + text = text.replace("\r", "") # Normalize name (optional, default like original) obj_name = (name or "text_object.txt").strip() diff --git a/packetserver/http/routers/objects_html.py b/packetserver/http/routers/objects_html.py index 577224c..4124f82 100644 --- a/packetserver/http/routers/objects_html.py +++ b/packetserver/http/routers/objects_html.py @@ -73,6 +73,7 @@ async def update_object( payload["private"] = (private == "on") if new_text is not None and new_text.strip(): + new_text = new_text.replace("\r","") payload["data_text"] = new_text.strip() elif new_file and new_file.filename: content = await new_file.read() diff --git a/packetserver/http/templates/job_new.html b/packetserver/http/templates/job_new.html index f8e0901..f77228e 100644 --- a/packetserver/http/templates/job_new.html +++ b/packetserver/http/templates/job_new.html @@ -41,6 +41,11 @@