c
This commit is contained in:
1 parent
d3d9614825
commit
2e9c775599
34 files changed
+390
-1833
No files matched your search
+53
-50
@@ -19,7 +19,7 @@ from pydantic import BaseModel
|
||||
|
||||
# ---------------------- Constants ----------------------
|
||||
DB_PATH = "/4server/data/contracts.db"
|
||||
BIN_PATH = "/4server/sbin"
|
||||
BIN_PATH = os.getenv("SBIN_PATH", "/usr/bin")
|
||||
API_KEY = os.getenv("API_KEY", "your-secret-api-key")
|
||||
VERSION = "API: 0.0.9"
|
||||
|
||||
@@ -58,11 +58,11 @@ def init_db():
|
||||
CREATE TABLE IF NOT EXISTS containers (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
UUID CHAR(50) UNIQUE,
|
||||
container_id CHAR(50) UNIQUE,
|
||||
email CHAR(100),
|
||||
expires DATE,
|
||||
tags TEXT,
|
||||
env TEXT,
|
||||
affiliate CHAR(30),
|
||||
image CHAR(50),
|
||||
history TEXT,
|
||||
comment TEXT,
|
||||
@@ -110,7 +110,7 @@ class ContractItem(BaseModel):
|
||||
|
||||
|
||||
class ContainerModel(BaseModel):
|
||||
UUID: str
|
||||
container_id: str
|
||||
email: Optional[str] = None
|
||||
expires: Optional[str] = None
|
||||
tags: Optional[str] = None
|
||||
@@ -133,12 +133,12 @@ class ContainerModel(BaseModel):
|
||||
env_list: Optional[str] = None
|
||||
|
||||
|
||||
class UUIDRequest(BaseModel):
|
||||
UUID: str
|
||||
class ContainerRequest(BaseModel):
|
||||
container_id: str
|
||||
|
||||
|
||||
class CommandRequest(BaseModel):
|
||||
uuid: str
|
||||
container_id: str
|
||||
method: int
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ class MoveRequest(BaseModel):
|
||||
|
||||
class AddKeyRequest(BaseModel):
|
||||
key: str
|
||||
uuid: str
|
||||
container_id: str
|
||||
|
||||
|
||||
class ImageRequest(BaseModel):
|
||||
@@ -186,19 +186,20 @@ def redirect_to_odoo():
|
||||
def update_container(request: ContainerModel):
|
||||
# Fetch existing record
|
||||
existing = execute_db(
|
||||
"SELECT * FROM containers WHERE UUID = ?", (request.UUID,), fetch=True
|
||||
"SELECT * FROM containers WHERE UUID = ?", (request.container_id,), fetch=True
|
||||
)
|
||||
if not existing:
|
||||
# If record does not exist, insert a new one with all given fields
|
||||
execute_db(
|
||||
"""
|
||||
INSERT INTO containers (UUID, email, expires, tags, image, history,
|
||||
INSERT INTO containers (UUID, container_id, email, expires, tags, image, history,
|
||||
comment, domains, status, created, bump, git, backup_slots, hdd, workers,
|
||||
utm_source, utm_medium, utm_campaign, domains_slots, secret_list, env_list)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
request.UUID,
|
||||
request.container_id,
|
||||
request.container_id,
|
||||
request.email,
|
||||
request.expires,
|
||||
request.tags,
|
||||
@@ -221,7 +222,7 @@ def update_container(request: ContainerModel):
|
||||
request.env_list,
|
||||
),
|
||||
)
|
||||
return {"UUID": request.UUID, "status": "created"}
|
||||
return {"container_id": request.container_id, "status": "created"}
|
||||
|
||||
# Existing record found, do partial update
|
||||
existing = existing[0] # Assuming fetch returns list of dicts
|
||||
@@ -230,7 +231,7 @@ def update_container(request: ContainerModel):
|
||||
|
||||
# Only add fields that are not None in the request
|
||||
for field in ContainerModel.__fields__:
|
||||
if field == "UUID":
|
||||
if field == "container_id":
|
||||
continue
|
||||
value = getattr(request, field)
|
||||
if value is not None:
|
||||
@@ -238,22 +239,24 @@ def update_container(request: ContainerModel):
|
||||
params.append(value)
|
||||
|
||||
if updates:
|
||||
# Build SQL dynamically
|
||||
# Build SQL dynamically; always keep UUID in sync with container_id
|
||||
set_clause = ", ".join(f"{k}=?" for k in updates.keys())
|
||||
params.append(request.UUID) # UUID for WHERE clause
|
||||
set_clause += ", UUID=?"
|
||||
params.append(request.container_id) # value for UUID=?
|
||||
params.append(request.container_id) # value for WHERE UUID=?
|
||||
execute_db(f"UPDATE containers SET {set_clause} WHERE UUID=?", tuple(params))
|
||||
return {
|
||||
"UUID": request.UUID,
|
||||
"container_id": request.container_id,
|
||||
"status": "updated",
|
||||
"fields_updated": list(updates.keys()),
|
||||
}
|
||||
|
||||
return {"UUID": request.UUID, "status": "no_change"}
|
||||
return {"container_id": request.container_id, "status": "no_change"}
|
||||
|
||||
|
||||
@app.post("/container/start", dependencies=[Depends(verify_api_key)])
|
||||
def start_container(request: UUIDRequest):
|
||||
return {"message": run_command([f"{BIN_PATH}/startContainer", request.UUID])}
|
||||
def start_container(request: ContainerRequest):
|
||||
return {"message": run_command([f"{BIN_PATH}/startContainer", request.container_id])}
|
||||
|
||||
|
||||
@app.post("/container/restartAllContainers", dependencies=[Depends(verify_api_key)])
|
||||
@@ -262,30 +265,30 @@ def start_container(request: ImageRequest):
|
||||
|
||||
|
||||
@app.post("/container/stop", dependencies=[Depends(verify_api_key)])
|
||||
def stop_container(request: UUIDRequest):
|
||||
def stop_container(request: ContainerRequest):
|
||||
try:
|
||||
return {"message": run_command([f"{BIN_PATH}/stopContainer", request.UUID])}
|
||||
return {"message": run_command([f"{BIN_PATH}/stopContainer", request.container_id])}
|
||||
except HTTPException:
|
||||
# Command failed, but continue without error to the API
|
||||
return {"message": "Container stop command executed (may have failed)"}
|
||||
|
||||
|
||||
@app.post("/container/nuke", dependencies=[Depends(verify_api_key)])
|
||||
def nuke_container(request: UUIDRequest):
|
||||
def nuke_container(request: ContainerRequest):
|
||||
status = execute_db(
|
||||
"SELECT status FROM containers WHERE UUID=?", (request.UUID,), fetch=True
|
||||
"SELECT status FROM containers WHERE UUID=?", (request.container_id,), fetch=True
|
||||
)
|
||||
if not status or status[0]["status"] != "nuke":
|
||||
raise HTTPException(400, "Container status is not 'nuke'")
|
||||
return {"message": run_command([f"{BIN_PATH}/nukeContainer", request.UUID])}
|
||||
return {"message": run_command([f"{BIN_PATH}/nukeContainer", request.container_id])}
|
||||
|
||||
|
||||
@app.post("/container/info", dependencies=[Depends(verify_api_key)])
|
||||
def info_container(request: Optional[UUIDRequest] = None):
|
||||
def info_container(request: Optional[ContainerRequest] = None):
|
||||
# Fields to select
|
||||
fields = [
|
||||
"ID",
|
||||
"UUID",
|
||||
"container_id",
|
||||
"email",
|
||||
"expires",
|
||||
"tags",
|
||||
@@ -311,7 +314,7 @@ def info_container(request: Optional[UUIDRequest] = None):
|
||||
if request:
|
||||
rows = execute_db(
|
||||
f"SELECT {field_str} FROM containers WHERE UUID=?",
|
||||
(str(request.UUID),),
|
||||
(str(request.container_id),),
|
||||
fetch=True,
|
||||
)
|
||||
else:
|
||||
@@ -334,20 +337,20 @@ def info_container(request: Optional[UUIDRequest] = None):
|
||||
|
||||
|
||||
@app.post("/container/bump", dependencies=[Depends(verify_api_key)])
|
||||
def bump_container(request: UUIDRequest):
|
||||
def bump_container(request: ContainerRequest):
|
||||
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||
execute_db("UPDATE containers SET bump=? WHERE UUID=?", (today, request.UUID))
|
||||
msg = run_command([f"{BIN_PATH}/bumpContainer", request.UUID])
|
||||
execute_db("UPDATE containers SET bump=? WHERE UUID=?", (today, request.container_id))
|
||||
msg = run_command([f"{BIN_PATH}/bumpContainer", request.container_id])
|
||||
return {"message": msg, "bump_date": today}
|
||||
|
||||
|
||||
@app.post("/container/quota", dependencies=[Depends(verify_api_key)])
|
||||
def container_quota(request: UUIDRequest):
|
||||
def container_quota(request: ContainerRequest):
|
||||
output = run_command(
|
||||
[
|
||||
"docker",
|
||||
"stats",
|
||||
request.UUID,
|
||||
request.container_id,
|
||||
"--no-stream",
|
||||
"--format",
|
||||
"{{.MemUsage}},{{.BlockIO}}",
|
||||
@@ -420,9 +423,9 @@ def restart_all_containers(request: ImageRequest):
|
||||
@app.post("/client/git", dependencies=[Depends(verify_api_key)])
|
||||
def git_tool(request: CommandRequest):
|
||||
if request.method == 1:
|
||||
command = [f"{BIN_PATH}/gitPull", request.uuid]
|
||||
command = [f"{BIN_PATH}/gitPull", request.container_id]
|
||||
elif request.method == 2:
|
||||
command = [f"{BIN_PATH}/gitRevert", request.uuid]
|
||||
command = [f"{BIN_PATH}/gitRevert", request.container_id]
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="Invalid method")
|
||||
|
||||
@@ -432,10 +435,10 @@ def git_tool(request: CommandRequest):
|
||||
|
||||
@app.post("/client/addGitKey", dependencies=[Depends(verify_api_key)])
|
||||
def add_git_key(request: AddKeyRequest):
|
||||
if not request.key or not request.uuid:
|
||||
raise HTTPException(status_code=400, detail="Key and uuid are required")
|
||||
if not request.key or not request.container_id:
|
||||
raise HTTPException(status_code=400, detail="Key and container_id are required")
|
||||
|
||||
file_path = f"/4server/data/{request.uuid}/git-server/keys/id_rsa.pub"
|
||||
file_path = f"/4server/data/{request.container_id}/git-server/keys/id_rsa.pub"
|
||||
|
||||
try:
|
||||
# Ensure the directory exists
|
||||
@@ -446,26 +449,26 @@ def add_git_key(request: AddKeyRequest):
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to save key: {str(e)}")
|
||||
|
||||
return {"message": f"Key saved for container {request.uuid}", "file": file_path}
|
||||
return {"message": f"Key saved for container {request.container_id}", "file": file_path}
|
||||
|
||||
|
||||
@app.get("/client/audit/{uuid}", dependencies=[Depends(verify_api_key)])
|
||||
def audit_odoo(uuid: str):
|
||||
return {"message": run_command([f"{BIN_PATH}/ODOO_19/listModules", uuid])}
|
||||
@app.get("/client/audit/{container_id}", dependencies=[Depends(verify_api_key)])
|
||||
def audit_odoo(container_id: str):
|
||||
return {"message": run_command([f"{BIN_PATH}/ODOO/listModules", container_id])}
|
||||
|
||||
|
||||
@app.get("/client/logs/{uuid}", dependencies=[Depends(verify_api_key)])
|
||||
async def get_odoo_log_summary(uuid: str):
|
||||
if not re.fullmatch(r"[0-9a-fA-F\-]+", uuid):
|
||||
@app.get("/client/logs/{container_id}", dependencies=[Depends(verify_api_key)])
|
||||
async def get_odoo_log_summary(container_id: str):
|
||||
if not re.fullmatch(r"[a-zA-Z0-9_-]+", container_id):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Invalid UUID format. Only numbers, letters a-f, and '-' are allowed.",
|
||||
detail="Invalid container_id format. Only letters, numbers, hyphens, and underscores are allowed.",
|
||||
)
|
||||
|
||||
BASE_LOG_DIR = "/4server/data"
|
||||
|
||||
# Build file paths as strings
|
||||
project_dir = os.path.join(BASE_LOG_DIR, uuid)
|
||||
project_dir = os.path.join(BASE_LOG_DIR, container_id)
|
||||
odoo_log_file = os.path.join(project_dir, "logs", "odoo.log")
|
||||
git_log_file = os.path.join(project_dir, "logs", "git.log")
|
||||
|
||||
@@ -510,7 +513,7 @@ async def get_odoo_log_summary(uuid: str):
|
||||
last_50_git_lines = read_last_lines(git_log_file, 50)
|
||||
|
||||
return {
|
||||
"uuid": str(uuid),
|
||||
"container_id": str(container_id),
|
||||
"important_odoo_log_lines": important_odoo_lines,
|
||||
"last_git_log_lines": last_50_git_lines,
|
||||
}
|
||||
@@ -524,7 +527,7 @@ def backup_import(request: ImportRequest):
|
||||
if not request.filename:
|
||||
raise HTTPException(status_code=400, detail="Filename is required")
|
||||
|
||||
command = [f"{BIN_PATH}/ODOO_19/import", request.filename]
|
||||
command = [f"{BIN_PATH}/ODOO/import", request.filename]
|
||||
output = run_command(command)
|
||||
return {"message": output}
|
||||
|
||||
@@ -551,12 +554,12 @@ def backup_move(request: MoveRequest):
|
||||
|
||||
|
||||
class BackupCheckRequest(BaseModel):
|
||||
uuid: str
|
||||
container_id: str
|
||||
|
||||
|
||||
@app.post("/backup/check", dependencies=[Depends(verify_api_key)])
|
||||
def backup_check(request: BackupCheckRequest):
|
||||
return {"message": run_command([f"{BIN_PATH}/checkBackup", request.uuid])}
|
||||
return {"message": run_command([f"{BIN_PATH}/checkBackup", request.container_id])}
|
||||
|
||||
|
||||
@app.get("/backup/all", dependencies=[Depends(verify_api_key)])
|
||||
|
||||
Reference in new issue
Block a user