Skip to content
Snippets Groups Projects
main.py 6.43 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jonas Leder's avatar
    Jonas Leder committed
    from waitress import serve
    from flask import Flask, request, render_template, redirect, abort, Markup
    import sqlite3
    from urllib.parse import urlparse
    import os
    
    import qrcode
    import base64
    from PIL import Image
    from io import BytesIO
    
    Jonas Leder's avatar
    Jonas Leder committed
    
    app = Flask(__name__)
    
    domain_to_index = {}
    
    Jonas Leder's avatar
    Jonas Leder committed
    domain_prepared = ""
    
    
    Jonas Leder's avatar
    Jonas Leder committed
    try:
    
    Jonas Leder's avatar
    Jonas Leder committed
        domain = os.environ["domains"].split(";") #Get the domains from the enviorement variable. If no envioremenr variable is set set it to 127.0.0.1:5000 (for testing)
    
    Jonas Leder's avatar
    Jonas Leder committed
    except:
        domain = ["127.0.0.1:5000"]
    
    Jonas Leder's avatar
    Jonas Leder committed
    
    
    Jonas Leder's avatar
    Jonas Leder committed
    builddate = ""
    try:
    
    Jonas Leder's avatar
    Jonas Leder committed
        if(os.environ["show_build_date"] == "1"): #If you want to see the builddate you can enable this enviorement variable
    
    Jonas Leder's avatar
    Jonas Leder committed
            builddate = ", Build date: " + open("builddate.txt", "r").read()
    except:
    
    Jonas Leder's avatar
    Jonas Leder committed
        pass #This normaly only happens, if the script runs without a container for testing.
    
    try:
        if(os.environ["production"] == "1"): #If you disable production the flask testserver will be used, because it makes more debug output
            production = True
        else:
            production = False
    except:
        production = False
    
    Jonas Leder's avatar
    Jonas Leder committed
    
    
    Jonas Leder's avatar
    Jonas Leder committed
    for domains in domain: #Make from every domnain a entry for the select box later
    
    Jonas Leder's avatar
    Jonas Leder committed
        domains = domains
        domain_prepared = domain_prepared + '<option value="' + str(domains) + '">' + str(domains) + '</option>'
    
        domain_to_index[domains] = str(index)
        index = index + 1
    
    
    Jonas Leder's avatar
    Jonas Leder committed
    
    def table_check():
        create_table = """
            CREATE TABLE WEB_URL(
            ID INTEGER PRIMARY KEY AUTOINCREMENT,
            LONG_URL TEXT NOT NULL, SHORT_URL TEXT NOT NULL
            );
            """
        with sqlite3.connect('db/urls.db') as conn:
            cursor = conn.cursor()
    
    Jonas Leder's avatar
    Jonas Leder committed
            try: #Try making the database structure, if fails Database was already created.
    
    Jonas Leder's avatar
    Jonas Leder committed
                cursor.execute(create_table)
    
            except sqlite3.OperationalError:
    
    Jonas Leder's avatar
    Jonas Leder committed
                pass
    
    
    
    Jonas Leder's avatar
    Jonas Leder committed
    def makeQR(text): #This function is used to create a QR code and encode it base64, if you make a new shortlink
    
        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_L,
            box_size=10,
            border=1,
        )
        qr.add_data(text)
        qr.make(fit=True)
    
        img = qr.make_image(fill_color="black", back_color="white")
        with BytesIO() as buffer:
            img.save(buffer, 'jpeg')
            return base64.b64encode(buffer.getvalue()).decode()
    
    
    Jonas Leder's avatar
    Jonas Leder committed
    @app.route('/', methods=['GET', 'POST'])
    def home():
    
    Jonas Leder's avatar
    Jonas Leder committed
        if request.method == 'POST': #Post will be executed if the client inserts a new entry
    
            if (request.form.get('url').replace(" ", "") == ""):
    
    Jonas Leder's avatar
    Jonas Leder committed
                return render_template('home.html', builddate=builddate, domain=domain_prepared, snackbar="Please enter a url to short, before submitting this form", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')]) #return the user the prefilled form with an error message, because no url to short was provided
    
            if (request.form.get('short').replace(" ", "") == ""):
    
    Jonas Leder's avatar
    Jonas Leder committed
                return render_template('home.html', builddate=builddate, domain=domain_prepared, snackbar="Please enter a short name, before submitting this form", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')]) #return the user the prefilled form with an error message, because no short link was provided
            shorturl = (request.form.get('domain') + "/" + request.form.get('short')).lower()
    
    
            url = request.form.get('url')
    
    Jonas Leder's avatar
    Jonas Leder committed
            with sqlite3.connect('db/urls.db') as conn: #Check if another user already used the short link
    
    Jonas Leder's avatar
    Jonas Leder committed
                cursor = conn.cursor()
    
                res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [shorturl])
    
                try:
                    short = res.fetchone()
                    already_used = False
                    if short is not None:
                        already_used = True
    
                except:
    
                if not already_used: #If short link wasn't used before, insert the link in the Database.
    
                    res = cursor.execute(
                        'INSERT INTO WEB_URL (LONG_URL, SHORT_URL) VALUES (?, ?)',
    
                        [url, shorturl]
    
                    return render_template('home.html', short_url=shorturl, builddate=builddate, domain=domain_prepared, qrcode=makeQR("http://" + shorturl)) #return the shorten link to the user
    
    Jonas Leder's avatar
    Jonas Leder committed
                    return render_template('home.html', builddate=builddate, domain=domain_prepared, snackbar="URL already used, please try another one", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')]) #return the user the prefilled form with an error message, because the url was already used
    
    Jonas Leder's avatar
    Jonas Leder committed
        return render_template('home.html', builddate=builddate, domain=domain_prepared) #If request method is get, return the default site to create a new shorten link
    
    Jonas Leder's avatar
    Jonas Leder committed
    
    
    @app.route('/favicon.ico') #Redirect to the static url of the favicon
    
    Jonas Leder's avatar
    Jonas Leder committed
    def favicon():
    
    Jonas Leder's avatar
    Jonas Leder committed
        return redirect("/static/favicon.ico")
    
    Jonas Leder's avatar
    Jonas Leder committed
    
    @app.route('/<short_url>')
    def redirect_short_url(short_url):
        host = request.headers['Host']
        url = ""
    
    Jonas Leder's avatar
    Jonas Leder committed
        with sqlite3.connect('db/urls.db') as conn: #Get the original URL from the database
    
    Jonas Leder's avatar
    Jonas Leder committed
            cursor = conn.cursor()
    
    Jonas Leder's avatar
    Jonas Leder committed
            res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [host + "/" + short_url.lower()])
    
    Jonas Leder's avatar
    Jonas Leder committed
            try:
                short = res.fetchone()
    
    Jonas Leder's avatar
    Jonas Leder committed
                if short is not None: #If a long url is found
    
                    url = short[0]
                    error_404 = False
                else:
    
    Jonas Leder's avatar
    Jonas Leder committed
                    error_404 = True #If no url is found throw a 404, the problem is, if I throw at this point a 404 it will be catched by the try, catch block.
            except Exception as e: #If there happens an error, print the exception to the console and throw a 500 error
    
    Jonas Leder's avatar
    Jonas Leder committed
                print(e)
    
    Jonas Leder's avatar
    Jonas Leder committed
                abort(500)
    
    Jonas Leder's avatar
    Jonas Leder committed
        if not error_404: #If there was no 404 error before, redirect the user. If not throw a 404 error
    
    Jonas Leder's avatar
    Jonas Leder committed
            return redirect(url)
    
        else:
            abort(404)
    
    Jonas Leder's avatar
    Jonas Leder committed
    
    
    if __name__ == '__main__':
    
    Jonas Leder's avatar
    Jonas Leder committed
        table_check()# This code checks whether database table is created or not
    
    Jonas Leder's avatar
    Jonas Leder committed
        if production: #Check if production variable is set to true use the waitress webserver, else use the buildin flask webserver, with more debug output
            serve(app, host='0.0.0.0', port= 5000) #Start the Webserver for all users on port 5000
        else:
            app.run(host='0.0.0.0', port=5000, debug=True)