From ae248f6fdc652a240a7879dae0e45074b2126dde Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 28 Jan 2020 00:44:51 -0800 Subject: [PATCH] better urls --- fedireads/federation.py | 10 ++++++-- fedireads/migrations/0001_initial.py | 5 +++- fedireads/models.py | 37 ++++++++++++++-------------- fedireads/urls.py | 13 +++++++--- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/fedireads/federation.py b/fedireads/federation.py index 45a352964..41c360402 100644 --- a/fedireads/federation.py +++ b/fedireads/federation.py @@ -54,10 +54,15 @@ def get_actor(request, username): 'preferredUsername': user.localname, 'inbox': format_inbox(user), 'followers': '%s/followers' % user.actor, + 'following': '%s/following' % user.actor, + 'summary': user.summary, 'publicKey': { 'id': '%s/#main-key' % user.actor, 'owner': user.actor, 'publicKeyPem': user.public_key, + }, + 'endpoints': { + 'sharedInbox': 'https://%s/inbox' % DOMAIN, } }) @@ -149,7 +154,7 @@ def handle_incoming_follow(activity): ''' # figure out who they want to follow to_follow = re.sub( - r'https?://([\w\.]+)/api/u/(\w+)', + r'https?://([\w\.]+)/user/(\w+)', r'\2@\1', activity['object'] ) @@ -243,6 +248,7 @@ def handle_shelve(user, book, shelf): 'id': shelf.activitypub_id } } + # TODO: this should be getting shared inboxes and deduplicating recipients = [format_inbox(u) for u in user.followers.all()] models.ShelveActivity( @@ -363,7 +369,7 @@ def broadcast(sender, action, recipients): def sign_and_send(sender, action, destination): ''' crpyto whatever and http junk ''' - inbox_fragment = '/api/u/%s/inbox' % (sender.localname) + inbox_fragment = sender.inbox.replace('https://%s' % DOMAIN, '') now = datetime.utcnow().isoformat() message_to_sign = '''(request-target): post %s host: https://%s diff --git a/fedireads/migrations/0001_initial.py b/fedireads/migrations/0001_initial.py index f6c3de2ed..beda8d365 100644 --- a/fedireads/migrations/0001_initial.py +++ b/fedireads/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.2 on 2020-01-28 06:21 +# Generated by Django 3.0.2 on 2020-01-28 08:28 from django.conf import settings import django.contrib.auth.models @@ -36,6 +36,9 @@ class Migration(migrations.Migration): ('public_key', models.TextField(blank=True, null=True)), ('api_key', models.CharField(blank=True, max_length=255, null=True)), ('actor', models.CharField(max_length=255)), + ('inbox', models.CharField(max_length=255)), + ('outbox', models.CharField(max_length=255)), + ('summary', models.TextField(blank=True, null=True)), ('local', models.BooleanField(default=True)), ('localname', models.CharField(blank=True, max_length=255, null=True, unique=True)), ('avatar', models.ImageField(blank=True, null=True, upload_to='uploads/')), diff --git a/fedireads/models.py b/fedireads/models.py index c682775f3..0b030029f 100644 --- a/fedireads/models.py +++ b/fedireads/models.py @@ -14,6 +14,9 @@ class User(AbstractUser): public_key = models.TextField(blank=True, null=True) api_key = models.CharField(max_length=255, blank=True, null=True) actor = models.CharField(max_length=255) + inbox = models.CharField(max_length=255) + outbox = models.CharField(max_length=255) + summary = models.TextField(blank=True, null=True) local = models.BooleanField(default=True) localname = models.CharField( max_length=255, @@ -27,25 +30,23 @@ class User(AbstractUser): created_date = models.DateTimeField(auto_now_add=True) updated_date = models.DateTimeField(auto_now=True) - def save(self, *args, **kwargs): - # give a new user keys - if not self.private_key: - random_generator = Random.new().read - key = RSA.generate(1024, random_generator) - self.private_key = key.export_key().decode('utf8') - self.public_key = key.publickey().export_key().decode('utf8') - if self.local and not re.match(r'\w+@\w+.\w+', self.username): - # set your local username that doesn't have the domain - self.username = '%s@%s' % (self.username, DOMAIN) - - if self.local and not self.localname: - self.localname = self.username.replace('@%s' % DOMAIN, '') - - if self.local and not self.actor: - self.actor = 'https://%s/api/u/%s' % (DOMAIN, self.localname) - - super().save(*args, **kwargs) +@receiver(models.signals.pre_save, sender=User) +def execute_before_save(sender, instance, *args, **kwargs): + ''' create shelves for new users ''' + # TODO: how are remote users handled? what if they aren't readers? + if not instance.local or instance.id: + return + instance.localname = instance.username + instance.username = '%s@%s' % (instance.username, DOMAIN) + instance.actor = 'https://%s/user/%s' % (DOMAIN, instance.localname) + instance.inbox = 'https://%s/user/%s/inbox' % (DOMAIN, instance.localname) + instance.outbox = 'https://%s/user/%s/outbox' % (DOMAIN, instance.localname) + if not instance.private_key: + random_generator = Random.new().read + key = RSA.generate(1024, random_generator) + instance.private_key = key.export_key().decode('utf8') + instance.public_key = key.publickey().export_key().decode('utf8') @receiver(models.signals.post_save, sender=User) diff --git a/fedireads/urls.py b/fedireads/urls.py index 90636a916..9b8fb3b45 100644 --- a/fedireads/urls.py +++ b/fedireads/urls.py @@ -20,6 +20,14 @@ from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), + + # federation endpoints + path('user/.json', federation.get_actor), + path('user//inbox', federation.inbox), + path('user//outbox', federation.outbox), + path('.well-known/webfinger', federation.webfinger), + + # ui views path('', views.home), path('login/', views.user_login), path('logout/', views.user_logout), @@ -27,6 +35,7 @@ urlpatterns = [ path('user//edit/', views.user_profile_edit), path('book/', views.book_page), + # internal action endpoints path('review/', views.review), path('shelve//', views.shelve), path('follow/', views.follow), @@ -34,8 +43,4 @@ urlpatterns = [ path('search/', views.search), path('upload-avatar/', views.upload_avatar), - path('api/u/', federation.get_actor), - path('api/u//inbox', federation.inbox), - path('api/u//outbox', federation.outbox), - path('.well-known/webfinger', federation.webfinger), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)