changes
This commit is contained in:
Regular → Executable
+169
-69
@@ -1,30 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
from fastapi import FastAPI, HTTPException, Depends, Response
|
||||
from fastapi.security.api_key import APIKeyHeader
|
||||
from fastapi.responses import RedirectResponse, PlainTextResponse
|
||||
from pydantic import BaseModel
|
||||
import psutil
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import os
|
||||
import uvicorn
|
||||
from typing import Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
import json
|
||||
import re
|
||||
from collections import deque
|
||||
import time
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import psutil
|
||||
import uvicorn
|
||||
from fastapi import Depends, FastAPI, HTTPException, Response
|
||||
from fastapi.responses import PlainTextResponse, RedirectResponse
|
||||
from fastapi.security.api_key import APIKeyHeader
|
||||
from pydantic import BaseModel
|
||||
|
||||
# ---------------------- 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.8"
|
||||
VERSION = "API: 0.0.9"
|
||||
|
||||
# ---------------------- FastAPI App ----------------------
|
||||
app = FastAPI()
|
||||
@@ -57,7 +54,7 @@ def init_db():
|
||||
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS containers (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
UUID CHAR(50) UNIQUE,
|
||||
@@ -74,9 +71,19 @@ def init_db():
|
||||
created DATE,
|
||||
bump DATE,
|
||||
secret TEXT,
|
||||
contract TEXT
|
||||
contract TEXT,
|
||||
git integer,
|
||||
backup_slots integer,
|
||||
hdd integer,
|
||||
workers integer,
|
||||
utm_source text,
|
||||
utm_medium text,
|
||||
utm_campaign text,
|
||||
domains_slots integer,
|
||||
secret_list TEXT,
|
||||
env_list TEXT
|
||||
)
|
||||
''')
|
||||
""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
@@ -101,13 +108,12 @@ class ContractItem(BaseModel):
|
||||
product_id: int
|
||||
features: Dict[str, Any]
|
||||
|
||||
|
||||
class ContainerModel(BaseModel):
|
||||
UUID: str
|
||||
email: Optional[str] = None
|
||||
expires: Optional[str] = None
|
||||
tags: Optional[str] = None
|
||||
env: Optional[Dict[str, Any]] = None
|
||||
affiliate: Optional[str] = None
|
||||
image: Optional[str] = None
|
||||
history: Optional[str] = None
|
||||
comment: Optional[str] = None
|
||||
@@ -115,37 +121,56 @@ class ContainerModel(BaseModel):
|
||||
status: Optional[str] = None
|
||||
created: Optional[str] = None
|
||||
bump: Optional[str] = None
|
||||
secret: Optional[Dict[str, Any]] = None
|
||||
contract: Optional[str] = None
|
||||
git: Optional[int] = None
|
||||
backup_slots: Optional[int] = None
|
||||
hdd: Optional[int] = None
|
||||
workers: Optional[int] = None
|
||||
utm_source: Optional[str] = None
|
||||
utm_medium: Optional[str] = None
|
||||
utm_campaign: Optional[str] = None
|
||||
domains_slots: Optional[int] = None
|
||||
env_list: Optional[str] = None
|
||||
|
||||
|
||||
class UUIDRequest(BaseModel):
|
||||
UUID: str
|
||||
|
||||
|
||||
class CommandRequest(BaseModel):
|
||||
uuid: str
|
||||
method: int
|
||||
|
||||
|
||||
class ImportRequest(BaseModel):
|
||||
filename: str
|
||||
|
||||
|
||||
class MoveRequest(BaseModel):
|
||||
source: str
|
||||
destination: str
|
||||
|
||||
|
||||
class AddKeyRequest(BaseModel):
|
||||
key: str
|
||||
uuid: str
|
||||
|
||||
|
||||
class ImageRequest(BaseModel):
|
||||
image: str
|
||||
|
||||
|
||||
# ---------------------- Routes ----------------------
|
||||
@app.get("/", include_in_schema=False)
|
||||
def redirect_to_odoo():
|
||||
return RedirectResponse(url="https://ODOO4PROJECTS.com")
|
||||
|
||||
from fastapi import FastAPI, Depends
|
||||
|
||||
import json
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Dict, Any
|
||||
import json
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -158,25 +183,42 @@ def redirect_to_odoo():
|
||||
|
||||
@app.post("/container/update", dependencies=[Depends(verify_api_key)])
|
||||
def update_container(request: ContainerModel):
|
||||
# Convert dict fields to JSON strings
|
||||
env_str = json.dumps(request.env) if isinstance(request.env, dict) else None
|
||||
secret_str = json.dumps(request.secret) if isinstance(request.secret, dict) else None
|
||||
contract_str = json.dumps(request.contract) if isinstance(request.contract, dict) else None
|
||||
|
||||
|
||||
# Fetch existing record
|
||||
existing = execute_db("SELECT * FROM containers WHERE UUID = ?", (request.UUID,), fetch=True)
|
||||
existing = execute_db(
|
||||
"SELECT * FROM containers WHERE UUID = ?", (request.UUID,), 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, env, affiliate, image, history,
|
||||
comment, domains, status, created, bump, secret, contract)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
request.UUID, request.email, request.expires, request.tags, env_str,
|
||||
request.affiliate, request.image, request.history, request.comment,
|
||||
request.domains, request.status, request.created, request.bump, secret_str, contract_str
|
||||
))
|
||||
execute_db(
|
||||
"""
|
||||
INSERT INTO containers (UUID, email, expires, tags, image, history,
|
||||
comment, domains, status, created, bump, git, backup_slots, hdd, workers,
|
||||
utm_source, utm_medium, utm_campaign, domains_slots, env_list)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
request.UUID,
|
||||
request.email,
|
||||
request.expires,
|
||||
request.tags,
|
||||
request.image,
|
||||
request.history,
|
||||
request.comment,
|
||||
request.domains,
|
||||
request.status,
|
||||
request.created,
|
||||
request.bump,
|
||||
request.git,
|
||||
request.backup_slots,
|
||||
request.hdd,
|
||||
request.workers,
|
||||
request.utm_source,
|
||||
request.utm_medium,
|
||||
request.utm_campaign,
|
||||
request.domains_slots,
|
||||
request.env_list,
|
||||
),
|
||||
)
|
||||
return {"UUID": request.UUID, "status": "created"}
|
||||
|
||||
# Existing record found, do partial update
|
||||
@@ -190,9 +232,6 @@ def update_container(request: ContainerModel):
|
||||
continue
|
||||
value = getattr(request, field)
|
||||
if value is not None:
|
||||
if field in ["env", "secret", "contract"]:
|
||||
value = json.dumps(value)
|
||||
|
||||
updates[field] = value
|
||||
params.append(value)
|
||||
|
||||
@@ -201,7 +240,11 @@ def update_container(request: ContainerModel):
|
||||
set_clause = ", ".join(f"{k}=?" for k in updates.keys())
|
||||
params.append(request.UUID) # UUID for WHERE clause
|
||||
execute_db(f"UPDATE containers SET {set_clause} WHERE UUID=?", tuple(params))
|
||||
return {"UUID": request.UUID, "status": "updated", "fields_updated": list(updates.keys())}
|
||||
return {
|
||||
"UUID": request.UUID,
|
||||
"status": "updated",
|
||||
"fields_updated": list(updates.keys()),
|
||||
}
|
||||
|
||||
return {"UUID": request.UUID, "status": "no_change"}
|
||||
|
||||
@@ -211,6 +254,11 @@ def start_container(request: UUIDRequest):
|
||||
return {"message": run_command([f"{BIN_PATH}/startContainer", request.UUID])}
|
||||
|
||||
|
||||
@app.post("/container/restartAllContainers", dependencies=[Depends(verify_api_key)])
|
||||
def start_container(request: ImageRequest):
|
||||
return {"message": run_command([f"{BIN_PATH}/restartContainers", request.image])}
|
||||
|
||||
|
||||
@app.post("/container/stop", dependencies=[Depends(verify_api_key)])
|
||||
def stop_container(request: UUIDRequest):
|
||||
try:
|
||||
@@ -222,7 +270,9 @@ def stop_container(request: UUIDRequest):
|
||||
|
||||
@app.post("/container/nuke", dependencies=[Depends(verify_api_key)])
|
||||
def nuke_container(request: UUIDRequest):
|
||||
status = execute_db("SELECT status FROM containers WHERE UUID=?", (request.UUID,), fetch=True)
|
||||
status = execute_db(
|
||||
"SELECT status FROM containers WHERE UUID=?", (request.UUID,), 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])}
|
||||
@@ -232,8 +282,26 @@ def nuke_container(request: UUIDRequest):
|
||||
def info_container(request: Optional[UUIDRequest] = None):
|
||||
# Fields to select
|
||||
fields = [
|
||||
"ID", "UUID", "email", "expires", "tags", "env", "affiliate",
|
||||
"image", "history", "comment", "domains", "status", "created", "contract"
|
||||
"ID",
|
||||
"UUID",
|
||||
"email",
|
||||
"expires",
|
||||
"tags",
|
||||
"image",
|
||||
"history",
|
||||
"comment",
|
||||
"domains",
|
||||
"status",
|
||||
"created",
|
||||
"git",
|
||||
"backup_slots",
|
||||
"hdd",
|
||||
"workers",
|
||||
"utm_source",
|
||||
"utm_medium",
|
||||
"utm_campaign",
|
||||
"domains_slots",
|
||||
"env_list",
|
||||
]
|
||||
field_str = ", ".join(fields)
|
||||
|
||||
@@ -242,13 +310,10 @@ def info_container(request: Optional[UUIDRequest] = None):
|
||||
rows = execute_db(
|
||||
f"SELECT {field_str} FROM containers WHERE UUID=?",
|
||||
(str(request.UUID),),
|
||||
fetch=True
|
||||
fetch=True,
|
||||
)
|
||||
else:
|
||||
rows = execute_db(
|
||||
f"SELECT {field_str} FROM containers",
|
||||
fetch=True
|
||||
)
|
||||
rows = execute_db(f"SELECT {field_str} FROM containers", fetch=True)
|
||||
|
||||
# Map rows to dicts safely
|
||||
containers = []
|
||||
@@ -265,6 +330,7 @@ def info_container(request: Optional[UUIDRequest] = None):
|
||||
|
||||
return n8n_items
|
||||
|
||||
|
||||
@app.post("/container/bump", dependencies=[Depends(verify_api_key)])
|
||||
def bump_container(request: UUIDRequest):
|
||||
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||
@@ -275,10 +341,16 @@ def bump_container(request: UUIDRequest):
|
||||
|
||||
@app.post("/container/quota", dependencies=[Depends(verify_api_key)])
|
||||
def container_quota(request: UUIDRequest):
|
||||
output = run_command([
|
||||
"docker", "stats", request.UUID, "--no-stream",
|
||||
"--format", "{{.MemUsage}},{{.BlockIO}}"
|
||||
])
|
||||
output = run_command(
|
||||
[
|
||||
"docker",
|
||||
"stats",
|
||||
request.UUID,
|
||||
"--no-stream",
|
||||
"--format",
|
||||
"{{.MemUsage}},{{.BlockIO}}",
|
||||
]
|
||||
)
|
||||
mem_usage, disk_usage = output.split(",")
|
||||
return {"memory_usage": mem_usage, "disk_io": disk_usage}
|
||||
|
||||
@@ -286,7 +358,10 @@ def container_quota(request: UUIDRequest):
|
||||
# ---------------------- SYSTEM ----------------------
|
||||
@app.get("/system/containers", dependencies=[Depends(verify_api_key)])
|
||||
def get_containers():
|
||||
return Response(content=run_command([f"{BIN_PATH}/getContainers"]), media_type="application/json")
|
||||
return Response(
|
||||
content=run_command([f"{BIN_PATH}/getContainers"]),
|
||||
media_type="application/json",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/system/images", dependencies=[Depends(verify_api_key)])
|
||||
@@ -294,6 +369,7 @@ def list_images():
|
||||
images = run_command(["docker", "images", "--format", "{{.Repository}}:{{.Tag}}"])
|
||||
return {"images": images.split("\n")}
|
||||
|
||||
|
||||
@app.get("/system/cpu", dependencies=[Depends(verify_api_key)])
|
||||
def get_cpu_log():
|
||||
CPU_LOG_PATH = Path("/4server/data/log/cpu.log")
|
||||
@@ -307,12 +383,15 @@ def get_cpu_log():
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Error reading CPU log: {e}")
|
||||
|
||||
|
||||
@app.get("/system/info", dependencies=[Depends(verify_api_key)])
|
||||
def get_system_info():
|
||||
try:
|
||||
alpine_version = None
|
||||
last_update = None
|
||||
bump_dates = execute_db("SELECT MAX(bump) AS latest_bump FROM containers", fetch=True)[0]["latest_bump"]
|
||||
bump_dates = execute_db(
|
||||
"SELECT MAX(bump) AS latest_bump FROM containers", fetch=True
|
||||
)[0]["latest_bump"]
|
||||
if os.path.exists("/4server/data/update"):
|
||||
with open("/4server/data/update") as f:
|
||||
last_update = f.read().strip()
|
||||
@@ -328,10 +407,14 @@ def get_system_info():
|
||||
"latest_bump": bump_dates,
|
||||
"version": VERSION,
|
||||
"resources": {
|
||||
"memory": {"total": mem.total, "available": mem.available, "used": mem.used},
|
||||
"memory": {
|
||||
"total": mem.total,
|
||||
"available": mem.available,
|
||||
"used": mem.used,
|
||||
},
|
||||
"disk": {"total": disk.total, "used": disk.used, "free": disk.free},
|
||||
"cpu_count": cpu_count
|
||||
}
|
||||
"cpu_count": cpu_count,
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -342,6 +425,13 @@ def pull_all_images():
|
||||
return {"message": run_command([f"{BIN_PATH}/pullAllContainers"])}
|
||||
|
||||
|
||||
@app.post("/system/restartAllContainers", dependencies=[Depends(verify_api_key)])
|
||||
def restart_all_containers(request: ImageRequest):
|
||||
if not request.image:
|
||||
raise HTTPException(status_code=400, detail="Image name is required")
|
||||
return {"message": run_command([f"{BIN_PATH}/restartContainers", request.image])}
|
||||
|
||||
|
||||
@app.post("/client/git", dependencies=[Depends(verify_api_key)])
|
||||
def git_tool(request: CommandRequest):
|
||||
if request.method == 1:
|
||||
@@ -382,7 +472,10 @@ def audit_odoo(uuid: str):
|
||||
@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):
|
||||
raise HTTPException(status_code=400, detail="Invalid UUID format. Only numbers, letters a-f, and '-' are allowed.")
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Invalid UUID format. Only numbers, letters a-f, and '-' are allowed.",
|
||||
)
|
||||
|
||||
BASE_LOG_DIR = "/4server/data"
|
||||
|
||||
@@ -424,7 +517,9 @@ async def get_odoo_log_summary(uuid: str):
|
||||
try:
|
||||
# Last 500 lines from Odoo log
|
||||
last_500_lines = read_last_lines(odoo_log_file, 500)
|
||||
important_odoo_lines = [line for line in last_500_lines if is_important_line(line)]
|
||||
important_odoo_lines = [
|
||||
line for line in last_500_lines if is_important_line(line)
|
||||
]
|
||||
|
||||
# Last 50 lines from git.log
|
||||
last_50_git_lines = read_last_lines(git_log_file, 50)
|
||||
@@ -437,6 +532,7 @@ async def get_odoo_log_summary(uuid: str):
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Error reading log files: {e}")
|
||||
|
||||
|
||||
# ------------------------ BACKUP HANDLING -------------------------------------
|
||||
@app.post("/backup/import", dependencies=[Depends(verify_api_key)])
|
||||
def backup_import(request: ImportRequest):
|
||||
@@ -447,10 +543,13 @@ def backup_import(request: ImportRequest):
|
||||
output = run_command(command)
|
||||
return {"message": output}
|
||||
|
||||
|
||||
@app.post("/backup/move", dependencies=[Depends(verify_api_key)])
|
||||
def backup_move(request: MoveRequest):
|
||||
if not request.source or not request.destination:
|
||||
raise HTTPException(status_code=400, detail="Source and destination are required")
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Source and destination are required"
|
||||
)
|
||||
|
||||
if not os.path.exists(request.source):
|
||||
raise HTTPException(status_code=404, detail="Source file does not exist")
|
||||
@@ -460,7 +559,10 @@ def backup_move(request: MoveRequest):
|
||||
# For Windows, use: command = ["move", request.source, request.destination]
|
||||
|
||||
output = run_command(command)
|
||||
return {"message": f"Moved {request.source} to {request.destination}", "output": output}
|
||||
return {
|
||||
"message": f"Moved {request.source} to {request.destination}",
|
||||
"output": output,
|
||||
}
|
||||
|
||||
|
||||
# ---------------------- Entry Point ----------------------
|
||||
@@ -469,5 +571,3 @@ if __name__ == "__main__":
|
||||
init_db()
|
||||
time.sleep(25)
|
||||
uvicorn.run(app, host="10.5.0.1", port=8888)
|
||||
|
||||
|
||||
|
||||
+34
-21
@@ -5,34 +5,47 @@ POSTGRES_PORT="${POSTGRES_PORT:-5432}"
|
||||
POSTGRES_ADMIN_USER="${POSTGRES_ADMIN_USER:-1gtT0sf8klB9lDbYZD9}"
|
||||
POSTGRES_ADMIN_PASSWORD="${POSTGRES_ADMIN_PASSWORD:-ZpSwWNafyy9GhY2gzHw}"
|
||||
|
||||
DB_PATH="/4server/data/contracts.db"
|
||||
|
||||
|
||||
get_contract_info() {
|
||||
|
||||
DB_PATH="/4server/data/contracts.db"
|
||||
echo "get_contract_info $UUID"
|
||||
|
||||
echo "get_contract_info $UUID"
|
||||
|
||||
while IFS="=" read -r key value; do
|
||||
# Single CTE: the table is scanned once and all projections come from it
|
||||
while IFS="=" read -r key value; do
|
||||
if [ -n "$key" ]; then
|
||||
export "$key=$value"
|
||||
fi
|
||||
done < <(sqlite3 "$DB_PATH" "
|
||||
SELECT 'UUID=' || UUID FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'EMAIL=' || email FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'EXPIRES=' || expires FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'TAGS=' || replace(replace(tags, char(10), ''), char(13), '') FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'ENV=' || replace(replace(env, char(10), ''), char(13), '') FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'IMAGE=' || image FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'DOMAINS=' || domains FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'STATUS=' || status FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'CREATED=' || created FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'SECRET=' || secret FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'CONTAINERDBID=' || id FROM containers WHERE UUID='$UUID'
|
||||
UNION ALL SELECT 'BUMP=' || bump FROM containers WHERE UUID='$UUID';
|
||||
")
|
||||
|
||||
|
||||
eval $(echo "$ENV" | jq -r 'to_entries | .[] | "export \(.key | ascii_upcase)=\(.value)"')
|
||||
done < <(sqlite3 "$DB_PATH" "
|
||||
WITH c AS (SELECT * FROM containers WHERE UUID='$UUID' LIMIT 1)
|
||||
SELECT 'UUID=' || COALESCE(UUID, '') FROM c UNION ALL
|
||||
SELECT 'EXPIRES=' || COALESCE(expires, '') FROM c UNION ALL
|
||||
SELECT 'IMAGE=' || COALESCE(image, '') FROM c UNION ALL
|
||||
SELECT 'STATUS=' || COALESCE(status, '') FROM c UNION ALL
|
||||
SELECT 'CREATED=' || COALESCE(created, '') FROM c UNION ALL
|
||||
SELECT 'SECRET_LIST=' || COALESCE(secret_list, '') FROM c UNION ALL
|
||||
SELECT 'CONTAINERDBID=' || COALESCE(id, '') FROM c UNION ALL
|
||||
SELECT 'GIT=' || COALESCE(git, '') FROM c UNION ALL
|
||||
SELECT 'BACKUP_SLOTS=' || COALESCE(backup_slots, '') FROM c UNION ALL
|
||||
SELECT 'HDD=' || COALESCE(hdd, '') FROM c UNION ALL
|
||||
SELECT 'WORKERS=' || COALESCE(workers, '') FROM c UNION ALL
|
||||
SELECT 'DOMAINS_SLOTS=' || COALESCE(domains_slots, '') FROM c;
|
||||
")
|
||||
|
||||
# Parse SECRET_LIST: backslash-separated key=value pairs (e.g. worex1=1\pswl=22)
|
||||
# - printf avoids echo interpreting escape sequences
|
||||
# - tr -d '\r\n' strips any embedded newlines the DB value may contain
|
||||
# - tr '\\' '\n' turns each backslash separator into a real newline
|
||||
# - %%=* / #*= split on the FIRST = only, so base64 values like tok=ab== are safe
|
||||
if [ -n "$SECRET_LIST" ]; then
|
||||
while IFS= read -r pair || [ -n "$pair" ]; do
|
||||
_key="${pair%%=*}"
|
||||
_value="${pair#*=}"
|
||||
if [ -n "$_key" ]; then
|
||||
export "$_key=$_value"
|
||||
fi
|
||||
done < <(printf '%s' "$SECRET_LIST" | tr -d '\r\n' | tr '\\' '\n')
|
||||
fi
|
||||
export ODOO_DB_PASSWORD=$psql
|
||||
}
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
sqlite3 /4server/data/contracts.db <<EOF
|
||||
ALTER TABLE containers ADD COLUMN env_list TEXT;
|
||||
UPDATE containers
|
||||
SET env_list = (
|
||||
SELECT group_concat(key || '=' || value, ', ')
|
||||
FROM json_each(containers.env)
|
||||
);
|
||||
ALTER TABLE containers ADD COLUMN secret_list TEXT;
|
||||
UPDATE containers
|
||||
SET secret_list = (
|
||||
SELECT group_concat(key || '=' || value, ', ')
|
||||
FROM json_each(containers.secret)
|
||||
);
|
||||
EOF
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: ./odoo_passwd.sh <container_name>
|
||||
|
||||
CONTAINER=$1
|
||||
|
||||
if [ -z "$CONTAINER" ]; then
|
||||
echo "Usage: $0 <docker_container>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 1: Get users list
|
||||
echo "Fetching users..."
|
||||
|
||||
USERS=$(doas docker exec -i "$CONTAINER" bash -c \
|
||||
'PGPASSWORD="$PASSWORD" psql -h "$HOST" -U "$USER" -At -F"," -c "SELECT id, login FROM res_users ORDER BY id;"')
|
||||
|
||||
# Step 2: Display users
|
||||
echo "Select a user:"
|
||||
IFS=$'\n'
|
||||
select USER_LINE in $USERS; do
|
||||
if [ -n "$USER_LINE" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
USER_ID=$(echo "$USER_LINE" | cut -d',' -f1)
|
||||
USER_LOGIN=$(echo "$USER_LINE" | cut -d',' -f2)
|
||||
|
||||
echo "Selected user: $USER_LOGIN (ID: $USER_ID)"
|
||||
|
||||
# Step 3: Ask for new password
|
||||
read -s -p "Enter new password: " NEW_PASS
|
||||
echo
|
||||
read -s -p "Confirm password: " CONFIRM_PASS
|
||||
echo
|
||||
|
||||
if [ "$NEW_PASS" != "$CONFIRM_PASS" ]; then
|
||||
echo "Passwords do not match!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 4: Generate Odoo-compatible hash using Python inside container
|
||||
HASH=$(doas docker exec -i "$CONTAINER" python3 - <<EOF
|
||||
from passlib.context import CryptContext
|
||||
ctx = CryptContext(schemes=['pbkdf2_sha512'])
|
||||
print(ctx.hash("$NEW_PASS"))
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "Updating password..."
|
||||
|
||||
# Step 5: Update DB
|
||||
doas docker exec -i "$CONTAINER" bash -c \
|
||||
"PGPASSWORD=\"$PASSWORD\" psql -h \"$HOST\" -U \"$USER\" -c \"UPDATE res_users SET password='$HASH' WHERE id=$USER_ID;\""
|
||||
|
||||
echo "Password updated successfully for $USER_LOGIN"
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
doas docker exec -it $1 bash -c 'PGPASSWORD="$PASSWORD" psql -h "$HOST" -U "$USER"'
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check that a search string was provided
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 <search_string>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SEARCH="$1"
|
||||
DB_PATH="/4server/data/contracts.db"
|
||||
|
||||
# Get all UUIDs where tags include the search string
|
||||
uuids=$(sqlite3 "$DB_PATH" "SELECT UUID FROM containers WHERE tags LIKE '%$SEARCH%';")
|
||||
|
||||
# Loop through each UUID
|
||||
for uuid in $uuids; do
|
||||
# Check if a container with this name is running
|
||||
if doas docker ps --format '{{.Names}}' | grep -qx "$uuid"; then
|
||||
echo "Restarting $uuid"
|
||||
/4server/sbin/startContainer $uuid
|
||||
fi
|
||||
done
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
doas docker exec -it $1 bash -c 'PGPASSWORD="$PASSWORD" psql -h "$HOST" -U "$USER"'
|
||||
@@ -10,10 +10,10 @@ UUID="${UUID:-default}"
|
||||
BRANCH="${BRANCH:-release}"
|
||||
|
||||
ODOO_DB_USER="${UUID}"
|
||||
export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql')
|
||||
#export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql')
|
||||
|
||||
|
||||
echo "ENV: $HDD $DOMAIN_COUNT $BACKUP_SLOTS $CONTAINERDBID"
|
||||
echo "ENV: $HDD $DOMAIN_SLOTS $BACKUP_SLOTS $CONTAINERDBID"
|
||||
echo "DBID: $CONTAINERDBID"
|
||||
|
||||
BASEURL="${BASEURL:-/4server/data/$UUID}"
|
||||
@@ -101,7 +101,7 @@ docker run -d --name "$UUID" \
|
||||
-e PASSWORD="$ODOO_DB_PASSWORD" \
|
||||
-e UUID="$UUID" \
|
||||
-e HDD="$HDD" \
|
||||
-e DOMAIN_COUNT="$DOMAIN_COUNT" \
|
||||
-e DOMAIN_SLOTS="$DOMAIN_SLOTS" \
|
||||
-e BACKUP_SLOTS="$BACKUP_SLOTS" \
|
||||
-e WORKER="$WORKER" \
|
||||
-e GIT="$GIT" \
|
||||
|
||||
@@ -10,10 +10,10 @@ UUID="${UUID:-default}"
|
||||
BRANCH="${BRANCH:-release}"
|
||||
|
||||
ODOO_DB_USER="${UUID}"
|
||||
export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql')
|
||||
#export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql')
|
||||
|
||||
|
||||
echo "ENV: $HDD $DOMAIN_COUNT $BACKUP_SLOTS $CONTAINERDBID"
|
||||
echo "ENV: $HDD $DOMAIN_SLOTS $BACKUP_SLOTS $CONTAINERDBID"
|
||||
echo "DBID: $CONTAINERDBID"
|
||||
|
||||
BASEURL="${BASEURL:-/4server/data/$UUID}"
|
||||
@@ -101,7 +101,7 @@ docker run -d --name "$UUID" \
|
||||
-e PASSWORD="$ODOO_DB_PASSWORD" \
|
||||
-e UUID="$UUID" \
|
||||
-e HDD="$HDD" \
|
||||
-e DOMAIN_COUNT="$DOMAIN_COUNT" \
|
||||
-e DOMAIN_SLOTS="$DOMAIN_SLOTS" \
|
||||
-e BACKUP_SLOTS="$BACKUP_SLOTS" \
|
||||
-e WORKER="$WORKER" \
|
||||
-e GIT="$GIT" \
|
||||
|
||||
@@ -10,10 +10,10 @@ UUID="${UUID:-default}"
|
||||
BRANCH="${BRANCH:-release}"
|
||||
|
||||
ODOO_DB_USER="${UUID}"
|
||||
export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql')
|
||||
#export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql')
|
||||
echo "***** $ODOO_DB_PASSWORD"
|
||||
|
||||
|
||||
echo "ENV: $HDD $DOMAIN_COUNT $BACKUP_SLOTS $CONTAINERDBID"
|
||||
echo "ENV: $HDD $DOMAIN_SLOTS $BACKUP_SLOTS $CONTAINERDBID"
|
||||
echo "DBID: $CONTAINERDBID"
|
||||
|
||||
BASEURL="${BASEURL:-/4server/data/$UUID}"
|
||||
@@ -104,7 +104,7 @@ docker run -d --name "$UUID" \
|
||||
-e PASSWORD="$ODOO_DB_PASSWORD" \
|
||||
-e UUID="$UUID" \
|
||||
-e HDD="$HDD" \
|
||||
-e DOMAIN_COUNT="$DOMAIN_COUNT" \
|
||||
-e DOMAIN_SLOTS="$DOMAIN_SLOTS" \
|
||||
-e BACKUP_SLOTS="$BACKUP_SLOTS" \
|
||||
-e WORKER="$WORKER" \
|
||||
-e GIT="$GIT" \
|
||||
|
||||
@@ -15,16 +15,20 @@ if [[ -z "$UUID" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$UUID" =~ ^[0-9a-fA-F-]+$ ]]; then
|
||||
echo "ERROR: Invalid UUID format: $UUID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
DOMAIN_FILE="/4server/data/$UUID/etc/domain"
|
||||
DB_FILE="/4server/data/contracts.db"
|
||||
if [ -f "$DOMAIN_FILE" ]; then
|
||||
DOMAINS=$(paste -sd "," "$DOMAIN_FILE")
|
||||
sqlite3 "$DB_FILE" <<SQL
|
||||
UPDATE containers
|
||||
SET domains='$DOMAINS'
|
||||
WHERE UUID='$UUID';
|
||||
SQL
|
||||
# Escape single quotes for SQLite ('' is the standard SQLite escape for ')
|
||||
DOMAINS_SAFE="${DOMAINS//\'/\'\'}"
|
||||
UUID_SAFE="${UUID//\'/\'\'}"
|
||||
sqlite3 "$DB_FILE" "UPDATE containers SET domains='$DOMAINS_SAFE' WHERE UUID='$UUID_SAFE';"
|
||||
fi
|
||||
|
||||
|
||||
@@ -59,4 +63,3 @@ case "$SECOND_PART" in
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
Reference in New Issue
Block a user