mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-26 03:21:05 +00:00
search endpoint (user part)
This commit is contained in:
parent
d2d278b475
commit
f7242452fa
6 changed files with 44 additions and 17 deletions
|
@ -19,11 +19,11 @@ def webfinger(request):
|
||||||
if not resource and not resource.startswith('acct:'):
|
if not resource and not resource.startswith('acct:'):
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
ap_id = resource.replace('acct:', '')
|
ap_id = resource.replace('acct:', '')
|
||||||
user = models.User.objects.filter(full_username=ap_id).first()
|
user = models.User.objects.filter(username=ap_id).first()
|
||||||
if not user:
|
if not user:
|
||||||
return HttpResponseNotFound('No account found')
|
return HttpResponseNotFound('No account found')
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'subject': 'acct:%s' % (user.full_username),
|
'subject': 'acct:%s' % (user.username),
|
||||||
'links': [
|
'links': [
|
||||||
{
|
{
|
||||||
'rel': 'self',
|
'rel': 'self',
|
||||||
|
@ -88,6 +88,22 @@ def inbox(request, username):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
|
|
||||||
|
|
||||||
|
def handle_account_search(query):
|
||||||
|
''' webfingerin' other servers '''
|
||||||
|
domain = query.split('@')[1]
|
||||||
|
try:
|
||||||
|
user = models.User.objects.get(username=query)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
url = 'https://%s/.well-known/webfinger' % domain
|
||||||
|
params = {'resource': 'acct:%s' % query}
|
||||||
|
response = requests.get(url, params=params)
|
||||||
|
data = response.json()
|
||||||
|
for link in data['links']:
|
||||||
|
if link['rel'] == 'self':
|
||||||
|
user = get_or_create_remote_user(link['href'])
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
def handle_add(activity):
|
def handle_add(activity):
|
||||||
''' receiving an Add activity (to shelve a book) '''
|
''' receiving an Add activity (to shelve a book) '''
|
||||||
# TODO what happens here? If it's a remote over, then I think
|
# TODO what happens here? If it's a remote over, then I think
|
||||||
|
@ -156,7 +172,7 @@ def handle_outgoing_follow(user, to_follow):
|
||||||
'summary': '',
|
'summary': '',
|
||||||
'type': 'Follow',
|
'type': 'Follow',
|
||||||
'actor': user.actor,
|
'actor': user.actor,
|
||||||
'object': to_follow,
|
'object': to_follow.actor,
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(user, activity, [format_inbox(to_follow)])
|
broadcast(user, activity, [format_inbox(to_follow)])
|
||||||
|
@ -281,6 +297,7 @@ def broadcast(sender, action, recipients):
|
||||||
for recipient in recipients:
|
for recipient in recipients:
|
||||||
sign_and_send(sender, action, recipient)
|
sign_and_send(sender, action, recipient)
|
||||||
|
|
||||||
|
|
||||||
def sign_and_send(sender, action, destination):
|
def sign_and_send(sender, action, destination):
|
||||||
''' crpyto whatever and http junk '''
|
''' crpyto whatever and http junk '''
|
||||||
inbox_fragment = '/api/u/%s/inbox' % (sender.username)
|
inbox_fragment = '/api/u/%s/inbox' % (sender.username)
|
||||||
|
@ -291,7 +308,7 @@ date: %s''' % (inbox_fragment, DOMAIN, now)
|
||||||
signer = pkcs1_15.new(RSA.import_key(sender.private_key))
|
signer = pkcs1_15.new(RSA.import_key(sender.private_key))
|
||||||
signed_message = signer.sign(SHA256.new(message_to_sign.encode('utf8')))
|
signed_message = signer.sign(SHA256.new(message_to_sign.encode('utf8')))
|
||||||
|
|
||||||
signature = 'keyId="%s",' % sender.full_username
|
signature = 'keyId="%s",' % sender.username
|
||||||
signature += 'headers="(request-target) host date",'
|
signature += 'headers="(request-target) host date",'
|
||||||
signature += 'signature="%s"' % b64encode(signed_message)
|
signature += 'signature="%s"' % b64encode(signed_message)
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
|
@ -306,9 +323,9 @@ date: %s''' % (inbox_fragment, DOMAIN, now)
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
def get_or_create_remote_user(activity):
|
|
||||||
|
def get_or_create_remote_user(actor):
|
||||||
''' wow, a foreigner '''
|
''' wow, a foreigner '''
|
||||||
actor = activity['actor']
|
|
||||||
try:
|
try:
|
||||||
user = models.User.objects.get(actor=actor)
|
user = models.User.objects.get(actor=actor)
|
||||||
except models.User.DoesNotExist:
|
except models.User.DoesNotExist:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.0.2 on 2020-01-28 02:46
|
# Generated by Django 3.0.2 on 2020-01-28 03:40
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
|
@ -32,7 +32,6 @@ class Migration(migrations.Migration):
|
||||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
('full_username', models.CharField(blank=True, max_length=255, null=True, unique=True)),
|
|
||||||
('private_key', models.TextField(blank=True, null=True)),
|
('private_key', models.TextField(blank=True, null=True)),
|
||||||
('public_key', models.TextField(blank=True, null=True)),
|
('public_key', models.TextField(blank=True, null=True)),
|
||||||
('api_key', models.CharField(blank=True, max_length=255, null=True)),
|
('api_key', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
|
|
@ -10,12 +10,6 @@ import re
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
''' a user who wants to read books '''
|
''' a user who wants to read books '''
|
||||||
full_username = models.CharField(
|
|
||||||
max_length=255,
|
|
||||||
blank=True,
|
|
||||||
null=True,
|
|
||||||
unique=True
|
|
||||||
)
|
|
||||||
private_key = models.TextField(blank=True, null=True)
|
private_key = models.TextField(blank=True, null=True)
|
||||||
public_key = models.TextField(blank=True, null=True)
|
public_key = models.TextField(blank=True, null=True)
|
||||||
api_key = models.CharField(max_length=255, blank=True, null=True)
|
api_key = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
@ -36,8 +30,8 @@ class User(AbstractUser):
|
||||||
|
|
||||||
if self.local and not self.actor:
|
if self.local and not self.actor:
|
||||||
self.actor = 'https://%s/api/u/%s' % (DOMAIN, self.username)
|
self.actor = 'https://%s/api/u/%s' % (DOMAIN, self.username)
|
||||||
if self.local and not self.full_username:
|
if self.local and not re.match(r'\w+@\w+.\w+', self.username):
|
||||||
self.full_username = '%s@%s' % (self.username, DOMAIN)
|
self.username = '%s@%s' % (self.username, DOMAIN)
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div id="search">
|
<div id="search">
|
||||||
<form action="search">
|
<form action="/search/">
|
||||||
<input type="text" name="q"></input>
|
<input type="text" name="q"></input>
|
||||||
<input type="submit" value="🔍"></input>
|
<input type="submit" value="🔍"></input>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -24,10 +24,13 @@ urlpatterns = [
|
||||||
path('logout/', views.user_logout),
|
path('logout/', views.user_logout),
|
||||||
path('user/<str:username>', views.user_profile),
|
path('user/<str:username>', views.user_profile),
|
||||||
path('book/<str:book_identifier>', views.book_page),
|
path('book/<str:book_identifier>', views.book_page),
|
||||||
|
|
||||||
path('review/', views.review),
|
path('review/', views.review),
|
||||||
path('shelve/<str:shelf_id>/<int:book_id>', views.shelve),
|
path('shelve/<str:shelf_id>/<int:book_id>', views.shelve),
|
||||||
path('follow/', views.follow),
|
path('follow/', views.follow),
|
||||||
path('unfollow/', views.unfollow),
|
path('unfollow/', views.unfollow),
|
||||||
|
path('search/', views.search),
|
||||||
|
|
||||||
path('api/u/<str:username>', federation.get_actor),
|
path('api/u/<str:username>', federation.get_actor),
|
||||||
path('api/u/<str:username>/inbox', federation.inbox),
|
path('api/u/<str:username>/inbox', federation.inbox),
|
||||||
path('api/u/<str:username>/outbox', federation.outbox),
|
path('api/u/<str:username>/outbox', federation.outbox),
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.template.response import TemplateResponse
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from fedireads import models, openlibrary
|
from fedireads import models, openlibrary
|
||||||
from fedireads import federation as api
|
from fedireads import federation as api
|
||||||
|
import re
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def home(request):
|
def home(request):
|
||||||
|
@ -133,3 +134,16 @@ def unfollow(request):
|
||||||
followed.followers.remove(request.user)
|
followed.followers.remove(request.user)
|
||||||
return redirect('/user/%s' % followed.username)
|
return redirect('/user/%s' % followed.username)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@login_required
|
||||||
|
def search(request):
|
||||||
|
''' that search bar up top '''
|
||||||
|
query = request.GET.get('q')
|
||||||
|
if re.match(r'\w+@\w+.\w+', query):
|
||||||
|
results = [api.handle_account_search(query)]
|
||||||
|
else:
|
||||||
|
results = []
|
||||||
|
|
||||||
|
return TemplateResponse(request, 'results.html', {'results': results})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue