kopia lustrzana https://github.com/dgtlmoon/changedetection.io
RSS allow access via token (#310)
Allow access via a token * New RSS URL * Redirect the old RSS feed URL * fix testspull/312/head
rodzic
e71dbbe771
commit
9815fc2526
|
@ -224,12 +224,72 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
# Disable password loginif there is not one set
|
# Disable password loginif there is not one set
|
||||||
app.config['LOGIN_DISABLED'] = datastore.data['settings']['application']['password'] == False
|
app.config['LOGIN_DISABLED'] = datastore.data['settings']['application']['password'] == False
|
||||||
|
|
||||||
|
# For the RSS path, allow access via a token
|
||||||
|
if request.path == '/rss' and request.args.get('token'):
|
||||||
|
app_rss_token = datastore.data['settings']['application']['rss_access_token']
|
||||||
|
rss_url_token = request.args.get('token')
|
||||||
|
if app_rss_token == rss_url_token:
|
||||||
|
app.config['LOGIN_DISABLED'] = True
|
||||||
|
|
||||||
|
@app.route("/rss", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def rss():
|
||||||
|
|
||||||
|
limit_tag = request.args.get('tag')
|
||||||
|
|
||||||
|
# Sort by last_changed and add the uuid which is usually the key..
|
||||||
|
sorted_watches = []
|
||||||
|
|
||||||
|
# @todo needs a .itemsWithTag() or something
|
||||||
|
for uuid, watch in datastore.data['watching'].items():
|
||||||
|
|
||||||
|
if limit_tag != None:
|
||||||
|
# Support for comma separated list of tags.
|
||||||
|
for tag_in_watch in watch['tag'].split(','):
|
||||||
|
tag_in_watch = tag_in_watch.strip()
|
||||||
|
if tag_in_watch == limit_tag:
|
||||||
|
watch['uuid'] = uuid
|
||||||
|
sorted_watches.append(watch)
|
||||||
|
|
||||||
|
else:
|
||||||
|
watch['uuid'] = uuid
|
||||||
|
sorted_watches.append(watch)
|
||||||
|
|
||||||
|
sorted_watches.sort(key=lambda x: x['last_changed'], reverse=True)
|
||||||
|
|
||||||
|
fg = FeedGenerator()
|
||||||
|
fg.title('changedetection.io')
|
||||||
|
fg.description('Feed description')
|
||||||
|
fg.link(href='https://changedetection.io')
|
||||||
|
|
||||||
|
for watch in sorted_watches:
|
||||||
|
if not watch['viewed']:
|
||||||
|
# Re #239 - GUID needs to be individual for each event
|
||||||
|
# @todo In the future make this a configurable link back (see work on BASE_URL https://github.com/dgtlmoon/changedetection.io/pull/228)
|
||||||
|
guid = "{}/{}".format(watch['uuid'], watch['last_changed'])
|
||||||
|
fe = fg.add_entry()
|
||||||
|
fe.title(watch['url'])
|
||||||
|
fe.link(href=watch['url'])
|
||||||
|
fe.description(watch['url'])
|
||||||
|
fe.guid(guid, permalink=False)
|
||||||
|
dt = datetime.datetime.fromtimestamp(int(watch['newest_history_key']))
|
||||||
|
dt = dt.replace(tzinfo=pytz.UTC)
|
||||||
|
fe.pubDate(dt)
|
||||||
|
|
||||||
|
response = make_response(fg.rss_str())
|
||||||
|
response.headers.set('Content-Type', 'application/rss+xml')
|
||||||
|
return response
|
||||||
|
|
||||||
@app.route("/", methods=['GET'])
|
@app.route("/", methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def index():
|
def index():
|
||||||
limit_tag = request.args.get('tag')
|
limit_tag = request.args.get('tag')
|
||||||
pause_uuid = request.args.get('pause')
|
pause_uuid = request.args.get('pause')
|
||||||
|
|
||||||
|
# Redirect for the old rss path which used the /?rss=true
|
||||||
|
if request.args.get('rss'):
|
||||||
|
return redirect(url_for('rss', tag=limit_tag))
|
||||||
|
|
||||||
if pause_uuid:
|
if pause_uuid:
|
||||||
try:
|
try:
|
||||||
datastore.data['watching'][pause_uuid]['paused'] ^= True
|
datastore.data['watching'][pause_uuid]['paused'] ^= True
|
||||||
|
@ -239,7 +299,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Sort by last_changed and add the uuid which is usually the key..
|
# Sort by last_changed and add the uuid which is usually the key..
|
||||||
sorted_watches = []
|
sorted_watches = []
|
||||||
for uuid, watch in datastore.data['watching'].items():
|
for uuid, watch in datastore.data['watching'].items():
|
||||||
|
@ -259,42 +318,17 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
sorted_watches.sort(key=lambda x: x['last_changed'], reverse=True)
|
sorted_watches.sort(key=lambda x: x['last_changed'], reverse=True)
|
||||||
|
|
||||||
existing_tags = datastore.get_all_tags()
|
existing_tags = datastore.get_all_tags()
|
||||||
rss = request.args.get('rss')
|
|
||||||
|
|
||||||
if rss:
|
from changedetectionio import forms
|
||||||
fg = FeedGenerator()
|
form = forms.quickWatchForm(request.form)
|
||||||
fg.title('changedetection.io')
|
|
||||||
fg.description('Feed description')
|
|
||||||
fg.link(href='https://changedetection.io')
|
|
||||||
|
|
||||||
for watch in sorted_watches:
|
output = render_template("watch-overview.html",
|
||||||
if not watch['viewed']:
|
form=form,
|
||||||
# Re #239 - GUID needs to be individual for each event
|
watches=sorted_watches,
|
||||||
# @todo In the future make this a configurable link back (see work on BASE_URL https://github.com/dgtlmoon/changedetection.io/pull/228)
|
tags=existing_tags,
|
||||||
guid = "{}/{}".format(watch['uuid'], watch['last_changed'])
|
active_tag=limit_tag,
|
||||||
fe = fg.add_entry()
|
app_rss_token=datastore.data['settings']['application']['rss_access_token'],
|
||||||
fe.title(watch['url'])
|
has_unviewed=datastore.data['has_unviewed'])
|
||||||
fe.link(href=watch['url'])
|
|
||||||
fe.description(watch['url'])
|
|
||||||
fe.guid(guid, permalink=False)
|
|
||||||
dt = datetime.datetime.fromtimestamp(int(watch['newest_history_key']))
|
|
||||||
dt = dt.replace(tzinfo=pytz.UTC)
|
|
||||||
fe.pubDate(dt)
|
|
||||||
|
|
||||||
response = make_response(fg.rss_str())
|
|
||||||
response.headers.set('Content-Type', 'application/rss+xml')
|
|
||||||
return response
|
|
||||||
|
|
||||||
else:
|
|
||||||
from changedetectionio import forms
|
|
||||||
form = forms.quickWatchForm(request.form)
|
|
||||||
|
|
||||||
output = render_template("watch-overview.html",
|
|
||||||
form=form,
|
|
||||||
watches=sorted_watches,
|
|
||||||
tags=existing_tags,
|
|
||||||
active_tag=limit_tag,
|
|
||||||
has_unviewed=datastore.data['has_unviewed'])
|
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ class ChangeDetectionStore:
|
||||||
# Custom notification content
|
# Custom notification content
|
||||||
'notification_title': None,
|
'notification_title': None,
|
||||||
'notification_body': None,
|
'notification_body': None,
|
||||||
'notification_format': None,
|
'notification_format': None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,6 +147,12 @@ class ChangeDetectionStore:
|
||||||
else:
|
else:
|
||||||
self.__data['app_guid'] = str(uuid_builder.uuid4())
|
self.__data['app_guid'] = str(uuid_builder.uuid4())
|
||||||
|
|
||||||
|
# Generate the URL access token for RSS feeds
|
||||||
|
if not 'rss_access_token' in self.__data['settings']['application']:
|
||||||
|
import secrets
|
||||||
|
secret = secrets.token_hex(16)
|
||||||
|
self.__data['settings']['application']['rss_access_token'] = secret
|
||||||
|
|
||||||
self.needs_write = True
|
self.needs_write = True
|
||||||
|
|
||||||
# Finally start the thread that will manage periodic data saves to JSON
|
# Finally start the thread that will manage periodic data saves to JSON
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
all {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
all {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ url_for('index', tag=active_tag , rss=true)}}"><img id="feed-icon" src="{{url_for('static_content', group='images', filename='Generic_Feed-icon.svg')}}" height="15px"></a>
|
<a href="{{ url_for('rss', tag=active_tag , token=app_rss_token)}}"><img id="feed-icon" src="{{url_for('static_content', group='images', filename='Generic_Feed-icon.svg')}}" height="15px"></a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -59,7 +59,7 @@ def test_check_basic_change_detection_functionality(client, live_server):
|
||||||
assert b'unviewed' in res.data
|
assert b'unviewed' in res.data
|
||||||
|
|
||||||
# #75, and it should be in the RSS feed
|
# #75, and it should be in the RSS feed
|
||||||
res = client.get(url_for("index", rss="true"))
|
res = client.get(url_for("rss"))
|
||||||
expected_url = url_for('test_endpoint', _external=True)
|
expected_url = url_for('test_endpoint', _external=True)
|
||||||
assert b'<rss' in res.data
|
assert b'<rss' in res.data
|
||||||
assert expected_url.encode('utf-8') in res.data
|
assert expected_url.encode('utf-8') in res.data
|
||||||
|
|
Ładowanie…
Reference in New Issue