diff --git a/packetserver/http/routers/jobs.py b/packetserver/http/routers/jobs.py index 679e02f..d336037 100644 --- a/packetserver/http/routers/jobs.py +++ b/packetserver/http/routers/jobs.py @@ -1,5 +1,5 @@ -from fastapi import APIRouter, Depends, HTTPException, Request -from fastapi.responses import HTMLResponse +from fastapi import APIRouter, Depends, HTTPException, Request, Form, UploadFile, File +from fastapi.responses import HTMLResponse, RedirectResponse from typing import List, Optional, Union, Tuple, Dict, Any from pydantic import BaseModel from datetime import datetime @@ -26,11 +26,13 @@ class JobSummary(BaseModel): finished_at: Optional[datetime] = None status: str # JobStatus.name return_code: int + env: Dict[str, str] = {} class JobDetail(JobSummary): output: str # base64-encoded errors: str # base64-encoded artifacts: List[Tuple[str, str]] # list of (filename, base64_data) + env: Dict[str, str] = {} from typing import Dict @@ -64,7 +66,8 @@ async def list_user_jobs( started_at=j.started_at, finished_at=j.finished_at, status=j.status.name, - return_code=j.return_code + return_code=j.return_code, + env=j.env )) except Exception as e: @@ -104,7 +107,8 @@ async def get_job_detail( return_code=job.return_code, output=job_dict["output"], errors=job_dict["errors"], - artifacts=job_dict["artifacts"] + artifacts=job_dict["artifacts"], + env=job_dict.get("env", {}) ) except HTTPException: @@ -200,4 +204,58 @@ async def create_job( raise except Exception as e: logging.error(f"Job creation failed for {username}: {e}\n{format_exc()}") - raise HTTPException(status_code=500, detail="Failed to queue job") \ No newline at end of file + raise HTTPException(status_code=500, detail="Failed to queue job") + +@dashboard_router.get("/jobs/new", response_class=HTMLResponse) +async def new_job_form( + request: Request, + current_user: HttpUser = Depends(get_current_http_user) +): + return templates.TemplateResponse( + "job_new.html", + { + "request": request, + "current_user": current_user.username + } + ) + +@dashboard_router.post("/jobs/new") +async def create_job_from_form( + db: DbDependency, + request: Request, + cmd: str = Form(...), + env_keys: List[str] = Form(default=[]), + env_values: List[str] = Form(default=[]), + files: List[UploadFile] = File(default=[]), + current_user: HttpUser = Depends(get_current_http_user) +): + # Build env dict from parallel lists + env = {} + for k, v in zip(env_keys, env_values): + if k.strip(): + env[k.strip()] = v.strip() + + # Build files dict for API (filename → base64) + files_dict = {} + for upload in files: + if upload.filename: + content = await upload.read() + files_dict[upload.filename] = base64.b64encode(content).decode('ascii') + + # Prepare payload for the existing API + payload = { + "cmd": [part.strip() for part in cmd.split() if part.strip()], # split on whitespace, like shell + "env": env if env else None, + "files": files_dict if files_dict else None + } + + # Call the API internally + from packetserver.http.routers.jobs import create_job as api_create_job + response = await api_create_job( + payload=JobCreate(**{k: v for k, v in payload.items() if v is not None}), + db=db, + current_user=current_user + ) + + # Redirect to the new job detail page + return RedirectResponse(url=f"/jobs/{response.id}", status_code=303) \ No newline at end of file diff --git a/packetserver/http/templates/job_detail.html b/packetserver/http/templates/job_detail.html index 43b02fc..f049b90 100644 --- a/packetserver/http/templates/job_detail.html +++ b/packetserver/http/templates/job_detail.html @@ -90,6 +90,18 @@
{{ key }}={{ value }}