From ceb1c3cf3db91e6bf4f59bbbbfc6a44bc72972a5 Mon Sep 17 00:00:00 2001 From: Divided by Zer0 Date: Tue, 27 Jun 2023 16:11:51 +0200 Subject: [PATCH] Feat: Allows instances to pull a badge (#13) --- README.md | 16 ++++++++- fediseer/apis/v1/__init__.py | 3 ++ fediseer/apis/v1/activitypub.py | 6 ++-- fediseer/apis/v1/badges.py | 55 ++++++++++++++++++++++++++++++ fediseer/assets/README.md | 5 +++ fediseer/assets/crossed-chains.svg | 1 + fediseer/assets/thumb-up.svg | 1 + fediseer/badges.py | 49 ++++++++++++++++++++++++++ requirements.txt | 4 ++- test.py | 50 ++++++--------------------- 10 files changed, 146 insertions(+), 44 deletions(-) create mode 100644 fediseer/apis/v1/badges.py create mode 100644 fediseer/assets/README.md create mode 100644 fediseer/assets/crossed-chains.svg create mode 100644 fediseer/assets/thumb-up.svg create mode 100644 fediseer/badges.py diff --git a/README.md b/README.md index a85b066..391c387 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,18 @@ It's reliant on the [Lemmy Fediverse Observer](https://lemmy.fediverse.observer/ The currently running instance is on https://fediseer.com -See devlog: https://dbzer0.com/blog/overseer-a-fediverse-chain-of-trust/ \ No newline at end of file +See devlog: https://dbzer0.com/blog/overseer-a-fediverse-chain-of-trust/ + +# Badges + +You can retrieve and display a badge for your fediverse domain by requesting a .svg for it on a special endpoint + +`/v1/badges/guarantees/{domain}.svg` will give you an badge of guarantee, mentioning the domains which guaranteed for your domain + +Example: +[![](http://fediseer.com/api/v1/badges/guarantees/lemmy.dbzer0.com.svg)](https://fediseer.com/api/v1/whitelist/lemmy.dbzer0.com) + +`/v1/badges/endorsements/{domain}.svg` will give you an badge of endorsements, providing a count of how many other the fediverse domains guaranteed for yours + +Example: + [![](http://fediseer.com/api/v1/badges/endorsements/lemmy.dbzer0.com.svg)](https://fediseer.com/api/v1/endorsements/lemmy.dbzer0.com) diff --git a/fediseer/apis/v1/__init__.py b/fediseer/apis/v1/__init__.py index 9742f26..de64bdd 100644 --- a/fediseer/apis/v1/__init__.py +++ b/fediseer/apis/v1/__init__.py @@ -3,6 +3,7 @@ import fediseer.apis.v1.whitelist as whitelist import fediseer.apis.v1.endorsements as endorsements import fediseer.apis.v1.guarantees as guarantees import fediseer.apis.v1.activitypub as activitypub +import fediseer.apis.v1.badges as badges import fediseer.apis.v1.find as find from fediseer.apis.v1.base import api @@ -16,3 +17,5 @@ api.add_resource(endorsements.Endorsements, "/endorsements/") api.add_resource(endorsements.Approvals, "/approvals/") api.add_resource(guarantees.Guarantors, "/guarantors/") api.add_resource(guarantees.Guarantees, "/guarantees/") +api.add_resource(badges.GuaranteeBadge, "/badges/guarantees/.svg") +api.add_resource(badges.EndorsementBadge, "/badges/endorsements/.svg") diff --git a/fediseer/apis/v1/activitypub.py b/fediseer/apis/v1/activitypub.py index 7b9541d..857389b 100644 --- a/fediseer/apis/v1/activitypub.py +++ b/fediseer/apis/v1/activitypub.py @@ -45,14 +45,14 @@ class Inbox(Resource): self.args = self.post_parser.parse_args() json_payload = request.get_json() actor = json_payload["actor"] - if json_payload.get("Type") == "Follow": + if json_payload.get("type") == "Follow": logger.info(f"Fediseer is now followed from: {actor}") - elif json_payload.get("Type") == "Delete": + elif json_payload.get("type") == "Delete": pass else: try: message = json_payload["object"]["content"] logger.info(f"Fediseer Inbox Received: From: {actor} | {message}") except: - logger.info(f"Received unexpected invox payload: {json_payload}") + logger.info(f"Received unexpected inbox payload: {json_payload}") return {"message": "delivered"}, 200 diff --git a/fediseer/apis/v1/badges.py b/fediseer/apis/v1/badges.py new file mode 100644 index 0000000..666bf44 --- /dev/null +++ b/fediseer/apis/v1/badges.py @@ -0,0 +1,55 @@ +from fediseer.apis.v1.base import * +from fediseer.badges import generate_endorsements_badge, generate_guarantee_badge +from sqlalchemy.orm import aliased +from fediseer.classes.instance import Guarantee, Endorsement +from flask import make_response +from sqlalchemy import func + +class GuaranteeBadge(Resource): + + def get(self, domain): + '''Retrieve Guarantee Badge SVG + ''' + guaranteed_instance_alias = aliased(Instance) + query = db.session.query( + Instance.domain, + guaranteed_instance_alias.domain, + ).join( + Guarantee, Instance.id == Guarantee.guaranteed_id + ).join( + guaranteed_instance_alias, guaranteed_instance_alias.id == Guarantee.guarantor_id + ).filter( + Instance.domain == domain + ) + guarantor = query.first() + if guarantor is None: + svg = generate_guarantee_badge(domain, None) + else: + svg = generate_guarantee_badge(domain, guarantor[1]) + response = make_response(svg) + response.headers['Content-Type'] = 'image/svg+xml' + return response + +class EndorsementBadge(Resource): + + def get(self, domain): + '''Retrieve Endorsement Badge SVG + ''' + query = db.session.query( + Instance.domain, + func.count(Endorsement.id) # Count the number of endorsements + ).join( + Endorsement, Instance.id == Endorsement.endorsed_id + ).filter( + Instance.domain == domain + ).group_by( + Instance.domain + ) + endorsements = query.first() + if endorsements is None: + svg = generate_endorsements_badge(domain, 0) + else: + svg = generate_endorsements_badge(domain, endorsements[1]) + response = make_response(svg) + response.headers['Content-Type'] = 'image/svg+xml' + return response diff --git a/fediseer/assets/README.md b/fediseer/assets/README.md new file mode 100644 index 0000000..367e6fa --- /dev/null +++ b/fediseer/assets/README.md @@ -0,0 +1,5 @@ +# Assets + +* [crossed-chains](https://game-icons.net/1x1/lorc/crossed-chains.html) +* [thumb-up](https://game-icons.net/1x1/delapouite/thumb-up.html) +* \ No newline at end of file diff --git a/fediseer/assets/crossed-chains.svg b/fediseer/assets/crossed-chains.svg new file mode 100644 index 0000000..607aedd --- /dev/null +++ b/fediseer/assets/crossed-chains.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fediseer/assets/thumb-up.svg b/fediseer/assets/thumb-up.svg new file mode 100644 index 0000000..a307c5e --- /dev/null +++ b/fediseer/assets/thumb-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fediseer/badges.py b/fediseer/badges.py new file mode 100644 index 0000000..e2bf55c --- /dev/null +++ b/fediseer/badges.py @@ -0,0 +1,49 @@ +from pybadges import badge +from loguru import logger +import base64 + +with open('fediseer/assets/crossed-chains.svg', 'rb') as file: + svg_data = file.read() + base64_data = base64.b64encode(svg_data).decode('utf-8') + embed_guarantee = f"data:image/svg+xml;base64,{base64_data}" +with open('fediseer/assets/thumb-up.svg', 'rb') as file: + svg_data = file.read() + base64_data = base64.b64encode(svg_data).decode('utf-8') + embed_endorsement = f"data:image/svg+xml;base64,{base64_data}" + +def generate_guarantee_badge(domain: str, guarantor: str): + left_color = "SeaGreen" + right_text=guarantor + if guarantor is None: + left_color = "red" + right_text="None" + guarantee_badge = badge( + left_text="Guarantee", + right_text=right_text, + left_color=left_color, + logo=embed_guarantee, + whole_title="Fediseer Guarantee", + left_title=domain, + right_title="Guarantor", + left_link="https://fediseer.com", + right_link=f"https://fediseer.com/api/v1/endorsements/{domain}", + ) + return guarantee_badge + +def generate_endorsements_badge(domain: str, count: int): + left_color = "DarkOliveGreen" + right_text=str(count) + if count == 0: + left_color = "red" + endorsements_badge = badge( + left_text="Endorsements", + right_text=right_text, + left_color=left_color, + logo=embed_endorsement, + whole_title="Fediseer Endorsements", + left_title=domain, + right_title="Endorsements Count", + left_link="https://fediseer.com", + right_link=f"https://fediseer.com/api/v1/whitelist/{domain}", + ) + return endorsements_badge diff --git a/requirements.txt b/requirements.txt index 014b44e..e0b30a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,6 @@ SQLAlchemy~=1.4.44 psycopg2-binary regex pythorhead>=0.8.2 -bleach \ No newline at end of file +bleach +boto3 +pybadges \ No newline at end of file diff --git a/test.py b/test.py index c6cdd9c..4b98bef 100644 --- a/test.py +++ b/test.py @@ -1,41 +1,13 @@ -import requests -import json -from datetime import datetime -import OpenSSL.crypto -import base64 -import hashlib -import sys -import uuid +from dotenv import load_dotenv +import os +import logging -with open('lemmy-hello-world.json', 'r') as file: - document = json.loads(file.read()) -document["id"] = f"https://fediseer.com/{uuid.uuid4()}" -document["object"]["id"] = f"https://fediseer.com/{uuid.uuid4()}" -document["object"]["published"] = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") -document = json.dumps(document, indent=4) -print(document) -digest = hashlib.sha256(document.encode('utf-8')).digest() -encoded_digest = base64.b64encode(digest).decode('utf-8') -digest_header = "SHA-256=" + encoded_digest -date = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT') +load_dotenv() -with open('private.pem', 'rb') as file: - private_key_data = file.read() - private_key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, private_key_data) - -signed_string = f"(request-target): post /inbox\nhost: overctrl.dbzer0.com\ndate: {date}\ndigest: {digest_header}" -signature = OpenSSL.crypto.sign(private_key, signed_string.encode('utf-8'), 'sha256') -encoded_signature = base64.b64encode(signature).decode('utf-8') - -header = f'keyId="https://fediseer.com/api/v1/user/fediseer",headers="(request-target) host date digest",signature="{encoded_signature}"' -headers = { - 'Host': 'overctrl.dbzer0.com', - 'Date': date, - 'Signature': header, - 'Digest': digest_header, - 'Content-Type': 'application/ld+json; profile="http://www.w3.org/ns/activitystreams"' -} -url = 'https://overctrl.dbzer0.com/inbox' -response = requests.post(url, data=document, headers=headers) -print('Response Status:', response.status_code) -print('Response Body:', response.text) +from fediseer.argparser import args +from fediseer.flask import OVERSEER +from loguru import logger +from fediseer.badges import generate_guarantee_badge +if __name__ == "__main__": + # Only setting this for the WSGI logs + print(generate_guarantee_badge("lemmy.dbzer0.com", "fediseer.com"))