elif not response.ok:
raise HTTPException(status_code=response.status_code, detail=response.text)
- return templates.TemplateResponse("scoreboard.html", {
+ return templates.TemplateResponse("views/scoreboard.html", {
"base_url" : config.get("base_url"),
"slogan" : config.get("slogan"),
"request" : request,
})
@router.get(config.get("base_url") + "/")
+def index(request: Request):
+ # Get info
+ response = requests.get(f"http://{config.get('host')}:{config.get('port')}{config.get('base_url')}/api/info.json")
+
+ if not response.ok:
+ raise HTTPException(status_code=response.status_code, detail=response.text)
+
+ return templates.TemplateResponse("views/index.html", {
+ "request": request,
+ "info" : response.json()
+ })
+
+@router.get(config.get("base_url") + "/top")
def index(request: Request, domain: str = None, reason: str = None, reverse: str = None):
if domain == "" or reason == "" or reverse == "":
- return fastapi.responses.RedirectResponse("/")
+ raise HTTPException(status_code=500, detail="Insufficient parameter provided")
response = requests.get(f"http://{config.get('host')}:{config.get('port')}{config.get('base_url')}/api/info.json")
block["first_seen"] = datetime.utcfromtimestamp(block["first_seen"]).strftime(config.get("timestamp_format"))
block["last_seen"] = datetime.utcfromtimestamp(block["last_seen"]).strftime(config.get("timestamp_format"))
- return templates.TemplateResponse("index.html", {
+ return templates.TemplateResponse("views/top.html", {
"request": request,
"domain" : domain,
"blocks" : blocklist,
print(f"INFO: Checking {len(federation)} entries from domain='{domain}',software='gotosocial' ...")
for peer in federation:
blocked = peer["domain"].lower()
- # DEBUG: print("DEBUG: BEFORE blocked:", blocked)
+ print("DEBUG: BEFORE blocked:", blocked)
blocked = fba.tidyup_domain(blocked)
- # DEBUG: print("DEBUG: AFTER blocked:", blocked)
+ print("DEBUG: AFTER blocked:", blocked)
if blocked == "":
print("WARNING: blocked is empty:", domain)
continue
elif blacklist.is_blacklisted(blocked):
- # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
+ print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
continue
elif blocked.count("*") > 0:
# GTS does not have hashes for obscured domains, so we have to guess it
print(f"WARNING: blocked='{blocked}',software='gotosocial' is not a valid domain name - skipped!")
continue
- # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
+ print("DEBUG: Looking up instance by domain:", blocked)
if not validators.domain(blocked):
print(f"WARNING: blocked='{blocked}',software='gotosocial' is not a valid domain name - skipped!")
continue
elif not instances.is_registered(blocked):
- # DEBUG: print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., domain='{domain}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
+ print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., domain='{domain}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
instances.add(blocked, domain, inspect.currentframe().f_code.co_name, nodeinfo_url)
if not blocks.is_instance_blocked(domain, blocked, "reject"):
- # DEBUG: print(f"DEBUG: domain='{domain}' is blocking '{blocked}' for unknown reason at this point")
+ print(f"DEBUG: domain='{domain}' is blocking '{blocked}' for unknown reason at this point")
blocks.add_instance(domain, blocked, "unknown", "reject")
blockdict.append({
"reason" : None
})
else:
- # DEBUG: print(f"DEBUG: Updating block last seen for domain='{domain}',blocked='{blocked}' ...")
+ print(f"DEBUG: Updating block last seen for domain='{domain}',blocked='{blocked}' ...")
blocks.update_last_seen(domain, blocked, "reject")
if "public_comment" in peer:
- # DEBUG: print("DEBUG: Updating block reason:", domain, blocked, peer["public_comment"])
+ print("DEBUG: Updating block reason:", domain, blocked, peer["public_comment"])
blocks.update_reason(peer["public_comment"], domain, blocked, "reject")
for entry in blockdict:
if entry["blocked"] == blocked:
- # DEBUG: print(f"DEBUG: Setting block reason for blocked='{blocked}':'{peer['public_comment']}'")
+ print(f"DEBUG: Setting block reason for blocked='{blocked}':'{peer['public_comment']}'")
entry["reason"] = peer["public_comment"]
- # DEBUG: print("DEBUG: Committing changes ...")
+ print("DEBUG: Committing changes ...")
fba.connection.commit()
except Exception as e:
print(f"ERROR: domain='{domain}',software='gotosocial',exception[{type(e)}]:'{str(e)}'")
- # DEBUG: print("DEBUG: EXIT!")
+ print("DEBUG: EXIT!")
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
-import sys
-
from fba import config
from fba import fba
from fba import instances
def get_peers(domain: str) -> list:
- # DEBUG: print(f"DEBUG: domain({len(domain)})={domain},software={software} - CALLED!")
+ # DEBUG: print(f"DEBUG: domain({len(domain)})={domain},software='lemmy' - CALLED!")
if type(domain) != str:
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
raise ValueError(f"Parameter 'domain' is empty")
- # DEBUG: print(f"DEBUG: domain='{domain}' is Lemmy, fetching JSON ...")
peers = list()
try:
+ # DEBUG: print(f"DEBUG: domain='{domain}' is Lemmy, fetching JSON ...")
response = fba.get_response(domain, "/api/v3/site", fba.api_headers, (config.get("connection_timeout"), config.get("read_timeout")))
data = fba.json_from_response(response)
# DEBUG: print(f"DEBUG: response.ok={response.ok},response.status_code='{response.status_code}',data[]='{type(data)}'")
if not response.ok or response.status_code >= 400:
print("WARNING: Could not reach any JSON API:", domain)
- fba.update_last_error(domain, response)
+ instances.update_last_error(domain, response)
elif response.ok and isinstance(data, list):
print(f"UNSUPPORTED: domain='{domain}' returned a list: '{data}'")
- sys.exit(255)
elif "federated_instances" in data:
# DEBUG: print(f"DEBUG: Found federated_instances for domain='{domain}'")
peers = peers + fba.add_peers(data["federated_instances"])
# DEBUG: print("DEBUG: Added instance(s) to peers")
else:
print("WARNING: JSON response does not contain 'federated_instances':", domain)
- fba.update_last_error(domain, response)
+ instances.update_last_error(domain, response)
except BaseException as e:
print(f"WARNING: Exception during fetching JSON: domain='{domain}',exception[{type(e)}]:'{str(e)}'")
)
except BaseException as e:
print("ERROR: Cannot fetch from domain:", domain, e)
- fba.update_last_error(domain, e)
+ instances.update_last_error(domain, e)
return {}
for header in doc.find_all("h3"):
}
def fetch_blocks(domain: str, origin: str, nodeinfo_url: str):
- print(f"DEBUG: domain='{domain}',origin='{origin}',nodeinfo_url='{nodeinfo_url}' - CALLED!")
+ # DEBUG: print(f"DEBUG: domain='{domain}',origin='{origin}',nodeinfo_url='{nodeinfo_url}' - CALLED!")
if type(domain) != str:
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
# DEBUG: print(f"DEBUG: fetched({len(fetched)})[]={type(fetched)}")
if isinstance(fetched, dict) and "error" in fetched and "message" in fetched["error"]:
print(f"WARNING: post_json_api() returned error: {fetched['error']['message']}")
- fba.update_last_error(domain, fetched["error"]["message"])
+ instances.update_last_error(domain, fetched["error"]["message"])
break
already = 0
from fba import instances
def get_peers(domain: str) -> list:
- # DEBUG: print(f"DEBUG: domain({len(domain)})={domain},software={software} - CALLED!")
+ # DEBUG: print(f"DEBUG: domain({len(domain)})={domain},software='peertube' - CALLED!")
if type(domain) != str:
raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
elif domain == "":
--- /dev/null
+<!DOCTYPE html>
+<head>
+ <title>fedi-block-api - {% block title %}{% endblock %}</title>
+ {% block rss %}
+ <link rel="alternate" type="application/rss+xml" title="RSS Feed for latest blocked instances" href="{{base_url}}/rss" />
+ {% endblock %}
+ <style>
+ body {
+ background-color: #000022;
+ color: #ffffff;
+ text-align: center;
+ font-family: sans;
+ }
+ .block_level {
+ background-color: #1c1c3c;
+ width: 100%;
+ margin: auto;
+ margin-top: 10px;
+ }
+ table {
+ width: 100%;
+ background-color: #2d2d4d;
+ border-spacing: 0px;
+ }
+ table tr:nth-of-type(2n) {
+ background-color: #1c1c3c;
+ }
+ table td {
+ padding: 4px;
+ }
+ .block_level table td:nth-of-type(1), .block_level table td:nth-of-type(2),
+ .block_level table td:nth-of-type(4), .block_level table td:nth-of-type(5) {
+ white-space: nowrap;
+ }
+ .block {
+ background-color: #2d2d4d;
+ padding: 5px;
+ margin: 5px;
+ }
+ a {
+ color: #ffffff;
+ }
+ a.listlink {
+ text-decoration: none;
+ font-size: 0.8em;
+ }
+ input[type="text"], input[type="submit"] {
+ padding: 5px;
+ border-radius: 5px;
+ color: white;
+ background: #445;
+ font-size: 16px;
+ }
+
+ input[type="text"]:hover {
+ border-color: #f08;
+ }
+
+ input[type="submit"] {
+ cursor: pointer;
+ }
+
+ input[type="submit"]:hover {
+ border-color: #f08;
+ }
+
+ span[title] {
+ text-decoration: underline dotted;
+ }
+
+ #content {
+ margin-bottom: 20px;
+ }
+
+ #header {
+ margin-bottom: 20px;
+ }
+
+ .scoreboard {
+ margin-left: auto;
+ margin-right: auto;
+ min-width: 50%;
+ width: 50em;
+ }
+ </style>
+</head>
+<body>
+ <div id="header">
+ {% block header %}<h1>Page has no header</h1>{% endblock %}
+ </div>
+
+ <div id="content">
+ {% block content %}{% endblock %}
+ </div>
+
+ <div id="footer">
+ {% block footer %}
+ source code: <a href="https://git.mxchange.org/?p=fba.git;a=summary" rel="source" target="_blank">git.mxchange.org</a><br /><br />
+ {% if info %}
+ {{info.slogan}}
+ {% elif slogan %}
+ {{slogan}}
+ {% else %}
+ NO SLOGAN WAS FOUND!!!
+ {% endif %}
+ {% endblock %}
+ </p>
+</body>
+</html>
+++ /dev/null
-<!DOCTYPE html>
-<head>
- <title>fedi-block-api{% if domain %} - Instances that block {{domain}}{% elif reverse %} - Instances that are blocked by {{reverse}}{% endif %}</title>
- <link rel="alternate" type="application/rss+xml" title="RSS Feed for latest blocked instances" href="{{base_url}}/rss" />
- {% if domain %}
- <link rel="alternate" type="application/rss+xml" title="RSS Feed for blocked domain {{domain}}" href="{{base_url}}/rss?domain={{domain}}" />
- {% elif reverse %}
- <link rel="alternate" type="application/rss+xml" title="RSS Feed for blocking domain {{reverse}}" href="{{base_url}}/rss?reverse={{reverse}}" />
- {% endif %}
- <style>
- body {
- background-color: #000022;
- color: #ffffff;
- text-align: center;
- font-family: sans;
- }
- .block_level {
- background-color: #1c1c3c;
- width: 100%;
- margin: auto;
- margin-top: 10px;
- }
- table {
- width: 100%;
- background-color: #2d2d4d;
- border-spacing: 0px;
- }
- table tr:nth-of-type(2n) {
- background-color: #1c1c3c;
- }
- table td {
- padding: 4px;
- }
- .block_level table td:nth-of-type(1), .block_level table td:nth-of-type(2),
- .block_level table td:nth-of-type(4), .block_level table td:nth-of-type(5) {
- white-space: nowrap;
- }
- .block {
- background-color: #2d2d4d;
- padding: 5px;
- margin: 5px;
- }
- a {
- color: #ffffff;
- }
- a.listlink {
- text-decoration: none;
- font-size: 0.8em;
- }
- input[type="text"], input[type="submit"] {
- padding: 5px;
- border-radius: 5px;
- color: white;
- background: #445;
- font-size: 16px;
- }
-
- input[type="text"]:hover {
- border-color: #f08;
- }
-
- input[type="submit"] {
- cursor: pointer;
- }
-
- input[type="submit"]:hover {
- border-color: #f08;
- }
-
- span[title] {
- text-decoration: underline dotted;
- }
- </style>
-</head>
-<body>
- {% if reason or domain or reverse %}
- {% if reason %}
- <h1>Instances that use "{{reason}}" in their reason</h1>
- {% elif reverse %}
- <h1>Instances that are blocked by {{reverse}}</h1>
- {% elif domain %}
- <h1>Instances that block {{domain}}</h1>
- {% endif %}
- {% for block_level in blocks %}
- <div class="block_level" id="{{block_level}}">
- <h2>{{block_level}} ({{blocks[block_level]|length}})</h2>
- <table>
- <th>Blocker</th>
- <th>{% if block_level == 'accept' %}Accepted{% else %}Blocked{% endif %}</th>
- <th>Reason</th>
- <th>First added</th>
- <th>Last seen</th>
- {% for block in blocks[block_level] %}
- <tr>
- <td>
- <a href="https://{{block['blocker']}}" rel="nofollow noopener noreferrer">{{block['blocker']}}</a>
- {% if reason or domain %}<a class="listlink" href="{{base_url}}/?reverse={{block['blocker']}}">↘</a>{% endif %}
- </td>
- <td>
- <a href="https://{{domain or block['blocked']}}" rel="nofollow noopener noreferrer">{{block['blocked']}}</a>
- {% if reason or reverse %}<a class="listlink" href="{{base_url}}/?domain={{domain or block['blocked']}}">↘</a>{% endif %}
- </td>
- <td>{{block['reason']}}</td>
- <td>{{block['first_seen']}}</td>
- <td>{{block['last_seen']}}</td>
- </tr>
- {% endfor %}
- </table>
- </div>
- {% endfor %}
- <h2>Infos:</h2>
- <p>
- <a href="{{base_url}}/">Index</a> /
- source code: <a href="https://git.mxchange.org/?p=fba.git;a=summary" rel="source" target="_blank">git.mxchange.org</a><br /><br />
- {{info.slogan}}
- </p>
- {% else %}
- <h1>Enter a Domain</h1>
- <form>
- <input type="text" name="domain" placeholder="example.com" required="required" />
- <input type="submit" value="Submit" />
- </form>
-
- <h1>Enter a Reason</h1>
- <form>
- <input type="text" name="reason" placeholder="free speech" required="required" />
- <input type="submit" value="Submit" />
- </form>
-
- <h1>Reverse search</h1>
- <form>
- <input type="text" name="reverse" placeholder="example.com" required="required" />
- <input type="submit" value="Submit" />
- </form>
-
- <h1>Scoreboards:</h1>
- <p>
- <a href="{{base_url}}/scoreboard?blockers=50">top 50 defederating</a> /
- <a href="{{base_url}}/scoreboard?blocked=50">defederated instances</a> /
- <a href="{{base_url}}/scoreboard?reference=50">referencing instances</a> /
- <a href="{{base_url}}/scoreboard?software=50">used software</a> /
- <a href="{{base_url}}/scoreboard?originator=10">originators</a>
- </p>
-
- <h2>Infos:</h2>
- <p>
- known instances: {{info.known_instances}}<br/>
- indexed instances: {{info.indexed_instances}}<br/>
- blocks recorded: {{info.blocks_recorded}}<br/>
- errorous instances: {{info.errorous_instances}}<br/>
- source code: <a href="https://git.mxchange.org/?p=fba.git;a=summary" rel="source" target="_blank">git.mxchange.org</a><br /><br />
- {{info.slogan}}
- </p>
- {% endif %}
-</body>
-</html>
+++ /dev/null
-<!DOCTYPE html>
-<head>
- <title>fedi-block-api - scoreboard - {% if software %}TOP {{software}} used software{% elif originator %}TOP {{originator}} scripts{% elif reference %}TOP {{reference}} referencing instances{% elif blocked %}TOP {{blocked}} deferated instances{% elif blockers %}TOP {{blockers}} deferating instances{% endif %}</title>
- <link rel="alternate" type="application/rss+xml" title="RSS Feed for latest blocked instances" href="{{base_url}}/rss" />
- <style>
- body {
- background-color: #000022;
- color: #ffffff;
- text-align: center;
- font-family: sans;
- }
- .block_level {
- background-color: #1c1c3c;
- width: 100%;
- margin: auto;
- margin-top: 10px;
- }
- .scoreboard {
- background-color: #1c1c3c;
- width: 40em;
- padding: 5px;
- margin: auto;
- margin-top: 10px;
- }
- table {
- width: 100%;
- background-color: #2d2d4d;
- border-spacing: 0px;
- }
- table tr:nth-of-type(2n) {
- background-color: #1c1c3c;
- }
- table td {
- padding: 4px;
- }
- .block_level table td:nth-of-type(1), .block_level table td:nth-of-type(2),
- .block_level table td:nth-of-type(4), .block_level table td:nth-of-type(5) {
- white-space: nowrap;
- }
- .block {
- background-color: #2d2d4d;
- padding: 5px;
- margin: 5px;
- }
- a {
- color: #ffffff;
- }
- a.listlink {
- text-decoration: none;
- font-size: 0.8em;
- }
- .info {
- margin-top: 25px;
- }
- input[type="text"], input[type="submit"] {
- padding: 5px;
- border-radius: 5px;
- color: white;
- background: #445;
- font-size: 16px;
- }
-
- input[type="text"]:hover {
- border-color: #f08;
- }
-
- input[type="submit"] {
- cursor: pointer;
- }
-
- input[type="submit"]:hover {
- border-color: #f08;
- }
-
- span[title] {
- text-decoration: underline dotted;
- }
- </style>
-</head>
-<body>
- {% if blockers %}
- <h1>Top {{blockers}} defederating instances</h1>
- {% elif blocked %}
- <h1>Top {{blocked}} defederated instances</h1>
- {% elif reference %}
- <h1>Top {{reference}} referencing instances</h1>
- {% elif software %}
- <h1>Top {{software}} used software</h1>
- {% elif originator %}
- <h1>TOP {{originator}} scripts</h1>
- {% endif %}
- <div class="scoreboard">
- <table>
- <th>№</th>
- <th>{% if software %}Software{% else %}Instance{% endif %}</th>
- <th>{% if reference %}References{% elif software %}Total{% else %}Blocks{% endif %}</th>
- {% for entry in scores %}
- <tr>
- <td>{{loop.index}}</td>
- <td>
- {% if software %}
- {{entry['domain']}}
- {% elif originator %}
- {{entry['domain']}}
- {% elif entry['domain'] == None %}
- -
- {% else %}
- <a href="{{base_url}}/?{% if blockers %}reverse{% elif blocked or reference %}domain{% endif %}={{entry['domain']}}" rel="nofollow noopener noreferrer">{{entry['domain']}}</a>
- <a class="listlink" href="https://{{entry['domain']}}" rel="external" target="_blank">↗</a>
- {% endif %}
- </td>
- <td>{{entry['highscore']}}</td>
- </tr>
- {% endfor %}
- </table>
- </div>
- <div class="info">
- <a href="{{base_url}}/">Index</a> /
- source code: <a href="https://git.mxchange.org/?p=fba.git;a=summary" rel="source" target="_blank">git.mxchange.org</a><br /><br />
- {{slogan}}
- </div>
-</body>
-</html>
--- /dev/null
+{% extends "base.html" %}
+
+{% block title %}Index{% endblock %}
+
+{% block header %}<h1>Welcome to FBA</h1>{% endblock %}
+
+{% block content %}
+ <h2>Enter a Domain</h2>
+ <form action="top">
+ <input type="text" name="domain" placeholder="example.com" required="required" />
+ <input type="submit" value="Submit" />
+ </form>
+
+ <h2>Enter a Reason</h2>
+ <form action="top">
+ <input type="text" name="reason" placeholder="free speech" required="required" />
+ <input type="submit" value="Submit" />
+ </form>
+
+ <h2>Reverse search</h2>
+ <form action="top">
+ <input type="text" name="reverse" placeholder="example.com" required="required" />
+ <input type="submit" value="Submit" />
+ </form>
+
+ <h2>Scoreboards:</h2>
+ <p>
+ <a href="{{base_url}}/scoreboard?blockers=50">top 50 defederating</a> /
+ <a href="{{base_url}}/scoreboard?blocked=50">defederated instances</a> /
+ <a href="{{base_url}}/scoreboard?reference=50">referencing instances</a> /
+ <a href="{{base_url}}/scoreboard?software=50">used software</a> /
+ <a href="{{base_url}}/scoreboard?originator=10">originators</a>
+ </p>
+{% endblock %}
+{% block footer %}
+ <h2>Infos:</h2>
+ <p>
+ known instances: {{info.known_instances}}<br/>
+ indexed instances: {{info.indexed_instances}}<br/>
+ blocks recorded: {{info.blocks_recorded}}<br/>
+ errorous instances: {{info.errorous_instances}}<br/>
+ </p>
+ {{ super() }}
+{% endblock %}
--- /dev/null
+{% extends "base.html" %}
+
+{% block title %}scoreboard - {% if software %}TOP {{software}} used software{% elif originator %}TOP {{originator}} scripts{% elif reference %}TOP {{reference}} referencing instances{% elif blocked %}TOP {{blocked}} deferated instances{% elif blockers %}TOP {{blockers}} deferating instances{% endif %}{% endblock %}
+
+{% block header %}
+ {% if blockers %}
+ <h1>Top {{blockers}} defederating instances</h1>
+ {% elif blocked %}
+ <h1>Top {{blocked}} defederated instances</h1>
+ {% elif reference %}
+ <h1>Top {{reference}} referencing instances</h1>
+ {% elif software %}
+ <h1>Top {{software}} used software</h1>
+ {% elif originator %}
+ <h1>TOP {{originator}} scripts</h1>
+ {% endif %}
+{% endblock %}
+
+{% block content %}
+ <div class="scoreboard">
+ <table>
+ <thead>
+ <th>№</th>
+ <th>{% if software %}Software{% else %}Instance{% endif %}</th>
+ <th>{% if reference %}References{% elif software %}Total{% else %}Blocks{% endif %}</th>
+ </thead>
+
+ <tbody>
+ {% for entry in scores %}
+ <tr>
+ <td>{{loop.index}}</td>
+ <td>
+ {% if software %}
+ {{entry['domain']}}
+ {% elif originator %}
+ {{entry['domain']}}
+ {% elif entry['domain'] == None %}
+ -
+ {% else %}
+ <a href="{{base_url}}/top?{% if blockers %}reverse{% elif blocked or reference %}domain{% endif %}={{entry['domain']}}" rel="nofollow noopener noreferrer">{{entry['domain']}}</a>
+ <a class="listlink" href="https://{{entry['domain']}}" rel="external" target="_blank">↗</a>
+ {% endif %}
+ </td>
+ <td>{{entry['highscore']}}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ </div>
+{% endblock %}
+
+{% block footer %}
+ <a href="{{base_url}}/">Index</a> /
+ {{ super() }}
+{% endblock %}
--- /dev/null
+{% extends "base.html" %}
+
+{% block title %}{% if domain %} - Instances that block {{domain}}{% elif reverse %} - Instances that are blocked by {{reverse}}{% endif %}{% endblock %}
+
+{% block rss %}
+ {{ super() }}
+ {% if domain %}
+ <link rel="alternate" type="application/rss+xml" title="RSS Feed for blocked domain {{domain}}" href="{{base_url}}/rss?domain={{domain}}" />
+ {% elif reverse %}
+ <link rel="alternate" type="application/rss+xml" title="RSS Feed for blocking domain {{reverse}}" href="{{base_url}}/rss?reverse={{reverse}}" />
+ {% endif %}
+{% endblock %}
+
+{% block header %}
+ {% if reason %}
+ <h1>Instances that use "{{reason}}" in their reason</h1>
+ {% elif reverse %}
+ <h1>Instances that are blocked by {{reverse}}</h1>
+ {% elif domain %}
+ <h1>Instances that block {{domain}}</h1>
+ {% endif %}
+{% endblock %}
+
+{% block content %}
+ {% for block_level in blocks %}
+ <div class="block_level" id="{{block_level}}">
+ <h2>{{block_level}} ({{blocks[block_level]|length}})</h2>
+ <table>
+ <thead>
+ <th>Blocker</th>
+ <th>{% if block_level == 'accept' %}Accepted{% else %}Blocked{% endif %}</th>
+ <th>Reason</th>
+ <th>First added</th>
+ <th>Last seen</th>
+ </thead>
+
+ <tbody>
+ {% for block in blocks[block_level] %}
+ <tr>
+ <td>
+ <a href="https://{{block['blocker']}}" rel="nofollow noopener noreferrer">{{block['blocker']}}</a>
+ {% if reason or domain %}<a class="listlink" href="{{base_url}}/top?reverse={{block['blocker']}}">↘</a>{% endif %}
+ </td>
+ <td>
+ <a href="https://{{domain or block['blocked']}}" rel="nofollow noopener noreferrer">{{block['blocked']}}</a>
+ {% if reason or reverse %}<a class="listlink" href="{{base_url}}/top?domain={{domain or block['blocked']}}">↘</a>{% endif %}
+ </td>
+ <td>{{block['reason']}}</td>
+ <td>{{block['first_seen']}}</td>
+ <td>{{block['last_seen']}}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ </div>
+ {% endfor %}
+{% endblock %}
+{% block footer %}
+ <a href="{{base_url}}/">Index</a> /
+ {{ super() }}
+{% endblock %}