]> git.ipfire.org Git - ipfire.org.git/commitdiff
dnsbl: Create a page that shows the history of a domain on a list
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 7 Jan 2026 17:07:55 +0000 (17:07 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 7 Jan 2026 17:07:55 +0000 (17:07 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/backend/dnsbl.py
src/templates/dnsbl/lists/domain.html [new file with mode: 0644]
src/templates/dnsbl/lists/history.html
src/templates/dnsbl/search.html
src/web/__init__.py
src/web/dnsbl.py

index 6839bfb536306a32ba28586385cc390aa7ed6de9..a7507b0a9b8283e79070ce69602528e5299dde80 100644 (file)
@@ -190,6 +190,7 @@ templates_dnsbl_DATA = \
 templates_dnsbldir = $(templatesdir)/dnsbl
 
 templates_dnsbl_lists_DATA = \
+       src/templates/dnsbl/lists/domain.html \
        src/templates/dnsbl/lists/history.html \
        src/templates/dnsbl/lists/index.html \
        src/templates/dnsbl/lists/reports.html \
index 0cbf230f6461f5b8117b47ffee323f6c7cdf3a18..c07f7d0c2933b3cded905019958218a762748226 100644 (file)
@@ -252,6 +252,16 @@ class List(Model):
 
                return [History(self._backend, **data) for data in response]
 
+       async def get_domain_history(self, name):
+               """
+                       Fetches the history of a specific domain on this list
+               """
+               response = await self._backend.dnsbl._fetch(
+                       "/lists/%s/domains/%s" % (self.slug, name),
+               )
+
+               return [DomainHistory(self._backend, **data) for data in response]
+
 
 class Source(Model):
        """
@@ -364,3 +374,32 @@ class History(Model):
 
        # Domains Removed
        domains_removed: typing.List[str] = []
+
+
+class DomainHistory(Model):
+       # Name
+       name: str
+
+       # Timestamp
+       timestamp: datetime.datetime
+
+       # Type
+       type: str
+
+       # Block?
+       block: bool = True
+
+       # By
+       by: str | None = None
+
+       # Source Name
+       source_name: str | None = None
+
+       # Report ID
+       report_id: uuid.UUID | None = None
+
+       # Blocks
+       blocks: int = 0
+
+       # Allows
+       allows: int = 0
diff --git a/src/templates/dnsbl/lists/domain.html b/src/templates/dnsbl/lists/domain.html
new file mode 100644 (file)
index 0000000..c9cfa1d
--- /dev/null
@@ -0,0 +1,148 @@
+{% extends "../../base.html" %}
+
+{% block head %}
+       {% module OpenGraph(
+               title=_("IPFire DNSBL - %(list)s - Domain %(domain)s") %\
+                       { "list" : list, "domain" : name },
+               description=list.description,
+       ) %}
+{% end block %}
+
+{% block title %}
+       {{ _("IPFire DNSBL") }} - {{ list }} - {{ _("Domain %s") % name }}
+{% end block %}
+
+{% block container %}
+       <section class="hero is-dark">
+               <div class="hero-body">
+                       <div class="container">
+                               <nav class="breadcrumb" aria-label="breadcrumbs">
+                                       <ul>
+                                               <li>
+                                                       <a href="/dnsbl">
+                                                               {{ _("IPFire DNSBL") }}
+                                                       </a>
+                                               </li>
+
+                                               <li>
+                                                       <a href="/dnsbl/lists">
+                                                               {{ _("Lists") }}
+                                                       </a>
+                                               </li>
+
+                                               <li>
+                                                       <a href="/dnsbl/lists/{{ list.slug }}">
+                                                               {{ list }}
+                                                       </a>
+                                               </li>
+
+                                               <li class="is-active">
+                                                       <a href="#" aria-current="page">{{ name }}</a>
+                                               </li>
+                                       </ul>
+                               </nav>
+
+                               <div class="columns is-vcentered">
+                                       <div class="column">
+                                               <h1 class="title">
+                                                       {{ name }}
+                                               </h1>
+
+                                               <h3 class="subtitle">
+                                                       {{ _("Listing on %s") % list }}
+                                               </h3>
+                                       </div>
+
+                                       <div class="column is-narrow">
+                                               {% for event in events %}
+                                                       {# Allowed? #}
+                                                       {% if event.allows %}
+                                                               <span class="tag is-large is-success">
+                                                                       {{ _("Allowed") }}
+                                                               </span>
+
+                                                       {# Blocked? #}
+                                                       {% elif event.blocks %}
+                                                               <span class="tag is-large is-danger">
+                                                                       {{ _("Blocked") }}
+                                                               </span>
+
+                                                       {# Not Listed #}
+                                                       {% else %}
+                                                               <span class="tag is-large is-white">
+                                                                       {{ _("Not Listed") }}
+                                                               </span>
+                                                       {% end %}
+
+                                                       {# Only use the latest result #}
+                                                       {% break %}
+
+                                               {# If there are no events, this has never been listed #}
+                                               {% else %}
+                                                       <span class="tag is-large is-white">
+                                                               {{ _("Not Listed") }}
+                                                       </span>
+                                               {% end %}
+                                       </div>
+                               </div>
+                       </div>
+               </div>
+       </section>
+
+       {# History #}
+       {% if events %}
+               <section class="section">
+                       <div class="container">
+                               <h5 class="title is-5">
+                                       {{ _("History") }}
+                               </h5>
+
+                               <table class="table is-hoverable is-fullwidth">
+                                       <tbody>
+                                               {% for event in events %}
+                                                       <tr>
+                                                               {# Timestamp #}
+                                                               <td class="is-narrow">
+                                                                       {{ locale.format_date(event.timestamp, shorter=True) }}
+                                                               </td>
+
+                                                               {# What happened? #}
+                                                               <td>
+                                                                       {% if event.type == "added" %}
+                                                                               {% if event.block %}
+                                                                                       {{ _("A block was added by %s") % (event.by or event.source_name) }}
+                                                                               {% else %}
+                                                                                       {{ _("The domain was exempted by %s") % (event.by or event.source_name) }}
+                                                                               {% end %}
+
+                                                                       {% elif event.type == "removed" %}
+                                                                               {% if event.block %}
+                                                                                       {{ _("A block was lifted by %s") % (event.by or event.source_name) }}
+                                                                               {% else %}
+                                                                                       {{ _("An exemption was removed by %s") % (event.by or event.source_name) }}
+                                                                               {% end %}
+                                                                       {% end %}
+                                                               </td>
+
+                                                               {# Show the current status #}
+                                                               {% if event.allows %}
+                                                                       <td class="is-narrow is-success">
+                                                                               {{ _("Allowed") }}
+                                                                       </td>
+                                                               {% elif event.blocks %}
+                                                                       <td class="is-narrow is-danger">
+                                                                               {{ _("Blocked") }}
+                                                                       </td>
+                                                               {% else %}
+                                                                       <td class="is-narrow">
+                                                                               {{ _("Not Listed") }}
+                                                                       </td>
+                                                               {% end %}
+                                                       </tr>
+                                               {% end %}
+                                       </tbody>
+                               </table>
+                       </div>
+               </section>
+       {% end %}
+{% end block %}
index 9a4cd87c2965ec7234634c34fa6440e36c9ec6b7..360e27a34aedc91a47f93a0af4ae9fd7297bdee5 100644 (file)
                                <ul>
                                        {# Domains Allowed #}
                                        {% for domain in event.domains_allowed %}
-                                               <li class="has-text-info" title="{{ _("Allowed") }}">
-                                                       &plus; {{ format_domain(domain) }}
+                                               <li title="{{ _("Allowed") }}">
+                                                       <a class="has-text-info" href="/dnsbl/lists/{{ list.slug }}/domains/{{ domain }}">
+                                                               &plus; {{ format_domain(domain) }}
+                                                       </a>
                                                </li>
                                        {% end %}
 
                                        {# Domains Blocked #}
                                        {% for domain in event.domains_blocked %}
-                                               <li class="has-text-success" title="{{ _("Blocked") }}">
-                                                       &plus; {{ format_domain(domain) }}
+                                               <li title="{{ _("Blocked") }}">
+                                                       <a class="has-text-success" href="/dnsbl/lists/{{ list.slug }}/domains/{{ domain }}">
+                                                               &plus; {{ format_domain(domain) }}
+                                                       </a>
                                                </li>
                                        {% end %}
 
                                        {# Domains Removed #}
                                        {% for domain in event.domains_removed %}
-                                               <li class="has-text-danger" title="{{ _("Removed") }}">
-                                                       &minus; {{ format_domain(domain) }}
+                                               <li title="{{ _("Removed") }}">
+                                                       <a class="has-text-danger" href="/dnsbl/lists/{{ list.slug }}/domains/{{ domain }}">
+                                                               &minus; {{ format_domain(domain) }}
+                                                       </a>
                                                </li>
                                        {% end %}
                                </ul>
index fbb8ce52af606e39ad11ab770997829bad358f49..3cd8ab8d49a5b7569ca5049bd6e1bf9c91709899 100644 (file)
@@ -39,7 +39,9 @@
                                                        <ul>
                                                                {% for domain in domains %}
                                                                        <li>
-                                                                               {{ domain }}
+                                                                               <a href="/dnsbl/lists/{{ list.slug }}/domains/{{ domain }}">
+                                                                                       {{ format_domain(domain) }}
+                                                                               </a>
                                                                        </li>
                                                                {% end %}
                                                        </ul>
index 66a8df41cfc998ddf6ca93fce9324dba307cf57f..00db7fe544936e7f2049563d4b212cae8b5ed4c1 100644 (file)
@@ -219,6 +219,7 @@ class Application(tornado.web.Application):
                        (r"/dnsbl/?", StaticHandler, { "template" : "dnsbl/index.html" }),
                        (r"/dnsbl/lists", dnsbl.ListsHandler),
                        (r"/dnsbl/lists/(\w+)", dnsbl.ListHandler),
+                       (r"/dnsbl/lists/(\w+)/domains/(.*)", dnsbl.ListDomainHandler),
                        (r"/dnsbl/lists/(\w+)/history", dnsbl.ListHistoryHandler),
                        (r"/dnsbl/lists/(\w+)/reports", dnsbl.ListReportsHandler),
                        (r"/dnsbl/lists/(\w+)/sources", dnsbl.ListSourcesHandler),
index 3cbbe364c6d317585685e81e14ac734c78d3926b..741ba1b53fd7bb52a5428dfa2e9b0984a0ae7298 100644 (file)
@@ -77,6 +77,20 @@ class ListSourcesHandler(base.AnalyticsMixin, BaseHandler):
                self.render("dnsbl/lists/sources.html", list=list, sources=sources)
 
 
+class ListDomainHandler(base.AnalyticsMixin, BaseHandler):
+       async def get(self, slug, name):
+               # 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 events
+               events = await list.get_domain_history(name)
+
+               # Render the page
+               self.render("dnsbl/lists/domain.html", list=list, name=name, events=events)
+
+
 class SubmitReportHandler(base.AnalyticsMixin, BaseHandler):
        async def get(self):
                # Fetch all lists