From: Michael Tremer Date: Mon, 29 Dec 2025 17:49:52 +0000 (+0000) Subject: dnsbl: Show more details about a list X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91e594da10835d4c4c9bb40b779d8c8951d606b9;p=ipfire.org.git dnsbl: Show more details about a list Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 2614058b..f13fff09 100644 --- a/Makefile.am +++ b/Makefile.am @@ -184,7 +184,8 @@ templates_blog_modules_DATA = \ templates_blog_modulesdir = $(templates_blogdir)/modules templates_dnsbl_DATA = \ - src/templates/dnsbl/index.html + src/templates/dnsbl/index.html \ + src/templates/dnsbl/list.html templates_dnsbldir = $(templatesdir)/dnsbl diff --git a/src/backend/dnsbl.py b/src/backend/dnsbl.py index 60f234b9..6244106a 100644 --- a/src/backend/dnsbl.py +++ b/src/backend/dnsbl.py @@ -4,6 +4,7 @@ import datetime import json import logging import pydantic +import tornado.httpclient import urllib.parse from . import base @@ -14,7 +15,7 @@ from .decorators import * log = logging.getLogger(__name__) class DNSBL(Object): - async def __fetch(self, path, **kwargs): + async def _fetch(self, path, **kwargs): url = urllib.parse.urljoin( #"https://api.dnsbl.ipfire.org", "http://dnsbl01.haj.ipfire.org:8000", path, @@ -32,10 +33,24 @@ class DNSBL(Object): """ Fetches all available lists """ - response = await self.__fetch("/lists") + response = await self._fetch("/lists") return [List(self.backend, **list) for list in response] + async def get_list(self, slug): + try: + response = await self._fetch("/lists/%s" % slug) + + # Return nothing if we received 404 + except tornado.httpclient.HTTPClientError as e: + if e.code == 404: + return + + raise e + + # Return the list + return List(self.backend, **response) + class Model(pydantic.BaseModel): """ @@ -92,3 +107,57 @@ class List(Model): return self.slug < other.slug return NotImplemented + + # Sources + + async def get_sources(self): + response = await self._backend.dnsbl._fetch("/lists/%s/sources" % self.slug) + + return [Source(self._backend, **data) for data in response] + + +class Source(Model): + """ + Represents a source of a list + """ + # Name + name : str + + # URL + url : str + + # Created At + created_at : datetime.datetime + + # Created By + created_by : str + + # Deleted At + deleted_at : datetime.datetime | None + + # Deleted By + deleted_by : str | None + + # License + license : str + + # Updated At + updated_at : datetime.datetime | None + + # Total Domains + total_domains : int + + # Dead Domains + dead_domains : int + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.name == other.name + + return NotImplemented + + def __lt__(self, other): + if isinstance(other, self.__class__): + return self.name < other.name + + return NotImplemented diff --git a/src/templates/dnsbl/list.html b/src/templates/dnsbl/list.html new file mode 100644 index 00000000..5c00ee47 --- /dev/null +++ b/src/templates/dnsbl/list.html @@ -0,0 +1,90 @@ +{% extends "../base.html" %} + +{% block head %} + {% module OpenGraph( + title=_("IPFire DNSBL - %s") % list.name, + description=list.description, + ) %} +{% end block %} + +{% block title %} + {{ _("IPFire DNSBL") }}{% if list.description %} - {{ list.description }}{% end %} +{% end block %} + +{% block container %} +
+
+
+

+ {{ list.name }} +

+ + {% if list.description %} +
+ {{ list.description }} +
+ {% end %} +
+
+
+ + {# Sources #} + {% if sources %} +
+
+
{{ _("Sources") }}
+ + + + + + + + + + + + + + {% for source in sorted(sources) %} + + + + + + + + {% end %} + +
+ {{ _("Name") }} + + {{ _("Last Update") }} + + {{ _("Listed Domains") }} +
+ + {{ source.name }} + + +
+ + {{ source.license }} +
+ {{ locale.format_date(source.updated_at, shorter=True) }} + + {{ format_number(source.total_domains) }} + +
+ + {# Dead Domains #} + {% if source.total_domains and source.dead_domains %} + + {{ _("Dead Domains: %s") % format_percent(source.dead_domains / source.total_domains) }} + + {% end %} +
+
+
+ {% end %} +{% end block %} diff --git a/src/web/__init__.py b/src/web/__init__.py index cc81fc27..0a50a39d 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -59,6 +59,7 @@ class Application(tornado.web.Application): "format_language_name" : self.format_language_name, "format_month_name" : self.format_month_name, "format_number" : self.format_number, + "format_percent" : self.format_percent, "format_phone_number" : self.format_phone_number, "format_phone_number_to_e164" : self.format_phone_number_to_e164, "format_phone_number_location" : self.format_phone_number_location, @@ -214,6 +215,7 @@ class Application(tornado.web.Application): # DNSBL (r"/dnsbl/?", dnsbl.IndexHandler), + (r"/dnsbl/lists/(\w+)", dnsbl.ListHandler), # Single-Sign-On for Discourse (r"/sso/discourse", auth.SSODiscourse), @@ -454,6 +456,9 @@ class Application(tornado.web.Application): def format_number(self, handler, *args, **kwargs): return babel.numbers.format_number(*args, locale=handler.locale.code, **kwargs) + def format_percent(self, handler, *args, **kwargs): + return babel.numbers.format_percent(*args, locale=handler.locale.code, **kwargs) + def format_phone_number(self, handler, number): if not isinstance(number, phonenumbers.PhoneNumber): try: diff --git a/src/web/dnsbl.py b/src/web/dnsbl.py index 94147f10..a8a4e21d 100644 --- a/src/web/dnsbl.py +++ b/src/web/dnsbl.py @@ -1,4 +1,5 @@ +import tornado.web from . import base from . import ui_modules @@ -12,6 +13,20 @@ class IndexHandler(base.AnalyticsMixin, base.BaseHandler): self.render("dnsbl/index.html", lists=lists) +class ListHandler(base.AnalyticsMixin, base.BaseHandler): + async def get(self, slug): + # Fetch the list + list = await self.backend.dnsbl.get_list(slug) + if not list: + raise tornado.web.HTTPError(404, "Could not find list '%s'" % slug) + + # Fetch the sources + sources = await list.get_sources() + + # Render the page + self.render("dnsbl/list.html", list=list, sources=sources) + + class ListsModule(ui_modules.UIModule): def render(self, lists): return self.render_string("dnsbl/modules/lists.html", lists=lists)