From 58f0a3c3c01b05e862fc21bcf5a570fce4d3e195 Mon Sep 17 00:00:00 2001 From: Jacob Coffee Date: Sat, 27 Apr 2024 22:32:58 -0500 Subject: [PATCH] feat: add scalar, correct routes, fix frontend boxes --- docs/api/domain/calculator/helpers.rst | 8 ++ .../calculator/controllers/calculator.py | 73 ++++++++++++------- src/app/domain/calculator/helpers.py | 43 +++++++++++ src/app/domain/web/resources/scalar_api.css | 57 +++++++++++++++ src/app/domain/web/templates/base/nav.html | 8 ++ src/app/domain/web/templates/index.html | 20 ++++- src/app/lib/openapi.py | 29 +++----- 7 files changed, 193 insertions(+), 45 deletions(-) create mode 100644 docs/api/domain/calculator/helpers.rst create mode 100644 src/app/domain/calculator/helpers.py create mode 100644 src/app/domain/web/resources/scalar_api.css diff --git a/docs/api/domain/calculator/helpers.rst b/docs/api/domain/calculator/helpers.rst new file mode 100644 index 0000000..f6e5039 --- /dev/null +++ b/docs/api/domain/calculator/helpers.rst @@ -0,0 +1,8 @@ +======= +helpers +======= + +Helper utilities for the application calculator. + +.. automodule:: app.domain.calculator.helpers + :members: diff --git a/src/app/domain/calculator/controllers/calculator.py b/src/app/domain/calculator/controllers/calculator.py index 4a8ae26..0e09a41 100644 --- a/src/app/domain/calculator/controllers/calculator.py +++ b/src/app/domain/calculator/controllers/calculator.py @@ -2,7 +2,6 @@ from __future__ import annotations -import ipaddress from typing import Annotated from litestar import Controller, get @@ -12,6 +11,7 @@ from litestar.status_codes import HTTP_200_OK from app.domain import urls +from app.domain.calculator.helpers import get_network_info from app.domain.calculator.schema import NetworkInfo __all__ = ("CalculatorController",) @@ -23,7 +23,7 @@ class CalculatorController(Controller): opt = {"exclude_from_auth": True} @get( - path=urls.IP, + path=f"{urls.IP}", operation_id="CalculatorIP", name="calculator:ip", status_code=HTTP_200_OK, @@ -42,6 +42,50 @@ async def ip( prefix: Annotated[ str, Parameter(..., description="The CIDR notation.", pattern=r"^(?:[0-9]|[1-2][0-9]|3[0-2])$") ], + ) -> NetworkInfo: + """Calculate IP. + + Args: + request: HTMXRequest + ip: The IP address in standard IPv4 format. + prefix: The CIDR notation. + + Returns: + The network information rendered via the schema object. + """ + network_info = get_network_info(ip, prefix) + + return NetworkInfo( + subnet_mask=network_info.subnet_mask, + wildcard_subnet_mask=network_info.wildcard_subnet_mask, + total_ips=network_info.total_ips, + usable_ips=network_info.usable_ips, + network_ip=network_info.network_ip, + broadcast_ip=network_info.broadcast_ip, + first_ip=network_info.first_ip, + last_ip=network_info.last_ip, + ) + + @get( + path=f"{urls.IP}/htmx", + operation_id="CalculatorIPHTMX", + name="calculator:ip:htmx", + status_code=HTTP_200_OK, + ) + async def ip_htmx( + self, + request: HTMXRequest, + ip: Annotated[ + str, + Parameter( + ..., + description="The IP address in standard IPv4 format.", + pattern=r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", + ), + ], + prefix: Annotated[ + str, Parameter(..., description="The CIDR notation.", pattern=r"^(?:[0-9]|[1-2][0-9]|3[0-2])$") + ], ) -> HTMXTemplate: """Calculate IP. @@ -53,30 +97,7 @@ async def ip( Returns: The request data via HTMX as a template passed to partial.html """ - net = ipaddress.IPv4Network(f"{ip}/{prefix}", strict=False) - - subnet_mask = str(net.netmask) - wildcard_subnet_mask = str(net.hostmask) - total_ips = net.num_addresses - usable_ips = total_ips - 2 # minus network and broadcast addresses - network_ip = str(net.network_address) - broadcast_ip = str(net.broadcast_address) - - # First and last usable IP addresses - hosts = list(net.hosts()) - first_ip = str(hosts[0]) if hosts else None - last_ip = str(hosts[-1]) if hosts else None - - network_info = NetworkInfo( - subnet_mask=subnet_mask, - wildcard_subnet_mask=wildcard_subnet_mask, - total_ips=total_ips, - usable_ips=usable_ips, - network_ip=network_ip, - broadcast_ip=broadcast_ip, - first_ip=first_ip, - last_ip=last_ip, - ) + network_info = get_network_info(ip, prefix) return HTMXTemplate( template_name="partial.html", diff --git a/src/app/domain/calculator/helpers.py b/src/app/domain/calculator/helpers.py new file mode 100644 index 0000000..140997d --- /dev/null +++ b/src/app/domain/calculator/helpers.py @@ -0,0 +1,43 @@ +"""Helpers for calculator domain module.""" + +import ipaddress + +from app.domain.calculator.schema import NetworkInfo + +__all__ = ("get_network_info",) + + +def get_network_info(ip: str, prefix: str) -> NetworkInfo: + """Get network information. + + Args: + ip: The IP address in standard IPv4 format. + prefix: The CIDR notation. + + Returns: + NetworkInfo: The network information. + """ + net = ipaddress.IPv4Network(f"{ip}/{prefix}", strict=False) + + subnet_mask = str(net.netmask) + wildcard_subnet_mask = str(net.hostmask) + total_ips = net.num_addresses + usable_ips = total_ips - 2 # minus network and broadcast addresses + network_ip = str(net.network_address) + broadcast_ip = str(net.broadcast_address) + + # First and last usable IP addresses + hosts = list(net.hosts()) + first_ip = str(hosts[0]) if hosts else None + last_ip = str(hosts[-1]) if hosts else None + + return NetworkInfo( + subnet_mask=subnet_mask, + wildcard_subnet_mask=wildcard_subnet_mask, + total_ips=total_ips, + usable_ips=usable_ips, + network_ip=network_ip, + broadcast_ip=broadcast_ip, + first_ip=first_ip, + last_ip=last_ip, + ) diff --git a/src/app/domain/web/resources/scalar_api.css b/src/app/domain/web/resources/scalar_api.css new file mode 100644 index 0000000..27fdb2b --- /dev/null +++ b/src/app/domain/web/resources/scalar_api.css @@ -0,0 +1,57 @@ +/* Custom Litestar-branded Styling for the Scalar.com OpenAPI documentation renderer */ + +/* basic theme */ +.light-mode { + --theme-color-1: #202235; + --theme-color-2: #757575; + --theme-color-3: #8e8e8e; + --theme-color-accent: #4297e8; + + --theme-background-1: #fff; + --theme-background-2: #f6f6f6; + --theme-background-3: #e7e7e7; + --theme-background-accent: rgba(31, 76, 120, 0.12); + + --theme-color-green: #069061; + --theme-color-red: #ef0006; + --theme-color-yellow: #edbe20; + --theme-color-blue: #0082d0; + --theme-color-orange: #fb892c; + --theme-color-purple: #5203d1; +} + +.dark-mode { + --theme-color-1: rgb(221, 223, 227, 1); + --theme-color-2: rgba(221, 223, 227, 0.62); + --theme-color-3: rgba(221, 223, 227, 0.44); + --theme-color-accent: #d0e8ff; + + --theme-background-1: #18181b; + --theme-background-2: #27272a; + --theme-background-3: #3e3e41; + --theme-background-accent: #d0e8ff1f; + + --theme-border-color: rgba(255, 255, 255, 0.1); + + --theme-color-green: #00b648; + --theme-color-red: #ff7b72; + --theme-color-yellow: #eabf6c; + --theme-color-blue: #a5d6ff; + --theme-color-orange: #ff8d4d; + --theme-color-purple: #f3c7ee; +} + +.dark-mode .sidebar, +.light-mode .sidebar { + --sidebar-background-1: var(--theme-background-1); + --sidebar-item-hover-color: currentColor; + --sidebar-item-hover-background: var(--theme-background-2); + --sidebar-item-active-background: var(--theme-background-accent); + --sidebar-border-color: var(--theme-border-color); + --sidebar-color-1: var(--theme-color-1); + --sidebar-color-2: var(--theme-color-2); + --sidebar-color-active: var(--theme-color-accent); + --sidebar-search-background: transparent; + --sidebar-search-border-color: var(--theme-border-color); + --sidebar-search--color: var(--theme-color-3); +} diff --git a/src/app/domain/web/templates/base/nav.html b/src/app/domain/web/templates/base/nav.html index e6260ff..257e578 100644 --- a/src/app/domain/web/templates/base/nav.html +++ b/src/app/domain/web/templates/base/nav.html @@ -185,6 +185,14 @@ aria-labelledby="menu-button" tabindex="-1">
+ + Scalar + {% endblock extrastyle %} {% block title %}Home{% endblock %} {% block body %} -
+

Calculator

@@ -79,6 +79,8 @@

@@ -92,6 +94,8 @@

@@ -107,6 +111,8 @@

@@ -120,6 +126,8 @@

@@ -137,6 +145,8 @@

@@ -152,6 +162,8 @@

@@ -167,6 +179,8 @@

@@ -182,6 +196,8 @@

@@ -239,7 +255,7 @@

IP N

`_ is implemented. - """ - - path: str = settings.openapi.PATH - +__all__ = ("config",) config = OpenAPIConfig( title=settings.openapi.TITLE or settings.app.NAME, - description=settings.openapi.DESCRIPTION, - servers=settings.openapi.SERVERS, # type: ignore[arg-type] - external_docs=settings.openapi.EXTERNAL_DOCS, # type: ignore[arg-type] version=settings.openapi.VERSION, contact=Contact(name=settings.openapi.CONTACT_NAME, email=settings.openapi.CONTACT_EMAIL), use_handler_docstrings=True, - root_schema_site="swagger", - openapi_controller=OverridenController, + path=settings.openapi.PATH, + servers=settings.openapi.SERVERS, # type: ignore[arg-type] + external_docs=settings.openapi.EXTERNAL_DOCS, # type: ignore[arg-type] + create_examples=True, + render_plugins=[ + ScalarRenderPlugin(version="1.20.7", path="/", css_url="/static/scalar_api.css"), + SwaggerRenderPlugin(), + StoplightRenderPlugin(), + RedocRenderPlugin(), + ], ) """ OpenAPI config for Network Information API.