start to add superfeedr notify handler

for #550
pull/741/head
Ryan Barrett 2023-12-02 21:18:22 -08:00
rodzic 5485cbeece
commit c54df36164
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
5 zmienionych plików z 68 dodań i 35 usunięć

2
app.py
Wyświetl plik

@ -6,7 +6,7 @@ registered.
from flask_app import app
# import all modules to register their Flask handlers
import activitypub, atproto, convert, follow, pages, redirect, superfeedr, ui, webfinger, web
import activitypub, atproto, convert, follow, pages, redirect, ui, webfinger, web
import models
models.reset_protocol_properties()

Wyświetl plik

@ -55,11 +55,6 @@ render
.. automodule:: render
:exclude-members: __eq__, __getnewargs__, __getstate__, __hash__, __new__, __repr__, __str__, __weakref__
superfeedr
----------
.. automodule:: superfeedr
:exclude-members: __eq__, __getnewargs__, __getstate__, __hash__, __new__, __repr__, __str__, __weakref__
web
---
.. automodule:: web

Wyświetl plik

@ -1,26 +0,0 @@
"""Superfeedr callback handlers.
Not really sure what this will be yet.
* https://github.com/snarfed/bridgy-fed/issues/550
* https://github.com/snarfed/bridgy-fed/issues/18#issuecomment-430731476
* https://documentation.superfeedr.com/publishers.html
"""
import logging
from flask import request
from flask_app import app
logger = logging.getLogger(__name__)
@app.route(r'/superfeedr/', methods=['GET', 'POST'])
@app.route(r'/superfeedr/<path:_>', methods=['GET', 'POST'])
def superfeedr(_=None):
"""Superfeedr subscription callback handler.
https://documentation.superfeedr.com/publishers.html#subscription-callback
"""
logger.info(f'Got:\n{request.get_data(as_text=True)}')
return '', 204

Wyświetl plik

@ -4,7 +4,7 @@ from unittest.mock import patch
from flask import g, get_flashed_messages
from google.cloud import ndb
from granary import as1, as2, microformats2
from granary import as1, as2, atom, microformats2
from oauth_dropins.webutil import util
from oauth_dropins.webutil import appengine_info
from oauth_dropins.webutil.testutil import NOW, requests_response
@ -1735,6 +1735,38 @@ class WebTest(TestCase):
"WARNING:models:actor https://user.com/ isn't https://user.com/like's author or actor ['https://eve.com/']",
logs.output)
@patch('oauth_dropins.webutil.appengine_config.tasks_client.create_task')
def test_superfeedr_make_task(self, mock_create_task, *_):
common.RUN_TASKS_INLINE = False
got = self.post('/superfeedr/notify/user.com', data="""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<uri>https://user.com/post</uri>
<content>I hereby post.</content>
</entry>
""", headers={'Content-Type': atom.CONTENT_TYPE})
self.assertEqual(200, got.status_code)
self.assert_task(mock_create_task, 'receive', '/queue/receive',
obj=Object(id='https://user.com/post').key.urlsafe(),
authed_as='user.com')
def test_superfeedr_no_user(self, *_):
orig_count = Object.query().count()
got = self.post('/webmention', data={'source': 'https://nope.com/post'})
self.assertEqual(400, got.status_code)
self.assertEqual(orig_count, Object.query().count())
def test_superfeedr_no_id(self, *mocks):
got = self.post('/superfeedr/notify/user.com', data="""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<content>I hereby post.</content>
</entry>
""", headers={'Content-Type': atom.CONTENT_TYPE})
self.assertEqual(400, got.status_code)
def _test_verify(self, redirects, hcard, actor, redirects_error=None):
self.user.has_redirects = False
self.user.put()

36
web.py
Wyświetl plik

@ -9,7 +9,7 @@ from urllib.parse import quote, urlencode, urljoin, urlparse
from flask import g, redirect, render_template, request
from google.cloud import ndb
from google.cloud.ndb import ComputedProperty
from granary import as1, as2, microformats2
from granary import as1, as2, atom, microformats2
import mf2util
from oauth_dropins.webutil import flask_util, util
from oauth_dropins.webutil.appengine_config import tasks_client
@ -581,6 +581,38 @@ def webmention_interactive():
return redirect('/', code=302)
# generate/check per-user token for auth?
# or https://documentation.superfeedr.com/subscribers.html#http-authentication ?
@app.post(f'/superfeedr/notify/<regex("{DOMAIN_RE}"):domain>')
def superfeedr_notify(domain):
"""Superfeedr notification handler.
https://documentation.superfeedr.com/publishers.html#subscription-callback
"""
logger.info(f'Got:\n{request.get_data(as_text=True)}')
type = request.headers.get('Content-Type', '').split(';')[0]
if type != atom.CONTENT_TYPE.split(';')[0]:
error(f'Expected Content-Type {atom.CONTENT_TYPE}, got {type}', status=406)
user = Web.get_by_id(domain)
if not user:
error(f'No user found for domain {domain}', status=304)
text = request.get_data(as_text=True)
obj = Object(atom=text)
logger.info(f'Converted to AS1: {json_dumps(obj.as1, indent=2)}')
id = obj.as1.get('id')
if not id:
return error('No id or URL!')
obj = Object.get_or_create(id=id, atom=text, source_protocol=Web.ABBREV)
common.create_task(queue='receive', obj=obj.key.urlsafe(), authed_as=domain)
return 'OK'
@app.post('/queue/webmention')
@cloud_tasks_only
def webmention_task():
@ -593,9 +625,9 @@ def webmention_task():
logger.info(f'webmention from {domain}')
user = Web.get_by_id(domain)
logger.info(f'User: {user.key}')
if not user:
error(f'No user found for domain {domain}', status=304)
logger.info(f'User: {user.key}')
# fetch source page
try: