]> git.ipfire.org Git - dbl.git/commitdiff
api: Generate a RSS feed with the latest reports
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 3 Mar 2026 15:41:46 +0000 (15:41 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 3 Mar 2026 15:41:46 +0000 (15:41 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
configure.ac
src/dbl/api/reports.py
src/dbl/reports.py

index 56965f8f599be64f85f7a323a111af721187447a..220725b6c4d57e8637e5656c9e4804e332354777 100644 (file)
@@ -57,6 +57,7 @@ AX_PYTHON_MODULE([babel], [fatal])
 AX_PYTHON_MODULE([fastapi], [fatal])
 AX_PYTHON_MODULE([httpx], [fatal])
 AX_PYTHON_MODULE([ldap], [fatal])
+AX_PYTHON_MODULE([lxml], [fatal])
 AX_PYTHON_MODULE([publicsuffix2], [fatal])
 AX_PYTHON_MODULE([rich], [fatal])
 AX_PYTHON_MODULE([sqlmodel], [fatal])
index f6bee181cb50071d86a27561a8b8d8685d66e8d1..3fb57d1c55c7051a05822be98e917739349ffad0 100644 (file)
@@ -18,7 +18,9 @@
 #                                                                             #
 ###############################################################################
 
+import datetime
 import fastapi
+import lxml.etree
 import pydantic
 import typing
 import uuid
@@ -183,5 +185,60 @@ async def submit_comment(
        # Create the comment
        return await report.comment(comment=comment.comment, reporter=user)
 
+
+"""
+       RSS
+"""
+@router.get(".rss")
+async def rss(
+               open: bool | None = None,
+               limit: int = 100,
+):
+       # Fetch the current time
+       now = datetime.datetime.utcnow()
+
+       # Atom Feed namespace
+       ATOM_NS = "http://www.w3.org/2005/Atom"
+
+       # Create a new XML object
+       root = lxml.etree.Element("rss", version="2.0", nsmap={ "atom" : ATOM_NS })
+
+       # Meta information
+       channel = lxml.etree.SubElement(root, "channel")
+       lxml.etree.SubElement(channel, "title").text = "IPFire DBL - Reports Feed"
+       lxml.etree.SubElement(channel, "link").text = "https://www.ipfire.org/dbl"
+       lxml.etree.SubElement(channel, "description").text = "Latest Reports"
+       lxml.etree.SubElement(channel, "lastBuildDate").text = now.strftime("%a, %d %b %Y %H:%M:%S +0000")
+
+       # Atom link to itself
+       atom_link = lxml.etree.SubElement(channel, "{%s}link" % ATOM_NS)
+       atom_link.set("href", "https://api.dbl.ipfire.org/reports.rss")
+       atom_link.set("rel",  "self")
+       atom_link.set("type", "application/rss+xml")
+
+       # Fetch the latest reports
+       async for report in backend.reports.get(open=open, limit=limit):
+               # Create a new item
+               item = lxml.etree.SubElement(channel, "item")
+
+               # Title
+               lxml.etree.SubElement(item, "title").text = report.title
+
+               # Set the URL has target link and GUID
+               lxml.etree.SubElement(item, "link").text = report.url
+               lxml.etree.SubElement(item, "guid").text = report.url
+
+               # Set the date
+               lxml.etree.SubElement(item, "pubDate").text = \
+                       report.reported_at.strftime("%a, %d %b %Y %H:%M:%S +0000")
+
+       # Serialize the XML document
+       content = lxml.etree.tostring(root,
+               pretty_print=True, xml_declaration=True, encoding="UTF-8")
+
+       # Send it to the client
+       return fastapi.Response(content=content, media_type="application/rss+xml")
+
+
 # Include our endpoints
 app.include_router(router)
index 74c0c9afe4f24bd313682e12a0f71d48fe8ffcdc..67801b823a784eb5d0b6f5ce709bbd19ebca091a 100644 (file)
@@ -303,6 +303,18 @@ class Report(sqlmodel.SQLModel, database.BackendMixin, table=True):
        # Accepted?
        accepted : bool = False
 
+       # Title
+
+       @property
+       def title(self):
+               """
+                       A brief one-liner to summarize the report
+               """
+               if self.block:
+                       return "[BLOCK] %s" % self.name
+               else:
+                       return "[ALLOW] %s" % self.name
+
        # Permissions check
        async def has_perm(self, user, accept=False):
                """