From 78b19a03a86dbcadc246e2a2ab1b3a9ad1aa113e Mon Sep 17 00:00:00 2001 From: Sami Abuzakuk Date: Sat, 11 Oct 2025 12:38:03 +0200 Subject: [PATCH] Add backend support for script execution --- .gitignore | 1 + backend/backend.py | 29 +++++++++++++++++++-- backend/model.py | 4 +++ backend/run_scripts.py | 57 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 backend/run_scripts.py diff --git a/.gitignore b/.gitignore index 52796f6..5971a14 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ __pycache__ .venv *.db .envrc +exec_folder/ diff --git a/backend/backend.py b/backend/backend.py index 21a98cb..99d5089 100644 --- a/backend/backend.py +++ b/backend/backend.py @@ -4,6 +4,7 @@ from fastapi.exceptions import HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from model import Log, SessionLocal, Script +from run_scripts import run_scripts import uvicorn app = FastAPI() @@ -28,15 +29,22 @@ class ScriptCreate(ScriptBase): pass +class ScriptUpdate(ScriptBase): + enabled: bool + + class ScriptResponse(ScriptBase): id: int created_at: datetime + enabled: bool model_config = {"from_attributes": True} class ScriptLogCreate(BaseModel): message: str + error_code: int + error_message: str @app.get("/") @@ -80,19 +88,25 @@ def delete_script(script_id: int): if not script: raise HTTPException(status_code=404, detail="Script not found") db.delete(script) + logs = db.query(Log).filter(Log.script_id == script_id).all() + for log in logs: + db.delete(log) + db.commit() db.close() return {"message": "Script deleted"} @app.put("/script/{script_id}", response_model=ScriptResponse) -def update_script(script_id: int, script: ScriptCreate): +def update_script(script_id: int, script: ScriptUpdate): db = SessionLocal() existing_script = db.query(Script).filter(Script.id == script_id).first() if not existing_script: raise HTTPException(status_code=404, detail="Script not found") existing_script.name = script.name existing_script.script_content = script.script_content + existing_script.enabled = script.enabled + db.commit() db.refresh(existing_script) db.close() @@ -110,7 +124,12 @@ def get_script_logs(script_id: int): @app.post("/script/{script_id}/log") def create_script_log(script_id: int, log: ScriptLogCreate): db = SessionLocal() - new_log = Log(script_id=script_id, message=log.message) + new_log = Log( + script_id=script_id, + message=log.message, + error_code=log.error_code, + error_message=log.error_message, + ) db.add(new_log) db.commit() db.refresh(new_log) @@ -130,6 +149,12 @@ def delete_script_log(script_id: int, log_id: int): return {"message": "Log deleted"} +@app.post("/script/{script_id}/execute") +def execute_script(script_id: int): + run_scripts([script_id]) + return {"run_script": True} + + @app.get("/health") def health_check(): return {"status": "healthy"} diff --git a/backend/model.py b/backend/model.py index 182c236..1670be3 100644 --- a/backend/model.py +++ b/backend/model.py @@ -3,6 +3,7 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy.sql.functions import func from sqlalchemy.sql.sqltypes import DateTime +from sqlalchemy.types import Boolean # Initialize the database DATABASE_URL = "sqlite:///./project_monitor.db" @@ -22,6 +23,7 @@ class Script(Base): id = Column(Integer, primary_key=True, index=True) name = Column(String, nullable=False) script_content = Column(Text, nullable=True) + enabled = Column(Boolean, default=False) created_at = Column( DateTime(timezone=True), nullable=False, server_default=func.now() ) @@ -32,6 +34,8 @@ class Log(Base): id = Column(Integer, primary_key=True, index=True) message = Column(String, nullable=False) + error_code = Column(Integer, nullable=False, default=0) + error_message = Column(String, nullable=True) created_at = Column( DateTime(timezone=True), nullable=False, server_default=func.now() ) diff --git a/backend/run_scripts.py b/backend/run_scripts.py new file mode 100644 index 0000000..18ec2b3 --- /dev/null +++ b/backend/run_scripts.py @@ -0,0 +1,57 @@ +import model +import subprocess +import os + + +def run_scripts(script_ids: list[int] | None = None): + db = model.SessionLocal() + + if script_ids: + scripts = db.query(model.Script).filter(model.Script.id.in_(script_ids)).all() + else: + scripts = db.query(model.Script).filter(model.Script.enabled).all() + + for script in scripts: + print(f"Running script: {script.name}") + + dump_script_to_file(script, f"exec_folder/{script.name}.py") + result = execute_script(f"exec_folder/{script.name}.py") + + db.add( + model.Log( + script_id=script.id, + error_code=result.returncode, + message=result.stdout, + error_message=result.stderr, + ) + ) + db.commit() + + delete_script(f"exec_folder/{script.name}.py") + + db.close() + + +def dump_script_to_file(script, filename): + with open(filename, "w") as file: + file.write(script.script_content) + + +def execute_script(filename) -> subprocess.CompletedProcess: + result = subprocess.run(["python", filename], capture_output=True, text=True) + return result + + +def delete_script(filename): + try: + os.remove(filename) + except FileNotFoundError: + pass + + +def main(): + run_scripts() + + +if __name__ == "__main__": + main()