2026-10-16 21:46:26 +02:00
|
|
|
"""Read-only PBS status and administrative connection management."""
|
|
|
|
|
|
|
|
|
|
from flask import Blueprint, jsonify, request
|
|
|
|
|
import auth_manager
|
|
|
|
|
import pbs_manager as pbs
|
|
|
|
|
from jwt_middleware import require_auth, require_admin_scope
|
|
|
|
|
|
|
|
|
|
pbs_bp = Blueprint("pbs", __name__)
|
|
|
|
|
|
|
|
|
|
|
2026-10-20 07:38:20 +00:00
|
|
|
def pbs_error(exc, status):
|
|
|
|
|
return jsonify(error=str(exc), **exc.problem()), status
|
|
|
|
|
|
|
|
|
|
|
2026-10-16 21:46:26 +02:00
|
|
|
@pbs_bp.after_request
|
|
|
|
|
def no_cache(response):
|
|
|
|
|
response.headers["Cache-Control"] = "no-store"
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pbs_bp.route("/api/platform")
|
|
|
|
|
@require_auth
|
|
|
|
|
def platform():
|
|
|
|
|
return jsonify(pbs.get_platform())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pbs_bp.route("/api/pbs/servers")
|
|
|
|
|
@require_auth
|
|
|
|
|
def servers():
|
|
|
|
|
try:
|
2026-10-19 18:18:18 +02:00
|
|
|
servers = pbs.public_servers()
|
|
|
|
|
return jsonify(servers=servers, local_available=pbs.local_available(), detected=_detected(servers))
|
2026-10-16 21:46:26 +02:00
|
|
|
except pbs.PBSError as exc:
|
2026-10-20 07:38:20 +00:00
|
|
|
return pbs_error(exc, 500)
|
2026-10-16 21:46:26 +02:00
|
|
|
|
|
|
|
|
|
2026-10-19 18:18:18 +02:00
|
|
|
def _detected(servers):
|
|
|
|
|
"""PBS storages of this PVE that have no monitoring connection yet."""
|
|
|
|
|
if pbs.is_pbs_host():
|
|
|
|
|
return []
|
|
|
|
|
import monitor_links
|
|
|
|
|
from urllib.parse import urlsplit
|
|
|
|
|
known = {urlsplit(row.get("url", "")).hostname for row in servers}
|
|
|
|
|
found = {}
|
|
|
|
|
for row in monitor_links._storage_pbs():
|
|
|
|
|
host = row["server"]
|
|
|
|
|
if host in known or monitor_links._is_this_host(host):
|
|
|
|
|
continue
|
|
|
|
|
port = row.get("port") if str(row.get("port", "")).isdigit() else monitor_links.PBS_UI_PORT
|
|
|
|
|
url = monitor_links._host_url(host, port, "https")
|
|
|
|
|
found.setdefault(url, {"name": row["name"], "url": url, "fingerprint": row.get("fingerprint", "")})
|
|
|
|
|
return list(found.values())
|
|
|
|
|
|
|
|
|
|
|
2026-10-16 21:46:26 +02:00
|
|
|
def _protected_configuration():
|
|
|
|
|
local = auth_manager.load_auth_config()
|
|
|
|
|
return local.get("enabled") and not local.get("declined") and local.get("password_hash")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pbs_bp.route("/api/pbs/servers", methods=["POST"])
|
|
|
|
|
@require_admin_scope
|
|
|
|
|
def save_server():
|
|
|
|
|
if not _protected_configuration():
|
2026-10-20 07:38:20 +00:00
|
|
|
return jsonify(error="Enable local authentication before saving remote PBS credentials.", code="pbs_auth_required"), 409
|
2026-10-16 21:46:26 +02:00
|
|
|
try:
|
|
|
|
|
identifier = pbs.save_server(request.get_json(silent=True))
|
|
|
|
|
return jsonify(success=True, id=identifier, servers=pbs.public_servers())
|
|
|
|
|
except pbs.PBSError as exc:
|
2026-10-20 07:38:20 +00:00
|
|
|
return pbs_error(exc, 400)
|
2026-10-16 21:46:26 +02:00
|
|
|
except OSError:
|
2026-10-20 07:38:20 +00:00
|
|
|
return jsonify(error="Could not save PBS connection.", code="pbs_save_failed"), 500
|
2026-10-16 21:46:26 +02:00
|
|
|
|
|
|
|
|
|
2026-10-19 23:12:31 +02:00
|
|
|
@pbs_bp.route("/api/pbs/servers/login", methods=["POST"])
|
|
|
|
|
@require_admin_scope
|
|
|
|
|
def connect_with_password():
|
|
|
|
|
if not _protected_configuration():
|
2026-10-20 07:38:20 +00:00
|
|
|
return jsonify(error="Enable local authentication before saving remote PBS credentials.", code="pbs_auth_required"), 409
|
2026-10-19 23:12:31 +02:00
|
|
|
try:
|
|
|
|
|
identifier = pbs.connect_with_password(request.get_json(silent=True))
|
|
|
|
|
return jsonify(success=True, id=identifier, servers=pbs.public_servers())
|
|
|
|
|
except pbs.PBSError as exc:
|
2026-10-20 07:38:20 +00:00
|
|
|
return pbs_error(exc, 400)
|
2026-10-19 23:12:31 +02:00
|
|
|
except OSError:
|
2026-10-20 07:38:20 +00:00
|
|
|
return jsonify(error="Could not save PBS connection.", code="pbs_save_failed"), 500
|
2026-10-19 23:12:31 +02:00
|
|
|
|
|
|
|
|
|
2026-10-16 21:46:26 +02:00
|
|
|
@pbs_bp.route("/api/pbs/servers/<identifier>", methods=["DELETE"])
|
|
|
|
|
@require_admin_scope
|
|
|
|
|
def delete_server(identifier):
|
|
|
|
|
if not _protected_configuration():
|
2026-10-20 07:38:20 +00:00
|
|
|
return jsonify(error="Enable local authentication before changing PBS connections.", code="pbs_auth_required"), 409
|
2026-10-16 21:46:26 +02:00
|
|
|
try:
|
|
|
|
|
pbs.delete_server(identifier)
|
|
|
|
|
return jsonify(success=True)
|
|
|
|
|
except pbs.PBSError as exc:
|
2026-10-20 07:38:20 +00:00
|
|
|
return pbs_error(exc, 400)
|
2026-10-16 21:46:26 +02:00
|
|
|
except OSError:
|
2026-10-20 07:38:20 +00:00
|
|
|
return jsonify(error="Could not remove PBS connection.", code="pbs_remove_failed"), 500
|
2026-10-16 21:46:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pbs_bp.route("/api/pbs/status")
|
|
|
|
|
@require_auth
|
|
|
|
|
def status():
|
|
|
|
|
try:
|
|
|
|
|
return jsonify(pbs.get_status(request.args.get("server", "local")))
|
|
|
|
|
except pbs.PBSError as exc:
|
2026-10-20 07:38:20 +00:00
|
|
|
return pbs_error(exc, 502)
|