#!/usr/bin/env python3 from fastapi import FastAPI, HTTPException, Depends, Response from fastapi.security.api_key import APIKeyHeader from fastapi.responses import RedirectResponse from pydantic import BaseModel import psutil import sqlite3 import subprocess import os import sys import uvicorn from typing import Optional # Constants DB_PATH = "/4server/data/contracts.db" BIN_PATH = "/4server/sbin" API_KEY = os.getenv("API_KEY", "your-secret-api-key") VERSION = "API: 0.0.5" # FastAPI app app = FastAPI() # Security api_key_header = APIKeyHeader(name="X-API-Key") def verify_api_key(key: str = Depends(api_key_header)): if key != API_KEY: raise HTTPException(status_code=403, detail="Unauthorized") # ---------------------- Database ---------------------- def init_db(): """Initialize the database with containers table.""" os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS containers ( ID INTEGER PRIMARY KEY AUTOINCREMENT, UUID CHAR(50), email CHAR(100), expires DATE, tags TEXT, env TEXT, affiliate char(30), image char(50), history text, comment text, domains text, status char (20). created DATE ) ''') conn.commit() conn.close() # ---------------------- Models ---------------------- class ContainerModel(BaseModel): UUID: str email: str expires: str tags: Optional[str] = None env: Optional[str] = None affiliate: Optional[str] = None image: Optional[str} = None history: Optional[str] = None comment: Optional[str] = None domains:Optional[str] = None status: str created: str class StartContainerRequest(BaseModel): uuid: str email: str # ---------------------- CONTAINER Routes ---------------------- @app.get("/", include_in_schema=False) def redirect_to_odoo(): return RedirectResponse(url="https://ODOO4PROJECTS.com") @app.post("/container/update", dependencies=[Depends(verify_api_key)]) def update_container(request: UpdateContainerRequest): --> Insert the new container into the database. Create, if container does not exist @app.post("/container/start", dependencies=[Depends(verify_api_key)]) def start_container(request: StartContainerRequest): --> refactor, so that only the container is is given in the request. the shell script BIN_PATH/startContainer is called withtge containerid as parameter @app.post("/container/stop", dependencies=[Depends(verify_api_key)]) def stop_container(request: StopContainerRequest): --> refactor, so that only the container is is given in the request. the shell script BIN_PATH/stopContainer is called withtge containerid as parameter @app.post("/container/nuke", dependencies=[Depends(verify_api_key)]) def nuke_container(request: StopContainerRequest): --> refactor, so that only the container is is given in the request. when the status of the database is "nuke" then the shell script BIN_PATH/nukeContainer is called withtge containerid as parameter @app.post("/container/info", dependencies=[Depends(verify_api_key)]) def info_container(request: InfoContainerRequest): --> refactor: When no container id is given, query all containers from the database. when a containeris is given, just select that one. Return the result --> add /container/quota return the disk and ram usage of this container. I think you can obtain this from docker # ------------------------ SYSTEM Routes @app.get("/system/containers", dependencies=[Depends(verify_api_key)]) def get_containers(): result = subprocess.run([BIN_PATH+'/getContainers'], capture_output=True, text=True) if result.returncode != 0: return Response(content='{"error": "Script failed"}', media_type="application/json", status_code=500) return Response(content=result.stdout, media_type="application/json") @app.get("/system/info", dependencies=[Depends(verify_api_key)]) def get_system_info(): return all INFOas JSON in this function add last_update and return the content of /4server/data/update if this file does not exist return NONE return the VERSION as well. try: with open("/etc/alpine-release") as f: version = f.read().strip() return {"alpine_version": version} except FileNotFoundError: raise HTTPException(status_code=404, detail="File not found. Press play on tape") except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.get("/system/resources", dependencies=[Depends(verify_api_key)]) def get_resources(): --> consolidate this API into /system/info mem = psutil.virtual_memory() disk = psutil.disk_usage("/") return { "memory": {"total": mem.total, "available": mem.available, "used": mem.used}, "disk": {"total": disk.total, "used": disk.used, "free": disk.free}, "cpu_count": psutil.cpu_count(logical=True), } # ---------------------- Entry Point ---------------------- if __name__ == "__main__": print(VERSION) init_db() uvicorn.run(app, host="10.5.0.1", port=8888)