|
|
|
from datetime import datetime, timedelta
|
|
|
|
from secrets import token_urlsafe
|
|
|
|
|
|
|
|
from flask import render_template, flash, redirect, url_for, abort, request, Markup
|
|
|
|
from app import app, db
|
|
|
|
from app.forms import UrlForm
|
|
|
|
from app.models import Url
|
|
|
|
from app.database_cleaner import check_url
|
|
|
|
|
|
|
|
domain = 'http://127.0.0.1:5000'
|
|
|
|
|
|
|
|
@app.before_request
|
|
|
|
def before_request():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@app.route('/index', methods=['GET', 'POST'])
|
|
|
|
def index():
|
|
|
|
form = UrlForm()
|
|
|
|
if form.validate_on_submit():
|
|
|
|
hash = getHash()
|
|
|
|
if hash is None:
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
death = calcDeath(form.retention.data, int(form.retention_type.data))
|
|
|
|
view_counter = None if form.retention_type.data != 4 else form.retention.data
|
|
|
|
if death is None and view_counter is None:
|
|
|
|
app.logger.warning("Neither death nor view_counter was recieved for url")
|
|
|
|
flash('Please specify a retention time')
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
url = Url(url = form.url.data.strip(), hash=hash, death=death, view_counter=view_counter)
|
|
|
|
db.session.add(url)
|
|
|
|
db.session.commit()
|
|
|
|
death = None if url.death is None else url.death.strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
app.logger.info(f"{getClientIp()} created hash '{url.hash}' for '{url.url}'. Death: {death}, View Counter: {url.view_counter}")
|
|
|
|
link = url_for("resolve_hash", hash=hash, _external = True)
|
|
|
|
flash(Markup(f'Your url is shortend to <a href="{link}">{link}</a>'))
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
return render_template("index.html", form=form)
|
|
|
|
|
|
|
|
def getHash():
|
|
|
|
for i in range(200):
|
|
|
|
hash = token_urlsafe(3)
|
|
|
|
url = Url.query.filter_by(hash=hash).first()
|
|
|
|
if (url is None and url != 'index'):
|
|
|
|
return hash
|
|
|
|
|
|
|
|
flash('Failed generating a unique hash. Please try again.')
|
|
|
|
return None
|
|
|
|
|
|
|
|
def getClientIp():
|
|
|
|
if app.config['REVERSE_PROXY'] == 0:
|
|
|
|
return request.remote_addr
|
|
|
|
if app.config['REVERSE_PROXY'] == 1:
|
|
|
|
return request.environ['HTTP_X_FORWARDED_FOR']
|
|
|
|
|
|
|
|
def calcDeath(retention, retention_type):
|
|
|
|
if retention_type == 1:
|
|
|
|
return datetime.now() + timedelta(minutes=retention)
|
|
|
|
if retention_type == 2:
|
|
|
|
return datetime.now() + timedelta(hours=retention)
|
|
|
|
if retention_type == 3:
|
|
|
|
return datetime.now() + timedelta(days=retention)
|
|
|
|
if retention_type == 4:
|
|
|
|
return None
|
|
|
|
app.logger.error(f"Retention_type out of range: '{retention_type}'")
|
|
|
|
|
|
|
|
@app.route('/', defaults={'hash' : 'index'})
|
|
|
|
@app.route('/<hash>')
|
|
|
|
def resolve_hash(hash):
|
|
|
|
if hash == 'index':
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
url = Url.query.filter_by(hash=hash).first_or_404()
|
|
|
|
if check_url(url) is not None: abort(404)
|
|
|
|
if url.view_counter is not None:
|
|
|
|
url.view_counter -= 1
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
countermessage = "" if url.view_counter is None else f". View counter was lowered to {url.view_counter}"
|
|
|
|
app.logger.info(f"{getClientIp()} requested hash '{url.hash}' which resolved to '{url.url}'{countermessage}")
|
|
|
|
return redirect(url.url, 301)
|