refactor: update all API blueprints (certs, dhcp, firewall, logs, network, proxy, wireguard)
This commit is contained in:
+18
-8
@@ -8,6 +8,16 @@ import logging
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from daemon.client import BadRequest, NotFound, delete, get, post
|
from daemon.client import BadRequest, NotFound, delete, get, post
|
||||||
|
from daemon.iface import (
|
||||||
|
DELETE_ACME_REMOVE,
|
||||||
|
GET_ACME_INFO,
|
||||||
|
GET_ACME_ISSUE_STATUS,
|
||||||
|
GET_ACME_LIST,
|
||||||
|
POST_ACME_EMAIL,
|
||||||
|
POST_ACME_ISSUE,
|
||||||
|
POST_ACME_RENEW,
|
||||||
|
POST_ACME_VALIDATE,
|
||||||
|
)
|
||||||
from webui.api.common import _error, _ok
|
from webui.api.common import _error, _ok
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -22,7 +32,7 @@ def list_certs_bp():
|
|||||||
Response containing the list of certificates or an error message.
|
Response containing the list of certificates or an error message.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/acme/list"))
|
return _ok(get(GET_ACME_LIST))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to list certificates: %s", exc)
|
logger.error("Failed to list certificates: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -39,7 +49,7 @@ def cert_details(domain: str):
|
|||||||
Response containing certificate info or an error message.
|
Response containing certificate info or an error message.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/acme/info", {"domain": domain}))
|
return _ok(get(GET_ACME_INFO, {"domain": domain}))
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
logger.info("Cert for '%s' not found: %s", domain, exc)
|
logger.info("Cert for '%s' not found: %s", domain, exc)
|
||||||
return _error(str(exc), 404)
|
return _error(str(exc), 404)
|
||||||
@@ -62,7 +72,7 @@ def validate():
|
|||||||
if not domain:
|
if not domain:
|
||||||
return _error("'domain' is required", 400)
|
return _error("'domain' is required", 400)
|
||||||
try:
|
try:
|
||||||
result = post("/acme/validate", {"domain": domain})
|
result = post(POST_ACME_VALIDATE, {"domain": domain})
|
||||||
return _ok(result)
|
return _ok(result)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
logger.info("Validation rejected: %s", exc)
|
logger.info("Validation rejected: %s", exc)
|
||||||
@@ -90,7 +100,7 @@ def issue_start():
|
|||||||
try:
|
try:
|
||||||
logger.info("Certificate issuance requested for '%s' via API", domain)
|
logger.info("Certificate issuance requested for '%s' via API", domain)
|
||||||
result = post(
|
result = post(
|
||||||
"/acme/issue", {"domain": domain, "webroot": webroot, "email": email}
|
POST_ACME_ISSUE, {"domain": domain, "webroot": webroot, "email": email}
|
||||||
)
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Certificate issuance started for '%s' (id=%s)",
|
"Certificate issuance started for '%s' (id=%s)",
|
||||||
@@ -117,7 +127,7 @@ def issue_status(request_id: str):
|
|||||||
Response containing issuance status or an error message.
|
Response containing issuance status or an error message.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
result = get("/acme/issue/status", {"id": request_id})
|
result = get(GET_ACME_ISSUE_STATUS, {"id": request_id})
|
||||||
return _ok(result)
|
return _ok(result)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
logger.info("Issuance request '%s' not found: %s", request_id, exc)
|
logger.info("Issuance request '%s' not found: %s", request_id, exc)
|
||||||
@@ -139,7 +149,7 @@ def renew_bp(domain: str):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
logger.info("Certificate renewal requested for '%s' via API", domain)
|
logger.info("Certificate renewal requested for '%s' via API", domain)
|
||||||
post("/acme/renew", {"domain": domain})
|
post(POST_ACME_RENEW, {"domain": domain})
|
||||||
logger.info("Certificate renewed for '%s'", domain)
|
logger.info("Certificate renewed for '%s'", domain)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -161,7 +171,7 @@ def remove_bp(domain: str):
|
|||||||
Response confirming removal or an error message.
|
Response confirming removal or an error message.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete("/acme/remove", {"domain": domain})
|
delete(DELETE_ACME_REMOVE, {"domain": domain})
|
||||||
logger.info("Certificate removed for '%s' via API", domain)
|
logger.info("Certificate removed for '%s' via API", domain)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
@@ -186,7 +196,7 @@ def set_email_bp():
|
|||||||
if not email:
|
if not email:
|
||||||
return _error("'email' is required", 400)
|
return _error("'email' is required", 400)
|
||||||
try:
|
try:
|
||||||
post("/acme/email", {"email": email})
|
post(POST_ACME_EMAIL, {"email": email})
|
||||||
logger.info("ACME email set via API: %s", email)
|
logger.info("ACME email set via API: %s", email)
|
||||||
return _ok({"email": email})
|
return _ok({"email": email})
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
|
|||||||
+29
-12
@@ -8,6 +8,20 @@ import logging
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
||||||
|
from daemon.iface import (
|
||||||
|
DELETE_DNSMASQ_DNS_RECORD_REMOVE,
|
||||||
|
DELETE_DNSMASQ_RANGES_REMOVE,
|
||||||
|
DELETE_DNSMASQ_STATIC_LEASE_REMOVE,
|
||||||
|
GET_DNSMASQ_CONFIG,
|
||||||
|
GET_DNSMASQ_LEASES,
|
||||||
|
GET_DNSMASQ_STATUS,
|
||||||
|
PATCH_DNSMASQ_CONFIG,
|
||||||
|
POST_DNSMASQ_APPLY,
|
||||||
|
POST_DNSMASQ_CONFIG,
|
||||||
|
POST_DNSMASQ_DNS_RECORD_ADD,
|
||||||
|
POST_DNSMASQ_RANGES_ADD,
|
||||||
|
POST_DNSMASQ_STATIC_LEASE_ADD,
|
||||||
|
)
|
||||||
from webui.api.common import _error, _ok
|
from webui.api.common import _error, _ok
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -27,7 +41,7 @@ def get_config_bp():
|
|||||||
JSON response with the config or an error.
|
JSON response with the config or an error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/dnsmasq/config"))
|
return _ok(get(GET_DNSMASQ_CONFIG))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to read DHCP config: %s", exc)
|
logger.error("Failed to read DHCP config: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -47,7 +61,7 @@ def post_config():
|
|||||||
if not isinstance(body, dict):
|
if not isinstance(body, dict):
|
||||||
return _error("Request body must be a JSON object", 400)
|
return _error("Request body must be a JSON object", 400)
|
||||||
try:
|
try:
|
||||||
post("/dnsmasq/config", body)
|
post(POST_DNSMASQ_CONFIG, body)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
logger.info("DHCP config save rejected: %s", exc)
|
logger.info("DHCP config save rejected: %s", exc)
|
||||||
@@ -71,7 +85,7 @@ def patch_config():
|
|||||||
if not isinstance(body, dict):
|
if not isinstance(body, dict):
|
||||||
return _error("Request body must be a JSON object", 400)
|
return _error("Request body must be a JSON object", 400)
|
||||||
try:
|
try:
|
||||||
patch("/dnsmasq/config", body)
|
patch(PATCH_DNSMASQ_CONFIG, body)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
logger.info("DHCP config patch rejected: %s", exc)
|
logger.info("DHCP config patch rejected: %s", exc)
|
||||||
@@ -86,7 +100,7 @@ def apply_bp():
|
|||||||
"""POST /api/dhcp/apply — Apply the current dnsmasq configuration to the running service."""
|
"""POST /api/dhcp/apply — Apply the current dnsmasq configuration to the running service."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
post("/dnsmasq/apply")
|
post(POST_DNSMASQ_APPLY)
|
||||||
logger.info("dnsmasq config applied via API")
|
logger.info("dnsmasq config applied via API")
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -104,7 +118,7 @@ def status_bp():
|
|||||||
"""GET /api/dhcp/status — Retrieve dnsmasq service status."""
|
"""GET /api/dhcp/status — Retrieve dnsmasq service status."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _ok(get("/dnsmasq/status"))
|
return _ok(get(GET_DNSMASQ_STATUS))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to get DHCP status: %s", exc)
|
logger.error("Failed to get DHCP status: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -134,7 +148,7 @@ def add_range_bp():
|
|||||||
return _error("'start' and 'end' are required", 400)
|
return _error("'start' and 'end' are required", 400)
|
||||||
try:
|
try:
|
||||||
post(
|
post(
|
||||||
"/dnsmasq/ranges/add",
|
POST_DNSMASQ_RANGES_ADD,
|
||||||
{
|
{
|
||||||
"interface": iface or "",
|
"interface": iface or "",
|
||||||
"start": start,
|
"start": start,
|
||||||
@@ -170,7 +184,8 @@ def remove_range_bp():
|
|||||||
return _error("'start' and 'end' are required", 400)
|
return _error("'start' and 'end' are required", 400)
|
||||||
try:
|
try:
|
||||||
delete(
|
delete(
|
||||||
"/dnsmasq/ranges/remove", {"interface": iface, "start": start, "end": end}
|
DELETE_DNSMASQ_RANGES_REMOVE,
|
||||||
|
{"interface": iface, "start": start, "end": end},
|
||||||
)
|
)
|
||||||
logger.info("DHCP range removed via API: %s-%s", start, end)
|
logger.info("DHCP range removed via API: %s-%s", start, end)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
@@ -192,7 +207,7 @@ def leases_bp():
|
|||||||
"""GET /api/dhcp/leases — Retrieve the current DHCP lease table."""
|
"""GET /api/dhcp/leases — Retrieve the current DHCP lease table."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _ok(get("/dnsmasq/leases"))
|
return _ok(get(GET_DNSMASQ_LEASES))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to read lease table: %s", exc)
|
logger.error("Failed to read lease table: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -220,7 +235,9 @@ def add_static_lease_bp():
|
|||||||
if not mac or not ip:
|
if not mac or not ip:
|
||||||
return _error("'mac' and 'ip' are required", 400)
|
return _error("'mac' and 'ip' are required", 400)
|
||||||
try:
|
try:
|
||||||
post("/dnsmasq/static-lease/add", {"mac": mac, "ip": ip, "hostname": hostname})
|
post(
|
||||||
|
POST_DNSMASQ_STATIC_LEASE_ADD, {"mac": mac, "ip": ip, "hostname": hostname}
|
||||||
|
)
|
||||||
logger.info("Static lease added via API: %s -> %s", mac, ip)
|
logger.info("Static lease added via API: %s -> %s", mac, ip)
|
||||||
return _ok({"mac": mac, "ip": ip, "hostname": hostname})
|
return _ok({"mac": mac, "ip": ip, "hostname": hostname})
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -242,7 +259,7 @@ def remove_static_lease_bp(mac):
|
|||||||
JSON response with success status or an error.
|
JSON response with success status or an error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete("/dnsmasq/static-lease/remove", {"mac": mac})
|
delete(DELETE_DNSMASQ_STATIC_LEASE_REMOVE, {"mac": mac})
|
||||||
logger.info("Static lease removed via API: %s", mac)
|
logger.info("Static lease removed via API: %s", mac)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
@@ -276,7 +293,7 @@ def add_dns_record_bp():
|
|||||||
return _error("'name' and 'address' are required", 400)
|
return _error("'name' and 'address' are required", 400)
|
||||||
try:
|
try:
|
||||||
post(
|
post(
|
||||||
"/dnsmasq/dns-record/add",
|
POST_DNSMASQ_DNS_RECORD_ADD,
|
||||||
{"name": name, "address": address, "hostname": hostname},
|
{"name": name, "address": address, "hostname": hostname},
|
||||||
)
|
)
|
||||||
logger.info("DNS record added via API: %s -> %s", name, address)
|
logger.info("DNS record added via API: %s -> %s", name, address)
|
||||||
@@ -300,7 +317,7 @@ def remove_dns_record_bp(name):
|
|||||||
JSON response with success status or an error.
|
JSON response with success status or an error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete("/dnsmasq/dns-record/remove", {"name": name})
|
delete(DELETE_DNSMASQ_DNS_RECORD_REMOVE, {"name": name})
|
||||||
logger.info("DNS record removed via API: %s", name)
|
logger.info("DNS record removed via API: %s", name)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
|
|||||||
+42
-21
@@ -8,6 +8,27 @@ import logging
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
||||||
|
from daemon.iface import (
|
||||||
|
DELETE_FIREWALL_FORWARD_PORT_REMOVE,
|
||||||
|
DELETE_FIREWALL_RICH_RULES_REMOVE,
|
||||||
|
DELETE_FIREWALL_ZONES_DELETE,
|
||||||
|
GET_FIREWALL_CONFIG,
|
||||||
|
GET_FIREWALL_CONFIG_PENDING,
|
||||||
|
GET_FIREWALL_INTERFACES,
|
||||||
|
GET_FIREWALL_RICH_RULES,
|
||||||
|
GET_FIREWALL_SERVICES,
|
||||||
|
GET_FIREWALL_ZONES,
|
||||||
|
GET_FIREWALL_ZONES_INFO,
|
||||||
|
PATCH_FIREWALL_CONFIG,
|
||||||
|
POST_FIREWALL_CONFIG,
|
||||||
|
POST_FIREWALL_CONFIG_APPLY,
|
||||||
|
POST_FIREWALL_FORWARD_PORT_ADD,
|
||||||
|
POST_FIREWALL_MASQUERADE,
|
||||||
|
POST_FIREWALL_RICH_RULES_ADD,
|
||||||
|
POST_FIREWALL_ZONES_CREATE,
|
||||||
|
POST_FIREWALL_ZONES_INTERFACES,
|
||||||
|
POST_FIREWALL_ZONES_SERVICES,
|
||||||
|
)
|
||||||
from webui.api.common import _error, _ok
|
from webui.api.common import _error, _ok
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -32,7 +53,7 @@ def config_list():
|
|||||||
JSON response with the config data or an error message.
|
JSON response with the config data or an error message.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/firewall/config"))
|
return _ok(get(GET_FIREWALL_CONFIG))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to read firewall config: %s", exc)
|
logger.error("Failed to read firewall config: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -60,9 +81,9 @@ def config_save():
|
|||||||
if not isinstance(body["zones"], dict):
|
if not isinstance(body["zones"], dict):
|
||||||
return _error("'zones' must be a dict", 400)
|
return _error("'zones' must be a dict", 400)
|
||||||
try:
|
try:
|
||||||
post("/firewall/config", body)
|
post(POST_FIREWALL_CONFIG, body)
|
||||||
try:
|
try:
|
||||||
pending = get("/firewall/config/pending")
|
pending = get(GET_FIREWALL_CONFIG_PENDING)
|
||||||
pending_data = {
|
pending_data = {
|
||||||
"pending": pending.get("pending", []),
|
"pending": pending.get("pending", []),
|
||||||
"needs_apply": pending.get("needs_apply", False),
|
"needs_apply": pending.get("needs_apply", False),
|
||||||
@@ -106,9 +127,9 @@ def patch_config():
|
|||||||
if not isinstance(body, dict):
|
if not isinstance(body, dict):
|
||||||
return _error("Request body must be a JSON object", 400)
|
return _error("Request body must be a JSON object", 400)
|
||||||
try:
|
try:
|
||||||
patch("/firewall/config", body)
|
patch(PATCH_FIREWALL_CONFIG, body)
|
||||||
try:
|
try:
|
||||||
pending = get("/firewall/config/pending")
|
pending = get(GET_FIREWALL_CONFIG_PENDING)
|
||||||
pending_data = {
|
pending_data = {
|
||||||
"pending": pending.get("pending", []),
|
"pending": pending.get("pending", []),
|
||||||
"needs_apply": pending.get("needs_apply", False),
|
"needs_apply": pending.get("needs_apply", False),
|
||||||
@@ -145,7 +166,7 @@ def config_apply_bp():
|
|||||||
JSON with ``applied_zones`` list or an error message.
|
JSON with ``applied_zones`` list or an error message.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
result = post("/firewall/config/apply")
|
result = post(POST_FIREWALL_CONFIG_APPLY)
|
||||||
logger.info("Firewall config applied: %s", result.get("applied_zones", []))
|
logger.info("Firewall config applied: %s", result.get("applied_zones", []))
|
||||||
return _ok(result)
|
return _ok(result)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -167,7 +188,7 @@ def config_pending_bp():
|
|||||||
JSON with pending changes and apply status.
|
JSON with pending changes and apply status.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/firewall/config/pending"))
|
return _ok(get(GET_FIREWALL_CONFIG_PENDING))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to check pending config: %s", exc)
|
logger.error("Failed to check pending config: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -189,7 +210,7 @@ def list_zones():
|
|||||||
JSON with ``active`` zones dict and ``available`` zones list.
|
JSON with ``active`` zones dict and ``available`` zones list.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
data = get("/firewall/zones")
|
data = get(GET_FIREWALL_ZONES)
|
||||||
return _ok(
|
return _ok(
|
||||||
{"active": data.get("active", {}), "available": data.get("available", [])}
|
{"active": data.get("active", {}), "available": data.get("available", [])}
|
||||||
)
|
)
|
||||||
@@ -212,7 +233,7 @@ def zone_details(name: str):
|
|||||||
JSON with zone configuration details or 404 error.
|
JSON with zone configuration details or 404 error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
info = get("/firewall/zones/info", {"zone": name})
|
info = get(GET_FIREWALL_ZONES_INFO, {"zone": name})
|
||||||
return _ok(info)
|
return _ok(info)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
logger.info("Zone '%s' not found: %s", name, exc)
|
logger.info("Zone '%s' not found: %s", name, exc)
|
||||||
@@ -241,7 +262,7 @@ def create_zone_bp():
|
|||||||
if not zone_name:
|
if not zone_name:
|
||||||
return _error("Zone name is required", 400)
|
return _error("Zone name is required", 400)
|
||||||
try:
|
try:
|
||||||
post("/firewall/zones/create", {"name": zone_name, "target": target})
|
post(POST_FIREWALL_ZONES_CREATE, {"name": zone_name, "target": target})
|
||||||
logger.info("Zone '%s' created via API", zone_name)
|
logger.info("Zone '%s' created via API", zone_name)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -266,7 +287,7 @@ def delete_zone_bp(name: str):
|
|||||||
JSON confirmation or 404 if the zone does not exist.
|
JSON confirmation or 404 if the zone does not exist.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete("/firewall/zones/delete", {"zone": name})
|
delete(DELETE_FIREWALL_ZONES_DELETE, {"zone": name})
|
||||||
logger.info("Zone '%s' deleted via API", name)
|
logger.info("Zone '%s' deleted via API", name)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
@@ -303,7 +324,7 @@ def set_zone_interfaces_bp(name: str):
|
|||||||
if not isinstance(interfaces, list):
|
if not isinstance(interfaces, list):
|
||||||
return _error("'interfaces' must be a list", 400)
|
return _error("'interfaces' must be a list", 400)
|
||||||
try:
|
try:
|
||||||
post("/firewall/zones/interfaces", {"zone": name, "interfaces": interfaces})
|
post(POST_FIREWALL_ZONES_INTERFACES, {"zone": name, "interfaces": interfaces})
|
||||||
logger.info("Zone '%s' interfaces updated: %s", name, interfaces)
|
logger.info("Zone '%s' interfaces updated: %s", name, interfaces)
|
||||||
return _ok({"zone": name, "interfaces": interfaces})
|
return _ok({"zone": name, "interfaces": interfaces})
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -343,7 +364,7 @@ def set_zone_services_bp(name: str):
|
|||||||
if not isinstance(services, list):
|
if not isinstance(services, list):
|
||||||
return _error("'services' must be a list", 400)
|
return _error("'services' must be a list", 400)
|
||||||
try:
|
try:
|
||||||
post("/firewall/zones/services", {"zone": name, "services": services})
|
post(POST_FIREWALL_ZONES_SERVICES, {"zone": name, "services": services})
|
||||||
return _ok({"zone": name, "services": services})
|
return _ok({"zone": name, "services": services})
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
logger.info("Set services for zone '%s' rejected: %s", name, exc)
|
logger.info("Set services for zone '%s' rejected: %s", name, exc)
|
||||||
@@ -372,7 +393,7 @@ def list_services():
|
|||||||
JSON with the list of available service names.
|
JSON with the list of available service names.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/firewall/services"))
|
return _ok(get(GET_FIREWALL_SERVICES))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to list services: %s", exc)
|
logger.error("Failed to list services: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -389,7 +410,7 @@ def list_interfaces():
|
|||||||
JSON with the list of available interface names.
|
JSON with the list of available interface names.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/firewall/interfaces"))
|
return _ok(get(GET_FIREWALL_INTERFACES))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to list interfaces: %s", exc)
|
logger.error("Failed to list interfaces: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -419,7 +440,7 @@ def add_rich_rule_bp():
|
|||||||
if not zone or not rule:
|
if not zone or not rule:
|
||||||
return _error("Both 'zone' and 'rule' are required", 400)
|
return _error("Both 'zone' and 'rule' are required", 400)
|
||||||
try:
|
try:
|
||||||
entry = post("/firewall/rich-rules/add", {"zone": zone, "rule": rule})
|
entry = post(POST_FIREWALL_RICH_RULES_ADD, {"zone": zone, "rule": rule})
|
||||||
logger.info("Rich rule added to zone '%s': %s", zone, rule[:80])
|
logger.info("Rich rule added to zone '%s': %s", zone, rule[:80])
|
||||||
return _ok({"zone": zone, "id": entry["id"], "rule": rule})
|
return _ok({"zone": zone, "id": entry["id"], "rule": rule})
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -444,7 +465,7 @@ def list_rich_rules(zone: str):
|
|||||||
JSON with list of rich rule entries for the zone.
|
JSON with list of rich rule entries for the zone.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/firewall/rich-rules", {"zone": zone}))
|
return _ok(get(GET_FIREWALL_RICH_RULES, {"zone": zone}))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to get rich rules for zone '%s': %s", zone, exc)
|
logger.error("Failed to get rich rules for zone '%s': %s", zone, exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -465,7 +486,7 @@ def remove_rich_rule_bp(zone: str, rule_id: str):
|
|||||||
JSON confirmation or 404 if the rule does not exist.
|
JSON confirmation or 404 if the rule does not exist.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete("/firewall/rich-rules/remove", {"zone": zone, "id": rule_id})
|
delete(DELETE_FIREWALL_RICH_RULES_REMOVE, {"zone": zone, "id": rule_id})
|
||||||
logger.info("Rich rule '%s' removed from zone '%s'", rule_id, zone)
|
logger.info("Rich rule '%s' removed from zone '%s'", rule_id, zone)
|
||||||
return _ok({"zone": zone, "id": rule_id})
|
return _ok({"zone": zone, "id": rule_id})
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
@@ -500,7 +521,7 @@ def set_masquerade_bp():
|
|||||||
if not zone or enable is None:
|
if not zone or enable is None:
|
||||||
return _error("'zone' and 'enable' (bool) are required", 400)
|
return _error("'zone' and 'enable' (bool) are required", 400)
|
||||||
try:
|
try:
|
||||||
post("/firewall/masquerade", {"zone": zone, "enable": bool(enable)})
|
post(POST_FIREWALL_MASQUERADE, {"zone": zone, "enable": bool(enable)})
|
||||||
logger.info(
|
logger.info(
|
||||||
"Masquerade %s on zone '%s' via API",
|
"Masquerade %s on zone '%s' via API",
|
||||||
"enabled" if enable else "disabled",
|
"enabled" if enable else "disabled",
|
||||||
@@ -555,7 +576,7 @@ def add_forward_port_bp():
|
|||||||
toaddr_str = str(toaddr) if toaddr else None
|
toaddr_str = str(toaddr) if toaddr else None
|
||||||
try:
|
try:
|
||||||
entry = post(
|
entry = post(
|
||||||
"/firewall/forward-port/add",
|
POST_FIREWALL_FORWARD_PORT_ADD,
|
||||||
{
|
{
|
||||||
"zone": zone,
|
"zone": zone,
|
||||||
"port": port_int,
|
"port": port_int,
|
||||||
@@ -590,7 +611,7 @@ def remove_forward_port_bp(zone: str, port: int, proto: str):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete(
|
delete(
|
||||||
"/firewall/forward-port/remove",
|
DELETE_FIREWALL_FORWARD_PORT_REMOVE,
|
||||||
{"zone": zone, "port": port, "proto": proto},
|
{"zone": zone, "port": port, "proto": proto},
|
||||||
)
|
)
|
||||||
logger.info("Forward port %s/%s removed from zone '%s'", port, proto, zone)
|
logger.info("Forward port %s/%s removed from zone '%s'", port, proto, zone)
|
||||||
|
|||||||
+32
-76
@@ -1,116 +1,72 @@
|
|||||||
"""Log viewing API blueprint.
|
"""Log viewing API blueprint.
|
||||||
|
|
||||||
Serves log content to the /logs page via HTMX endpoints through vacuum-walld.
|
Wraps raw log text in the standard JSON response contract.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from flask import Blueprint, render_template_string
|
from flask import Blueprint
|
||||||
|
|
||||||
from daemon.client import get
|
from daemon.client import NotFound, get
|
||||||
|
from daemon.iface import (
|
||||||
|
GET_LOGS_APP,
|
||||||
|
GET_LOGS_DNSMASQ,
|
||||||
|
GET_LOGS_JOURNAL,
|
||||||
|
GET_LOGS_NGINX_ACCESS,
|
||||||
|
GET_LOGS_NGINX_ERROR,
|
||||||
|
)
|
||||||
|
from webui.api.common import _error, _ok
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
bp = Blueprint("logs", __name__)
|
bp = Blueprint("logs", __name__)
|
||||||
|
|
||||||
_LOG_LINE_TEMPLATE = """\
|
|
||||||
{% for line in lines %}
|
|
||||||
<div class="log-line{% if 'ERROR' in line %} log-error{% elif 'WARN' in line %} log-warn{% endif %}">{{ line | e }}</div>
|
|
||||||
{% endfor %}"""
|
|
||||||
|
|
||||||
|
|
||||||
def _render_log_lines(text: str) -> str:
|
|
||||||
"""Render raw log text into styled HTML log-line divs.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: Raw log content with newline-separated lines.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
HTML string with color-coded log-line elements.
|
|
||||||
"""
|
|
||||||
lines = text.rstrip("\n").split("\n") if text.strip() else []
|
|
||||||
return render_template_string(_LOG_LINE_TEMPLATE, lines=lines)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/journal")
|
@bp.route("/journal")
|
||||||
def journal():
|
def journal():
|
||||||
"""GET /api/logs/journal — Return systemd journal log lines.
|
"""GET /api/logs/journal — Return systemd journal log lines."""
|
||||||
|
|
||||||
Fetches the daemon's journal log content via vacuum-walld and
|
|
||||||
renders it as styled HTML log-line elements.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
HTML string containing rendered journal log lines.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
text = get("/logs/journal")
|
return _ok(get(GET_LOGS_JOURNAL))
|
||||||
return _render_log_lines(text)
|
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return _render_log_lines("(error reading journal)\n")
|
return _error("error reading journal", 500)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/nginx/access")
|
@bp.route("/nginx/access")
|
||||||
def nginx_access():
|
def nginx_access():
|
||||||
"""GET /api/logs/nginx/access — Return nginx access log lines.
|
"""GET /api/logs/nginx/access — Return nginx access log lines."""
|
||||||
|
|
||||||
Fetches the nginx access log content via vacuum-walld and
|
|
||||||
renders it as styled HTML log-line elements.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
HTML string containing rendered access log lines.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
text = get("/logs/nginx/access")
|
return _ok(get(GET_LOGS_NGINX_ACCESS))
|
||||||
return _render_log_lines(text)
|
except NotFound:
|
||||||
|
return _error("log file not found", 404)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return _render_log_lines("(log file not found)\n")
|
return _error("error reading log", 500)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/nginx/error")
|
@bp.route("/nginx/error")
|
||||||
def nginx_error():
|
def nginx_error():
|
||||||
"""GET /api/logs/nginx/error — Return nginx error log lines.
|
"""GET /api/logs/nginx/error — Return nginx error log lines."""
|
||||||
|
|
||||||
Fetches the nginx error log content via vacuum-walld and
|
|
||||||
renders it as styled HTML log-line elements.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
HTML string containing rendered error log lines.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
text = get("/logs/nginx/error")
|
return _ok(get(GET_LOGS_NGINX_ERROR))
|
||||||
return _render_log_lines(text)
|
except NotFound:
|
||||||
|
return _error("log file not found", 404)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return _render_log_lines("(log file not found)\n")
|
return _error("error reading log", 500)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/dnsmasq")
|
@bp.route("/dnsmasq")
|
||||||
def dnsmasq():
|
def dnsmasq():
|
||||||
"""GET /api/logs/dnsmasq — Return dnsmasq log lines.
|
"""GET /api/logs/dnsmasq — Return dnsmasq log lines."""
|
||||||
|
|
||||||
Fetches the dnsmasq log content via vacuum-walld and
|
|
||||||
renders it as styled HTML log-line elements.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
HTML string containing rendered dnsmasq log lines.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
text = get("/logs/dnsmasq")
|
return _ok(get(GET_LOGS_DNSMASQ))
|
||||||
return _render_log_lines(text)
|
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return _render_log_lines("(error reading journal)\n")
|
return _error("error reading journal", 500)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/app")
|
@bp.route("/app")
|
||||||
def app_log():
|
def app_log():
|
||||||
"""GET /api/logs/app — Return application log lines.
|
"""GET /api/logs/app — Return application log lines."""
|
||||||
|
|
||||||
Fetches the application log content via vacuum-walld and
|
|
||||||
renders it as styled HTML log-line elements.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
HTML string containing rendered application log lines.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
text = get("/logs/app")
|
return _ok(get(GET_LOGS_APP))
|
||||||
return _render_log_lines(text)
|
except NotFound:
|
||||||
|
return _error("log file not found", 404)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return _render_log_lines("(log file not found)\n")
|
return _error("error reading log", 500)
|
||||||
|
|||||||
+27
-8
@@ -9,6 +9,16 @@ import logging
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from daemon.client import NotFound, get, post
|
from daemon.client import NotFound, get, post
|
||||||
|
from daemon.iface import (
|
||||||
|
GET_NETWORK_INFER_DHCP_RANGES,
|
||||||
|
GET_NETWORK_INFER_ZONES,
|
||||||
|
GET_NETWORK_INTERFACE_NAME,
|
||||||
|
GET_NETWORK_INTERFACES,
|
||||||
|
POST_NETWORK_APPLY,
|
||||||
|
POST_NETWORK_INTERFACE_NAME,
|
||||||
|
POST_NETWORK_INTERFACE_RELOAD,
|
||||||
|
)
|
||||||
|
from lib.common import validate_interface_name
|
||||||
from webui.api.common import _error, _ok
|
from webui.api.common import _error, _ok
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -26,7 +36,7 @@ def list_interfaces():
|
|||||||
JSON with interface config + runtime state.
|
JSON with interface config + runtime state.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/network/interfaces"))
|
return _ok(get(GET_NETWORK_INTERFACES))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to list network interfaces: %s", exc)
|
logger.error("Failed to list network interfaces: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -43,7 +53,10 @@ def get_interface(name: str):
|
|||||||
JSON with interface config and runtime state.
|
JSON with interface config and runtime state.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/network/interfaces/" + name, {"name": name}))
|
validate_interface_name(name)
|
||||||
|
return _ok(get(GET_NETWORK_INTERFACE_NAME, {"name": name}))
|
||||||
|
except ValueError as exc:
|
||||||
|
return _error(str(exc), 400)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
logger.info("Interface '%s' not found: %s", name, exc)
|
logger.info("Interface '%s' not found: %s", name, exc)
|
||||||
return _error(str(exc), 404)
|
return _error(str(exc), 404)
|
||||||
@@ -65,11 +78,14 @@ def save_interface(name: str):
|
|||||||
Returns:
|
Returns:
|
||||||
JSON confirmation.
|
JSON confirmation.
|
||||||
"""
|
"""
|
||||||
body = request.get_json(silent=True) or {}
|
body = {**(request.get_json(silent=True) or {}), "name": name}
|
||||||
try:
|
try:
|
||||||
post("/network/interfaces/" + name, body)
|
validate_interface_name(name)
|
||||||
|
post(POST_NETWORK_INTERFACE_NAME, body)
|
||||||
logger.info("Interface '%s' config saved", name)
|
logger.info("Interface '%s' config saved", name)
|
||||||
return _ok({"name": name, "applied": True})
|
return _ok({"name": name, "applied": True})
|
||||||
|
except ValueError as exc:
|
||||||
|
return _error(str(exc), 400)
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
return _error(str(exc), 404)
|
return _error(str(exc), 404)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -88,9 +104,12 @@ def reload_interface(name: str):
|
|||||||
JSON confirmation.
|
JSON confirmation.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
post("/network/interfaces/" + name + "/reload", {"name": name})
|
validate_interface_name(name)
|
||||||
|
post(POST_NETWORK_INTERFACE_RELOAD, {"name": name})
|
||||||
logger.info("Interface '%s' reloaded", name)
|
logger.info("Interface '%s' reloaded", name)
|
||||||
return _ok({"name": name, "reloaded": True})
|
return _ok({"name": name, "reloaded": True})
|
||||||
|
except ValueError as exc:
|
||||||
|
return _error(str(exc), 400)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to reload interface '%s': %s", name, exc)
|
logger.error("Failed to reload interface '%s': %s", name, exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -107,7 +126,7 @@ def apply_all():
|
|||||||
JSON with number of interfaces applied.
|
JSON with number of interfaces applied.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
result = post("/network/apply", {})
|
result = post(POST_NETWORK_APPLY, {})
|
||||||
logger.info("Network config applied: %d interfaces", result.get("applied", 0))
|
logger.info("Network config applied: %d interfaces", result.get("applied", 0))
|
||||||
return _ok(result)
|
return _ok(result)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -126,7 +145,7 @@ def infer_dhcp_ranges():
|
|||||||
JSON with per-interface suggested DHCP ranges.
|
JSON with per-interface suggested DHCP ranges.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/network/infer-dhcp-ranges"))
|
return _ok(get(GET_NETWORK_INFER_DHCP_RANGES))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to infer DHCP ranges: %s", exc)
|
logger.error("Failed to infer DHCP ranges: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -143,7 +162,7 @@ def infer_zones():
|
|||||||
JSON with per-interface suggested zone names.
|
JSON with per-interface suggested zone names.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/network/infer-zones"))
|
return _ok(get(GET_NETWORK_INFER_ZONES))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to infer zones: %s", exc)
|
logger.error("Failed to infer zones: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
|
|||||||
+24
-11
@@ -8,6 +8,19 @@ import logging
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
||||||
|
from daemon.iface import (
|
||||||
|
DELETE_NGINX_DOMAINS_REMOVE,
|
||||||
|
GET_NGINX_CONFIG,
|
||||||
|
GET_NGINX_DOMAINS,
|
||||||
|
PATCH_NGINX_CONFIG,
|
||||||
|
POST_NGINX_APPLY,
|
||||||
|
POST_NGINX_CONFIG,
|
||||||
|
POST_NGINX_DOMAINS_ADD,
|
||||||
|
POST_NGINX_DOMAINS_UPDATE,
|
||||||
|
POST_NGINX_MANAGEMENT,
|
||||||
|
POST_NGINX_SSL_APPLY,
|
||||||
|
POST_NGINX_TEST,
|
||||||
|
)
|
||||||
from webui.api.common import _error, _ok
|
from webui.api.common import _error, _ok
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -27,7 +40,7 @@ def ssl_apply_bp():
|
|||||||
RuntimeError: If nginx SSL snippet write fails.
|
RuntimeError: If nginx SSL snippet write fails.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
post("/nginx/ssl-apply")
|
post(POST_NGINX_SSL_APPLY)
|
||||||
logger.info("SSL snippet written via API")
|
logger.info("SSL snippet written via API")
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -45,7 +58,7 @@ def get_config_bp():
|
|||||||
Current config dict from the daemon.
|
Current config dict from the daemon.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/nginx/config"))
|
return _ok(get(GET_NGINX_CONFIG))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to read proxy config: %s", exc)
|
logger.error("Failed to read proxy config: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -67,7 +80,7 @@ def post_config():
|
|||||||
if not isinstance(body, dict):
|
if not isinstance(body, dict):
|
||||||
return _error("Request body must be a JSON object", 400)
|
return _error("Request body must be a JSON object", 400)
|
||||||
try:
|
try:
|
||||||
post("/nginx/config", body)
|
post(POST_NGINX_CONFIG, body)
|
||||||
logger.info("Proxy config saved: %s", sorted(body.keys()))
|
logger.info("Proxy config saved: %s", sorted(body.keys()))
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -94,7 +107,7 @@ def patch_config():
|
|||||||
if not isinstance(body, dict):
|
if not isinstance(body, dict):
|
||||||
return _error("Request body must be a JSON object", 400)
|
return _error("Request body must be a JSON object", 400)
|
||||||
try:
|
try:
|
||||||
patch("/nginx/config", body)
|
patch(PATCH_NGINX_CONFIG, body)
|
||||||
logger.info("Proxy config patched: %s", sorted(body.keys()))
|
logger.info("Proxy config patched: %s", sorted(body.keys()))
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -115,7 +128,7 @@ def list_domains():
|
|||||||
List of domain dicts from the daemon.
|
List of domain dicts from the daemon.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/nginx/domains"))
|
return _ok(get(GET_NGINX_DOMAINS))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to list proxy domains: %s", exc)
|
logger.error("Failed to list proxy domains: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -153,7 +166,7 @@ def add_domain_bp():
|
|||||||
return _error("'backend_port' is required", 400)
|
return _error("'backend_port' is required", 400)
|
||||||
try:
|
try:
|
||||||
post(
|
post(
|
||||||
"/nginx/domains/add",
|
POST_NGINX_DOMAINS_ADD,
|
||||||
{
|
{
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"backend_host": backend_host,
|
"backend_host": backend_host,
|
||||||
@@ -189,7 +202,7 @@ def update_domain_bp(domain):
|
|||||||
if not body:
|
if not body:
|
||||||
return _error("Request body must be a JSON object with fields to update", 400)
|
return _error("Request body must be a JSON object with fields to update", 400)
|
||||||
try:
|
try:
|
||||||
post("/nginx/domains/update", {"domain": domain, **body})
|
post(POST_NGINX_DOMAINS_UPDATE, {"domain": domain, **body})
|
||||||
logger.info("Proxy domain '%s' updated via API", domain)
|
logger.info("Proxy domain '%s' updated via API", domain)
|
||||||
return _ok({"domain": domain})
|
return _ok({"domain": domain})
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -213,7 +226,7 @@ def remove_domain_bp(domain):
|
|||||||
``{"domain": ...}`` on success.
|
``{"domain": ...}`` on success.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete("/nginx/domains/remove", {"domain": domain})
|
delete(DELETE_NGINX_DOMAINS_REMOVE, {"domain": domain})
|
||||||
logger.info("Proxy domain removed via API: %s", domain)
|
logger.info("Proxy domain removed via API: %s", domain)
|
||||||
return _ok({"domain": domain})
|
return _ok({"domain": domain})
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
@@ -234,7 +247,7 @@ def apply_bp():
|
|||||||
``{"ok": true}`` on success.
|
``{"ok": true}`` on success.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
post("/nginx/apply")
|
post(POST_NGINX_APPLY)
|
||||||
logger.info("nginx config applied via API")
|
logger.info("nginx config applied via API")
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -252,7 +265,7 @@ def test_bp():
|
|||||||
``{"valid": true, "output": ...}`` on success. Returns 400 if test fails.
|
``{"valid": true, "output": ...}`` on success. Returns 400 if test fails.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
result = post("/nginx/test")
|
result = post(POST_NGINX_TEST)
|
||||||
if result.get("valid"):
|
if result.get("valid"):
|
||||||
return _ok({"valid": True, "output": result.get("output", "")})
|
return _ok({"valid": True, "output": result.get("output", "")})
|
||||||
return _error(result.get("output", "unknown error"), 400)
|
return _error(result.get("output", "unknown error"), 400)
|
||||||
@@ -287,7 +300,7 @@ def management_bp():
|
|||||||
auth_pass = body.get("auth_pass")
|
auth_pass = body.get("auth_pass")
|
||||||
try:
|
try:
|
||||||
post(
|
post(
|
||||||
"/nginx/management",
|
POST_NGINX_MANAGEMENT,
|
||||||
{
|
{
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"flask_host": flask_host,
|
"flask_host": flask_host,
|
||||||
|
|||||||
+27
-13
@@ -8,6 +8,20 @@ import logging
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
from daemon.client import BadRequest, NotFound, delete, get, patch, post
|
||||||
|
from daemon.iface import (
|
||||||
|
DELETE_WIREGUARD_PEERS_REMOVE,
|
||||||
|
GET_WIREGUARD_CONFIG,
|
||||||
|
GET_WIREGUARD_PEER_STATUS,
|
||||||
|
GET_WIREGUARD_PEERS,
|
||||||
|
GET_WIREGUARD_STATUS,
|
||||||
|
PATCH_WIREGUARD_CONFIG,
|
||||||
|
POST_WIREGUARD_APPLY,
|
||||||
|
POST_WIREGUARD_CONFIG,
|
||||||
|
POST_WIREGUARD_DOWN,
|
||||||
|
POST_WIREGUARD_GENERATE_CLIENT,
|
||||||
|
POST_WIREGUARD_INITIALIZE,
|
||||||
|
POST_WIREGUARD_PEERS_ADD,
|
||||||
|
)
|
||||||
from webui.api.common import _error, _ok
|
from webui.api.common import _error, _ok
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -25,7 +39,7 @@ def get_config_bp():
|
|||||||
response on failure.
|
response on failure.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/wireguard/config"))
|
return _ok(get(GET_WIREGUARD_CONFIG))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to read WireGuard config: %s", exc)
|
logger.error("Failed to read WireGuard config: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -53,7 +67,7 @@ def post_config():
|
|||||||
body = dict(body)
|
body = dict(body)
|
||||||
body["interface"] = dict(body["interface"])
|
body["interface"] = dict(body["interface"])
|
||||||
body["interface"].pop("private_key", None)
|
body["interface"].pop("private_key", None)
|
||||||
post("/wireguard/config", body)
|
post(POST_WIREGUARD_CONFIG, body)
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
logger.info("WireGuard config save rejected: %s", exc)
|
logger.info("WireGuard config save rejected: %s", exc)
|
||||||
@@ -85,7 +99,7 @@ def patch_config():
|
|||||||
body = dict(body)
|
body = dict(body)
|
||||||
body["interface"] = dict(body["interface"])
|
body["interface"] = dict(body["interface"])
|
||||||
body["interface"].pop("private_key", None)
|
body["interface"].pop("private_key", None)
|
||||||
patch("/wireguard/config", body)
|
patch(PATCH_WIREGUARD_CONFIG, body)
|
||||||
logger.info("WireGuard config patched: %s", sorted(body.keys()))
|
logger.info("WireGuard config patched: %s", sorted(body.keys()))
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except BadRequest as exc:
|
except BadRequest as exc:
|
||||||
@@ -106,7 +120,7 @@ def apply_bp():
|
|||||||
Success response on acceptance, or 500 on server error.
|
Success response on acceptance, or 500 on server error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
post("/wireguard/apply")
|
post(POST_WIREGUARD_APPLY)
|
||||||
logger.info("WireGuard tunnel applied via API")
|
logger.info("WireGuard tunnel applied via API")
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -124,7 +138,7 @@ def up_bp():
|
|||||||
Success response on acceptance, or 500 on server error.
|
Success response on acceptance, or 500 on server error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
post("/wireguard/apply")
|
post(POST_WIREGUARD_APPLY)
|
||||||
logger.info("WireGuard tunnel started via API")
|
logger.info("WireGuard tunnel started via API")
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -142,7 +156,7 @@ def down_bp():
|
|||||||
Success response on acceptance, or 500 on server error.
|
Success response on acceptance, or 500 on server error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
post("/wireguard/down")
|
post(POST_WIREGUARD_DOWN)
|
||||||
logger.info("WireGuard tunnel brought down via API")
|
logger.info("WireGuard tunnel brought down via API")
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -161,7 +175,7 @@ def status_bp():
|
|||||||
response on failure.
|
response on failure.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/wireguard/status"))
|
return _ok(get(GET_WIREGUARD_STATUS))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to get WireGuard status: %s", exc)
|
logger.error("Failed to get WireGuard status: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -177,7 +191,7 @@ def initialize_bp():
|
|||||||
Success response on acceptance, or 500 on server error.
|
Success response on acceptance, or 500 on server error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
post("/wireguard/initialize")
|
post(POST_WIREGUARD_INITIALIZE)
|
||||||
logger.info("WireGuard initialized via API")
|
logger.info("WireGuard initialized via API")
|
||||||
return _ok(None)
|
return _ok(None)
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
@@ -208,7 +222,7 @@ def add_peer_bp():
|
|||||||
return _error("'name' is required", 400)
|
return _error("'name' is required", 400)
|
||||||
try:
|
try:
|
||||||
peer = post(
|
peer = post(
|
||||||
"/wireguard/peers/add",
|
POST_WIREGUARD_PEERS_ADD,
|
||||||
{
|
{
|
||||||
"name": name,
|
"name": name,
|
||||||
"endpoint": body.get("endpoint"),
|
"endpoint": body.get("endpoint"),
|
||||||
@@ -241,7 +255,7 @@ def remove_peer_bp(name):
|
|||||||
or 500 on server error.
|
or 500 on server error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
delete("/wireguard/peers/remove", {"name": name})
|
delete(DELETE_WIREGUARD_PEERS_REMOVE, {"name": name})
|
||||||
logger.info("WireGuard peer '%s' removed via API", name)
|
logger.info("WireGuard peer '%s' removed via API", name)
|
||||||
return _ok({"name": name})
|
return _ok({"name": name})
|
||||||
except NotFound as exc:
|
except NotFound as exc:
|
||||||
@@ -263,7 +277,7 @@ def peers_bp():
|
|||||||
on failure.
|
on failure.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/wireguard/peers"))
|
return _ok(get(GET_WIREGUARD_PEERS))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to list WireGuard peers: %s", exc)
|
logger.error("Failed to list WireGuard peers: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -280,7 +294,7 @@ def peer_status_bp():
|
|||||||
on failure.
|
on failure.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return _ok(get("/wireguard/peer-status"))
|
return _ok(get(GET_WIREGUARD_PEER_STATUS))
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
logger.error("Failed to get WireGuard peer status: %s", exc)
|
logger.error("Failed to get WireGuard peer status: %s", exc)
|
||||||
return _error(str(exc), 500)
|
return _error(str(exc), 500)
|
||||||
@@ -309,7 +323,7 @@ def generate_client_bp():
|
|||||||
return _error("Field 'server_endpoint' is required", 400)
|
return _error("Field 'server_endpoint' is required", 400)
|
||||||
try:
|
try:
|
||||||
result = post(
|
result = post(
|
||||||
"/wireguard/generate-client",
|
POST_WIREGUARD_GENERATE_CLIENT,
|
||||||
{
|
{
|
||||||
"name": name,
|
"name": name,
|
||||||
"server_endpoint": server_endpoint,
|
"server_endpoint": server_endpoint,
|
||||||
|
|||||||
Reference in New Issue
Block a user