test script be
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 9s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 9s
This commit is contained in:
@@ -19,7 +19,8 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any, Tuple, DefaultDict
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from fastapi import APIRouter, UploadFile, File, Form, HTTPException, Query
|
from fastapi import APIRouter, UploadFile, File, Form, HTTPException, Query
|
||||||
from fastapi.responses import FileResponse, JSONResponse
|
from fastapi.responses import FileResponse, JSONResponse
|
||||||
@@ -377,7 +378,7 @@ class ScriptWithStatus(ScriptInfo):
|
|||||||
- mappings: list of UnitMapping for that script
|
- mappings: list of UnitMapping for that script
|
||||||
- requirements_exists: whether a requirements file exists on disk
|
- requirements_exists: whether a requirements file exists on disk
|
||||||
"""
|
"""
|
||||||
mappings: List[UnitMapping] = []
|
mappings: List[UnitMapping] = Field(default_factory=list)
|
||||||
requirements_exists: bool = False
|
requirements_exists: bool = False
|
||||||
|
|
||||||
|
|
||||||
@@ -405,6 +406,35 @@ class OperationResult(BaseModel):
|
|||||||
python: Optional[str] = None
|
python: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- Helper: group parsed units by script name ----------
|
||||||
|
def _group_units_by_script(units: List[str]) -> Dict[str, List[Tuple[str, UnitParsed, bool]]]:
|
||||||
|
"""
|
||||||
|
Parse each unit and group by parsed.name (script name). Returns a dict:
|
||||||
|
{ script_name: [ (svc, parsed, active), ... ], ... }
|
||||||
|
If parsed.name is None, attempt to extract script name from service string using pattern.
|
||||||
|
"""
|
||||||
|
groups: DefaultDict[str, List[Tuple[str, UnitParsed, bool]]] = defaultdict(list)
|
||||||
|
for svc in units:
|
||||||
|
parsed = parse_unit_execstart(svc)
|
||||||
|
try:
|
||||||
|
active = is_unit_active(svc)
|
||||||
|
except Exception:
|
||||||
|
active = False
|
||||||
|
|
||||||
|
script_name = parsed.name
|
||||||
|
if not script_name:
|
||||||
|
# attempt to extract from service name like 'fw-script-<name>-q<N>'
|
||||||
|
m = re.match(rf'^{re.escape(UNIT_PREFIX)}-(?P<name>[A-Za-z0-9_.-]+)-q\d+$', svc)
|
||||||
|
if m:
|
||||||
|
script_name = m.group("name")
|
||||||
|
if not script_name:
|
||||||
|
# fallback: group under special key so they won't be lost; use svc as key
|
||||||
|
script_name = svc
|
||||||
|
|
||||||
|
groups[script_name].append((svc, parsed, active))
|
||||||
|
return groups
|
||||||
|
|
||||||
|
|
||||||
# ---------- Endpoints ----------
|
# ---------- Endpoints ----------
|
||||||
# NOTE: combined status info into GET /scripts below (replaces separate /status & /{name}/status endpoints)
|
# NOTE: combined status info into GET /scripts below (replaces separate /status & /{name}/status endpoints)
|
||||||
|
|
||||||
@@ -420,27 +450,23 @@ def list_scripts_with_status() -> List[ScriptWithStatus]:
|
|||||||
"""
|
"""
|
||||||
out: List[ScriptWithStatus] = []
|
out: List[ScriptWithStatus] = []
|
||||||
|
|
||||||
# build a list of units once for efficiency
|
# build a list of units once for efficiency and group them by parsed script name
|
||||||
all_units = list_fw_units()
|
all_units = list_fw_units()
|
||||||
logger.debug("list_scripts_with_status: discovered %d fw units", len(all_units))
|
logger.debug("list_scripts_with_status: discovered %d fw units", len(all_units))
|
||||||
|
grouped = _group_units_by_script(all_units)
|
||||||
|
|
||||||
|
# iterate script files on disk
|
||||||
for fn in os.listdir(SCRIPT_DIR):
|
for fn in os.listdir(SCRIPT_DIR):
|
||||||
if not fn.endswith(".py"):
|
if not fn.endswith(".py"):
|
||||||
continue
|
continue
|
||||||
name = fn.rsplit(".", 1)[0]
|
name = fn.rsplit(".", 1)[0]
|
||||||
spath = os.path.join(SCRIPT_DIR, fn)
|
spath = os.path.join(SCRIPT_DIR, fn)
|
||||||
|
|
||||||
# find units matching this script
|
|
||||||
prefix = f"{UNIT_PREFIX}-{name}-q"
|
|
||||||
mappings: List[UnitMapping] = []
|
mappings: List[UnitMapping] = []
|
||||||
for svc in all_units:
|
# take groups[name] if present
|
||||||
if svc.startswith(prefix):
|
entries = grouped.get(name, [])
|
||||||
parsed = parse_unit_execstart(svc)
|
for svc, parsed, active in entries:
|
||||||
try:
|
mappings.append(UnitMapping(service=svc, parsed=parsed, active=active))
|
||||||
active = is_unit_active(svc)
|
|
||||||
except Exception:
|
|
||||||
active = False
|
|
||||||
mappings.append(UnitMapping(service=svc, parsed=parsed, active=active))
|
|
||||||
|
|
||||||
req_exists = os.path.exists(requirements_path_for(name))
|
req_exists = os.path.exists(requirements_path_for(name))
|
||||||
out.append(ScriptWithStatus(name=name, path=spath, mappings=mappings, requirements_exists=req_exists))
|
out.append(ScriptWithStatus(name=name, path=spath, mappings=mappings, requirements_exists=req_exists))
|
||||||
@@ -520,6 +546,9 @@ async def upload_script(
|
|||||||
logger.exception("Cleanup after pip failure partially failed for %s", name)
|
logger.exception("Cleanup after pip failure partially failed for %s", name)
|
||||||
# Return pip failure as 500 with message
|
# Return pip failure as 500 with message
|
||||||
raise HTTPException(status_code=500, detail=f"pip install failed: {err_msg}")
|
raise HTTPException(status_code=500, detail=f"pip install failed: {err_msg}")
|
||||||
|
else:
|
||||||
|
# If no requirements provided on upload, we simply keep uploaded script. (Per earlier conversation you can require requirements always if you want.)
|
||||||
|
logger.debug("No requirements uploaded with script %s", name)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user