forget-cancellare-vecchi-toot/routes/__init__.py

303 lines
8.8 KiB
Python
Raw Normal View History

from flask import render_template, url_for, redirect, request, g,\
make_response
2017-08-31 18:59:09 +02:00
from datetime import datetime, timedelta, timezone
2017-09-20 23:02:36 +02:00
import libforget.twitter
import libforget.mastodon
from libforget.auth import require_auth, csrf,\
get_viewer
from model import Session, TwitterArchive, MastodonApp, MastodonInstance
from app import app, db, sentry, limiter, imgproxy
2017-07-30 13:53:14 +02:00
import tasks
from zipfile import BadZipFile
2017-08-09 11:32:32 +02:00
from twitter import TwitterError
2017-08-09 14:27:39 +02:00
from urllib.error import URLError
2017-09-20 23:02:36 +02:00
import libforget.version
import libforget.settings
import libforget.json
2017-09-08 00:38:54 +02:00
import re
2017-07-25 09:52:24 +02:00
2017-08-29 14:46:32 +02:00
2017-07-25 09:52:24 +02:00
@app.route('/')
def index():
viewer = get_viewer()
if viewer:
2017-08-29 14:46:32 +02:00
return render_template(
'logged_in.html',
2017-09-20 23:02:36 +02:00
scales=libforget.interval.SCALES,
2017-08-29 14:46:32 +02:00
tweet_archive_failed='tweet_archive_failed' in request.args,
2017-08-31 18:59:09 +02:00
settings_error='settings_error' in request.args,
2017-09-20 23:02:36 +02:00
viewer_json=libforget.json.account(viewer),
2017-08-31 18:59:09 +02:00
)
else:
return redirect(url_for('about'))
2017-08-29 14:46:32 +02:00
@app.route('/about/')
def about():
instances = (
MastodonInstance.query
2017-09-17 19:11:45 +02:00
.filter(MastodonInstance.popularity > 5)
.order_by(db.desc(MastodonInstance.popularity),
MastodonInstance.instance)
.limit(5).all())
return render_template(
'about.html',
mastodon_instances=instances,
twitter_login_error='twitter_login_error' in request.args)
2017-07-25 09:52:24 +02:00
2017-09-20 23:28:12 +02:00
@app.route('/about/privacy')
def privacy():
return render_template('privacy.html')
@app.route('/login/twitter')
@limiter.limit('10/minute')
2017-07-27 00:35:53 +02:00
def twitter_login_step1():
try:
2017-09-20 23:02:36 +02:00
return redirect(libforget.twitter.get_login_url(
2017-08-29 14:46:32 +02:00
callback=url_for('twitter_login_step2', _external=True),
**app.config.get_namespace("TWITTER_")
))
except (TwitterError, URLError):
if sentry:
sentry.captureException()
2017-08-29 14:46:32 +02:00
return redirect(
url_for('about', twitter_login_error='', _anchor='log_in'))
2017-08-29 14:46:32 +02:00
2017-07-27 00:35:53 +02:00
def login(account_id):
session = Session(account_id=account_id)
db.session.add(session)
db.session.commit()
session.account.dormant = False
db.session.commit()
tasks.fetch_acc.s(account_id).apply_async(routing_key='high')
return session
2017-07-27 00:35:53 +02:00
@app.route('/login/twitter/callback')
@limiter.limit('10/minute')
2017-07-27 00:35:53 +02:00
def twitter_login_step2():
try:
oauth_token = request.args['oauth_token']
oauth_verifier = request.args['oauth_verifier']
2017-09-20 23:02:36 +02:00
token = libforget.twitter.receive_verifier(
2017-08-29 14:46:32 +02:00
oauth_token, oauth_verifier,
**app.config.get_namespace("TWITTER_"))
2017-07-30 13:53:14 +02:00
session = login(token.account_id)
2017-07-30 13:53:14 +02:00
g.viewer = session
return redirect(url_for('index'))
except (TwitterError, URLError):
if sentry:
sentry.captureException()
2017-08-29 14:46:32 +02:00
return redirect(
url_for('about', twitter_login_error='', _anchor='log_in'))
2017-08-29 14:46:32 +02:00
2017-08-29 13:26:32 +02:00
class TweetArchiveEmptyException(Exception):
pass
2017-08-29 14:46:32 +02:00
@app.route('/upload_tweet_archive', methods=('POST',))
2017-08-10 17:07:39 +02:00
@limiter.limit('10/10 minutes')
@require_auth
def upload_tweet_archive():
2017-08-29 14:46:32 +02:00
ta = TwitterArchive(
account=g.viewer.account,
body=request.files['file'].read())
2017-07-31 00:07:34 +02:00
db.session.add(ta)
db.session.commit()
try:
2017-09-20 23:02:36 +02:00
files = libforget.twitter.chunk_twitter_archive(ta.id)
2017-08-12 20:32:51 +02:00
ta.chunks = len(files)
db.session.commit()
2017-07-31 00:07:34 +02:00
2017-08-29 13:26:32 +02:00
if not ta.chunks > 0:
raise TweetArchiveEmptyException()
2017-08-12 20:32:51 +02:00
for filename in files:
2017-08-18 23:03:49 +02:00
tasks.import_twitter_archive_month.s(ta.id, filename).apply_async()
2017-08-12 20:32:51 +02:00
return redirect(url_for('index', _anchor='recent_archives'))
2017-08-29 13:26:32 +02:00
except (BadZipFile, TweetArchiveEmptyException):
if sentry:
sentry.captureException()
2017-08-29 14:46:32 +02:00
return redirect(
url_for('index', tweet_archive_failed='',
_anchor='tweet_archive_import'))
2017-08-03 16:05:28 +02:00
@app.route('/settings', methods=('POST',))
2017-08-25 10:50:11 +02:00
@csrf
@require_auth
def settings():
viewer = get_viewer()
try:
2017-09-20 23:02:36 +02:00
for attr in libforget.settings.attrs:
2017-08-07 16:26:25 +02:00
if attr in request.form:
setattr(viewer, attr, request.form[attr])
db.session.commit()
except ValueError:
if sentry:
sentry.captureException()
return 400
2017-07-31 18:29:09 +02:00
2017-08-03 16:05:28 +02:00
return redirect(url_for('index', settings_saved=''))
2017-08-29 14:46:32 +02:00
2017-08-03 16:05:28 +02:00
@app.route('/disable', methods=('POST',))
2017-08-25 10:50:11 +02:00
@csrf
2017-08-03 16:05:28 +02:00
@require_auth
def disable():
g.viewer.account.policy_enabled = False
db.session.commit()
2017-07-31 18:29:09 +02:00
2017-08-03 16:05:28 +02:00
return redirect(url_for('index'))
2017-08-29 14:46:32 +02:00
2017-08-03 16:05:28 +02:00
@app.route('/enable', methods=('POST',))
2017-08-25 10:50:11 +02:00
@csrf
2017-08-03 16:05:28 +02:00
@require_auth
def enable():
2017-08-29 14:46:32 +02:00
if 'confirm' not in request.form and not g.viewer.account.policy_enabled:
2017-08-03 21:37:00 +02:00
if g.viewer.account.policy_delete_every == timedelta(0):
approx = g.viewer.account.estimate_eligible_for_delete()
2017-08-29 14:46:32 +02:00
return render_template(
'warn.html',
message=f"""
You've set the time between deleting posts to 0. Every post
that matches your expiration rules will be deleted within
minutes.
{ ("That's about " + str(approx) + " posts.") if approx > 0
else "" }
Go ahead?
""")
2017-09-03 01:23:54 +02:00
if (not g.viewer.account.last_delete or
g.viewer.account.last_delete <
2017-08-31 18:59:09 +02:00
datetime.now(timezone.utc) - timedelta(days=365)):
2017-08-29 14:46:32 +02:00
return render_template(
'warn.html',
message="""
Once you enable Forget, posts that match your
expiration rules will be deleted <b>permanently</b>.
We can't bring them back. Make sure that you won't
miss them.
""")
2017-08-03 21:37:00 +02:00
2017-08-03 16:05:28 +02:00
g.viewer.account.policy_enabled = True
db.session.commit()
return redirect(url_for('index'))
2017-07-31 18:29:09 +02:00
@app.route('/logout')
@require_auth
def logout():
if(g.viewer):
db.session.delete(g.viewer)
db.session.commit()
g.viewer = None
return redirect(url_for('about'))
2017-08-29 14:46:32 +02:00
@app.route('/login/mastodon', methods=('GET', 'POST'))
2017-08-23 15:14:24 +02:00
def mastodon_login_step1(instance=None):
2017-08-29 14:46:32 +02:00
instance_url = (request.args.get('instance_url', None)
or request.form.get('instance_url', None))
2017-08-23 15:14:24 +02:00
if not instance_url:
instances = (
MastodonInstance
.query.filter(MastodonInstance.popularity > 1)
.order_by(db.desc(MastodonInstance.popularity),
MastodonInstance.instance)
.limit(30))
2017-08-29 14:46:32 +02:00
return render_template(
'mastodon_login.html', instances=instances,
address_error=request.method == 'POST',
generic_error='error' in request.args
2017-08-23 15:14:24 +02:00
)
instance_url = instance_url.lower()
# strip protocol
instance_url = re.sub('^https?://', '', instance_url,
2017-09-08 00:38:54 +02:00
count=1, flags=re.IGNORECASE)
# strip username
instance_url = instance_url.split("@")[-1]
# strip trailing path
instance_url = instance_url.split('/')[0]
2017-08-29 14:46:32 +02:00
callback = url_for('mastodon_login_step2',
2017-08-29 16:45:00 +02:00
instance_url=instance_url, _external=True)
try:
2017-09-20 23:02:36 +02:00
app = libforget.mastodon.get_or_create_app(
instance_url,
callback,
url_for('index', _external=True))
db.session.merge(app)
db.session.commit()
2017-09-20 23:02:36 +02:00
return redirect(libforget.mastodon.login_url(app, callback))
except Exception:
if sentry:
sentry.captureException()
return redirect(url_for('mastodon_login_step1', error=True))
2017-08-29 14:46:32 +02:00
2017-08-29 16:45:00 +02:00
@app.route('/login/mastodon/callback/<instance_url>')
2017-08-29 14:46:32 +02:00
def mastodon_login_step2(instance_url):
code = request.args.get('code', None)
2017-08-29 14:46:32 +02:00
app = MastodonApp.query.get(instance_url)
if not code or not app:
2017-08-29 16:31:43 +02:00
return redirect(url_for('mastodon_login_step1', error=True))
2017-08-29 14:46:32 +02:00
callback = url_for('mastodon_login_step2',
2017-08-29 16:45:00 +02:00
instance_url=instance_url, _external=True)
2017-09-20 23:02:36 +02:00
token = libforget.mastodon.receive_code(code, app, callback)
account = token.account
session = login(account.id)
db.session.commit()
g.viewer = session
return redirect(url_for('index'))
2017-08-31 20:46:38 +02:00
2017-08-31 20:46:38 +02:00
@app.route('/sentry/setup.js')
def sentry_setup():
client_dsn = app.config.get('SENTRY_DSN').split('@')
client_dsn[:1] = client_dsn[0].split(':')
client_dsn = ':'.join(client_dsn[0:2]) + '@' + client_dsn[3]
resp = make_response(render_template(
'sentry.js', sentry_dsn=client_dsn))
2017-08-31 20:46:38 +02:00
resp.headers.set('content-type', 'text/javascript')
return resp
@app.route('/dismiss', methods={'POST'})
@csrf
@require_auth
def dismiss():
get_viewer().reason = None
db.session.commit()
return redirect(url_for('index'))
@app.route('/avatar/<identifier>')
def avatar(identifier):
return imgproxy.respond(identifier)