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 from flask_app import app
# import all modules to register their Flask handlers # 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 import models
models.reset_protocol_properties() models.reset_protocol_properties()

Wyświetl plik

@ -55,11 +55,6 @@ render
.. automodule:: render .. automodule:: render
:exclude-members: __eq__, __getnewargs__, __getstate__, __hash__, __new__, __repr__, __str__, __weakref__ :exclude-members: __eq__, __getnewargs__, __getstate__, __hash__, __new__, __repr__, __str__, __weakref__
superfeedr
----------
.. automodule:: superfeedr
:exclude-members: __eq__, __getnewargs__, __getstate__, __hash__, __new__, __repr__, __str__, __weakref__
web web
--- ---
.. automodule:: 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 flask import g, get_flashed_messages
from google.cloud import ndb 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 util
from oauth_dropins.webutil import appengine_info from oauth_dropins.webutil import appengine_info
from oauth_dropins.webutil.testutil import NOW, requests_response 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/']", "WARNING:models:actor https://user.com/ isn't https://user.com/like's author or actor ['https://eve.com/']",
logs.output) 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): def _test_verify(self, redirects, hcard, actor, redirects_error=None):
self.user.has_redirects = False self.user.has_redirects = False
self.user.put() 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 flask import g, redirect, render_template, request
from google.cloud import ndb from google.cloud import ndb
from google.cloud.ndb import ComputedProperty from google.cloud.ndb import ComputedProperty
from granary import as1, as2, microformats2 from granary import as1, as2, atom, microformats2
import mf2util import mf2util
from oauth_dropins.webutil import flask_util, util from oauth_dropins.webutil import flask_util, util
from oauth_dropins.webutil.appengine_config import tasks_client from oauth_dropins.webutil.appengine_config import tasks_client
@ -581,6 +581,38 @@ def webmention_interactive():
return redirect('/', code=302) 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') @app.post('/queue/webmention')
@cloud_tasks_only @cloud_tasks_only
def webmention_task(): def webmention_task():
@ -593,9 +625,9 @@ def webmention_task():
logger.info(f'webmention from {domain}') logger.info(f'webmention from {domain}')
user = Web.get_by_id(domain) user = Web.get_by_id(domain)
logger.info(f'User: {user.key}')
if not user: if not user:
error(f'No user found for domain {domain}', status=304) error(f'No user found for domain {domain}', status=304)
logger.info(f'User: {user.key}')
# fetch source page # fetch source page
try: try: