added the official docker cli
All checks were successful
Build and Push Docker Image / build (push) Successful in 39s
All checks were successful
Build and Push Docker Image / build (push) Successful in 39s
-switched to user 1000 for security. -added user to docker group -properly mounted btrfs drive on host allows users to create snapshots
This commit is contained in:
@@ -3,41 +3,81 @@ import cgi
|
||||
import cgitb
|
||||
import os
|
||||
import json
|
||||
import glob
|
||||
import sys
|
||||
import traceback
|
||||
import tempfile
|
||||
|
||||
cgitb.enable()
|
||||
print("Content-Type: application/json\n")
|
||||
|
||||
ENV_FILE = "/config/backupbot.env"
|
||||
ENV_FILE = "/config/backupbot.conf"
|
||||
ZONEINFO_DIR = "/usr/share/zoneinfo"
|
||||
|
||||
# Logging level from environment
|
||||
LOG_LEVEL = os.environ.get("BACKUPBOT_WEB_LOGGING", "info").lower()
|
||||
LOG_LEVELS = {"debug": 3, "info": 2, "warn": 1}
|
||||
|
||||
|
||||
def log(level, message, exc=None):
|
||||
"""
|
||||
Docker-friendly logging.
|
||||
level: "debug", "info", "warn"
|
||||
exc: exception object (only used in debug)
|
||||
"""
|
||||
if LOG_LEVELS.get(level, 0) <= LOG_LEVELS.get(LOG_LEVEL, 0):
|
||||
timestamp = (
|
||||
__import__("datetime")
|
||||
.datetime.now()
|
||||
.strftime(
|
||||
"%Y-%m-%d \
|
||||
%H:%M:%S"
|
||||
)
|
||||
)
|
||||
msg = f"[{timestamp}] [{level.upper()}] {message}"
|
||||
print(msg, file=sys.stderr)
|
||||
if exc and LOG_LEVEL == "debug":
|
||||
traceback.print_exception(
|
||||
type(exc), exc, exc.__traceback__, file=sys.stderr
|
||||
)
|
||||
|
||||
|
||||
def read_env():
|
||||
env = {}
|
||||
if os.path.exists(ENV_FILE):
|
||||
with open(ENV_FILE) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, val = line.split("=", 1)
|
||||
key = key.strip()
|
||||
val = val.strip().split("#")[0].strip()
|
||||
env[key] = val
|
||||
try:
|
||||
with open(ENV_FILE) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or "=" not in line:
|
||||
continue
|
||||
key, val = line.split("=", 1)
|
||||
env[key.strip()] = val.strip()
|
||||
except Exception as e:
|
||||
log("warn", f"Failed to read config: {e}", e)
|
||||
return env
|
||||
|
||||
|
||||
def write_env(env):
|
||||
with open(ENV_FILE, "w") as f:
|
||||
for key, val in env.items():
|
||||
f.write(f"{key}={val}\n")
|
||||
try:
|
||||
dir_name = os.path.dirname(ENV_FILE)
|
||||
os.makedirs(dir_name, exist_ok=True)
|
||||
# Write atomically to temp file
|
||||
with tempfile.NamedTemporaryFile("w", dir=dir_name, delete=False) as tmp:
|
||||
for key, val in env.items():
|
||||
tmp.write(f"{key}={val}\n")
|
||||
temp_name = tmp.name
|
||||
os.replace(temp_name, ENV_FILE)
|
||||
log("info", f"Configuration saved to {ENV_FILE}")
|
||||
except Exception as e:
|
||||
log("warn", f"Failed to write config: {e}", e)
|
||||
raise
|
||||
|
||||
|
||||
def list_timezones():
|
||||
zones = []
|
||||
for root, _, files in os.walk(ZONEINFO_DIR):
|
||||
rel_root = os.path.relpath(root, ZONEINFO_DIR)
|
||||
if rel_root.startswith("posix") or rel_root.startswith("right"):
|
||||
if rel_root.startswith(("posix", "right")):
|
||||
continue
|
||||
for file in files:
|
||||
if file.startswith(".") or file.endswith((".tab", ".zi")):
|
||||
@@ -49,18 +89,27 @@ def list_timezones():
|
||||
form = cgi.FieldStorage()
|
||||
action = form.getvalue("action")
|
||||
|
||||
if action == "get":
|
||||
print(json.dumps(read_env()))
|
||||
elif action == "set":
|
||||
try:
|
||||
raw = os.environ.get("CONTENT_LENGTH")
|
||||
length = int(raw) if raw else 0
|
||||
try:
|
||||
if action == "get":
|
||||
env = read_env()
|
||||
log("debug", f"Returning configuration: {env}")
|
||||
print(json.dumps(env))
|
||||
elif action == "set":
|
||||
raw_len = os.environ.get("CONTENT_LENGTH")
|
||||
length = int(raw_len) if raw_len else 0
|
||||
data = json.loads(os.read(0, length))
|
||||
write_env(data)
|
||||
log("debug", f"Received new configuration: {data}")
|
||||
env = read_env()
|
||||
env.update(data) # update existing keys, add new keys
|
||||
write_env(env)
|
||||
print(json.dumps({"status": "ok", "message": "Configuration saved."}))
|
||||
except Exception as e:
|
||||
print(json.dumps({"status": "error", "message": str(e)}))
|
||||
elif action == "get_timezones":
|
||||
print(json.dumps({"timezones": list_timezones()}))
|
||||
else:
|
||||
print(json.dumps({"status": "error", "message": "Invalid action"}))
|
||||
elif action == "get_timezones":
|
||||
zones = list_timezones()
|
||||
log("debug", f"Returning {len(zones)} timezones")
|
||||
print(json.dumps({"timezones": zones}))
|
||||
else:
|
||||
log("warn", f"Invalid action requested: {action}")
|
||||
print(json.dumps({"status": "error", "message": "Invalid action"}))
|
||||
except Exception as e:
|
||||
log("warn", f"Unhandled exception: {e}", e)
|
||||
print(json.dumps({"status": "error", "message": str(e)}))
|
||||
|
||||
Reference in New Issue
Block a user