templates_dnsbldir = $(templatesdir)/dnsbl
templates_dnsbl_lists_DATA = \
+ src/templates/dnsbl/lists/history.html \
src/templates/dnsbl/lists/index.html \
src/templates/dnsbl/lists/reports.html \
src/templates/dnsbl/lists/sources.html \
import logging
import pydantic
import tornado.httpclient
+import typing
import urllib.parse
import uuid
# Return the report
return Report(self._backend, **response)
+ # History
+
+ async def get_history(self, limit=None):
+ """
+ Fetches the history of this list
+ """
+ response = await self._backend.dnsbl._fetch(
+ "/lists/%s/history" % self.slug, args={ "limit" : limit },
+ )
+
+ return [History(self._backend, **data) for data in response]
+
class Source(Model):
"""
self._list = await self._backend.dnsbl.get_list(self.list_slug)
return self._list
+
+
+class History(Model):
+ """
+ Represents a history event
+ """
+ # Timestamp
+ ts: datetime.datetime
+
+ # Domains Added
+ domains_added: typing.List[str] = []
+
+ # Domains Removed
+ domains_removed: typing.List[str] = []
--- /dev/null
+{% extends "../../base.html" %}
+
+{% block head %}
+ {% module OpenGraph(
+ title=_("IPFire DNSBL - %s - History") % list,
+ description=list.description,
+ ) %}
+{% end block %}
+
+{% block title %}{{ _("IPFire DNSBL") }} - {{ list }} - {{ _("History") }}{% 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">{{ _("History") }}</a>
+ </li>
+ </ul>
+ </nav>
+
+ <h1 class="title">
+ {{ _("History: %s") % list }}
+ </h1>
+ </div>
+ </div>
+ </section>
+
+ {# Events #}
+ {% for event in reversed(history) %}
+ <section class="section">
+ <div class="container">
+ <h5 class="title is-5">
+ {{ locale.format_date(event.ts) }}
+ </h5>
+
+ {# Tags #}
+ <span class="tags">
+ {% if event.domains_added %}
+ <span class="tag is-success">
+ + {{ len(event.domains_added) }}
+ </span>
+ {% end %}
+
+ {% if event.domains_removed %}
+ <span class="tag is-danger">
+ − {{ len(event.domains_removed) }}
+ </span>
+ {% end %}
+ </span>
+
+ <ul>
+ {# Domains Added #}
+ {% for domain in event.domains_added %}
+ <li class="has-text-success">
+ + {{ domain }}
+ </li>
+ {% end %}
+
+ {# Domains Removed #}
+ {% for domain in event.domains_removed %}
+ <li class="has-text-danger">
+ − {{ domain }}
+ </li>
+ {% end %}
+ </ul>
+ </div>
+ </section>
+ {% end %}
+{% end block %}
<a class="button" href="/dnsbl/lists/{{ list.slug }}/sources">
<span class="icon is-small">
- <i class="fas fa-list-check" aria-hidden="true"></i>
+ <i class="fas fa-code-pull-request" aria-hidden="true"></i>
</span>
<span>{{ _("Sources") }}</span>
</a>
+
+ <a class="button" href="/dnsbl/lists/{{ list.slug }}/history">
+ <span class="icon is-small">
+ <i class="fas fa-list-check" aria-hidden="true"></i>
+ </span>
+
+ <span>{{ _("History") }}</span>
+ </a>
</div>
</div>
</section>
(r"/dnsbl/?", StaticHandler, { "template" : "dnsbl/index.html" }),
(r"/dnsbl/lists", dnsbl.ListsHandler),
(r"/dnsbl/lists/(\w+)", dnsbl.ListHandler),
+ (r"/dnsbl/lists/(\w+)/history", dnsbl.ListHistoryHandler),
(r"/dnsbl/lists/(\w+)/reports", dnsbl.ListReportsHandler),
(r"/dnsbl/lists/(\w+)/sources", dnsbl.ListSourcesHandler),
(r"/dnsbl/report", dnsbl.SubmitReportHandler),
self.render("dnsbl/lists/show.html", list=list)
+class ListHistoryHandler(base.AnalyticsMixin, 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 history
+ history = await list.get_history(limit=10)
+
+ # Render the page
+ self.render("dnsbl/lists/history.html", list=list, history=history)
+
+
class ListReportsHandler(base.AnalyticsMixin, BaseHandler):
async def get(self, slug):
# Fetch the list