Add web interface styling and configuration, fix existing long URL retrieval and duplicate short URL checks
This commit is contained in:
		
							parent
							
								
									513050e131
								
							
						
					
					
						commit
						b58f1dd273
					
				
					 5 changed files with 123 additions and 45 deletions
				
			
		| 
						 | 
					@ -27,3 +27,7 @@ allowed_chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
 | 
				
			||||||
# Amount of time in seconds to spend generating random short URLs until timeout
 | 
					# Amount of time in seconds to spend generating random short URLs until timeout
 | 
				
			||||||
# Default: 5
 | 
					# Default: 5
 | 
				
			||||||
random_gen_timeout: 5
 | 
					random_gen_timeout: 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Name shown on tab while on site and on page header
 | 
				
			||||||
 | 
					# Default: 'liteshort'
 | 
				
			||||||
 | 
					site_name: 'liteshort'
 | 
				
			||||||
							
								
								
									
										64
									
								
								liteshort.py
									
										
									
									
									
								
							
							
						
						
									
										64
									
								
								liteshort.py
									
										
									
									
									
								
							| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
from flask import Flask, Response, request, current_app, g, send_from_directory
 | 
					from flask import Flask, request, current_app, g, render_template
 | 
				
			||||||
import bcrypt
 | 
					import bcrypt
 | 
				
			||||||
import random
 | 
					import random
 | 
				
			||||||
import sqlite3
 | 
					import sqlite3
 | 
				
			||||||
| 
						 | 
					@ -12,11 +12,11 @@ def load_config():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    req_options = {'admin_username': 'admin', 'database_name': "urls", 'random_length': 4,
 | 
					    req_options = {'admin_username': 'admin', 'database_name': "urls", 'random_length': 4,
 | 
				
			||||||
                   'allowed_chars': 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
 | 
					                   'allowed_chars': 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
 | 
				
			||||||
                   'random_gen_timeout': 5
 | 
					                   'random_gen_timeout': 5, 'site_name': 'liteshort'
 | 
				
			||||||
                   }
 | 
					                   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    config_types = {'admin_username': str, 'database_name': str, 'random_length': int,
 | 
					    config_types = {'admin_username': str, 'database_name': str, 'random_length': int,
 | 
				
			||||||
                    'allowed_chars': str, 'random_gen_timeout': int}
 | 
					                    'allowed_chars': str, 'random_gen_timeout': int, 'site_name': str}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for option in req_options.keys():
 | 
					    for option in req_options.keys():
 | 
				
			||||||
        if option not in new_config.keys():  # Make sure everything in req_options is set in config
 | 
					        if option not in new_config.keys():  # Make sure everything in req_options is set in config
 | 
				
			||||||
| 
						 | 
					@ -45,19 +45,18 @@ def check_password(password, pass_config):
 | 
				
			||||||
        raise RuntimeError('This should never occur! Bailing...')
 | 
					        raise RuntimeError('This should never occur! Bailing...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_short_exist(database, short):
 | 
					def check_short_exist(short):
 | 
				
			||||||
    database.cursor().execute("SELECT long FROM urls WHERE short = ?", (short,))
 | 
					    query = query_db('SELECT long FROM urls WHERE short = ?', (short,))
 | 
				
			||||||
    result = database.cursor().fetchone()
 | 
					    if query:
 | 
				
			||||||
    if database.cursor().fetchone():
 | 
					        return True
 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    return False
 | 
					    return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_long_exist(database, long):
 | 
					def check_long_exist(long):
 | 
				
			||||||
    database.cursor().execute("SELECT short FROM urls WHERE long = ?", (long,))
 | 
					    query = query_db('SELECT short FROM urls WHERE long = ?', (long,))
 | 
				
			||||||
    result = database.cursor().fetchone()
 | 
					    for i in query:
 | 
				
			||||||
    if database.cursor().fetchone():
 | 
					        if i and (len(i['short']) <= current_app.config["random_length"]):  # Checks if query if pre-existing URL is same as random length URL
 | 
				
			||||||
        return result
 | 
					            return i['short']
 | 
				
			||||||
    return False
 | 
					    return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,6 +76,23 @@ def get_db():
 | 
				
			||||||
    return g.db
 | 
					    return g.db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def query_db(query, args=(), one=False):
 | 
				
			||||||
 | 
					    cur = get_db().execute(query, args)
 | 
				
			||||||
 | 
					    rv = cur.fetchall()
 | 
				
			||||||
 | 
					    cur.close()
 | 
				
			||||||
 | 
					    return (rv[0] if rv else None) if one else rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def response(rq, short, error_msg=None):
 | 
				
			||||||
 | 
					    if 'json' in rq.form and rq.form['json']:
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        if short:
 | 
				
			||||||
 | 
					            return render_template("main.html", result=(True, rq.base_url + short))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return render_template("main.html", result=(False, error_msg))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config = load_config()
 | 
					config = load_config()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app = Flask(__name__)
 | 
					app = Flask(__name__)
 | 
				
			||||||
| 
						 | 
					@ -85,36 +101,36 @@ app.config.update(config)  # Add loaded YAML config to Flask config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route('/')
 | 
					@app.route('/')
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    return send_from_directory('static', 'main.html')
 | 
					    return render_template("main.html")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.route('/', methods=['POST'])
 | 
					@app.route('/', methods=['POST'])
 | 
				
			||||||
def main_post():
 | 
					def main_post():
 | 
				
			||||||
    if 'long' in request.form and request.form['long']:
 | 
					    if 'long' in request.form and request.form['long']:
 | 
				
			||||||
        database = get_db()
 | 
					 | 
				
			||||||
        if 'short' in request.form and request.form['short']:
 | 
					        if 'short' in request.form and request.form['short']:
 | 
				
			||||||
            for char in request.form['short']:
 | 
					            for char in request.form['short']:
 | 
				
			||||||
                if char not in current_app.config['allowed_chars']:
 | 
					                if char not in current_app.config['allowed_chars']:
 | 
				
			||||||
                    return Response('Character ' + char + ' not allowed in short URL.', status=200)
 | 
					                    return response(request, None, 'Character ' + char + ' not allowed in short URL.')
 | 
				
			||||||
            short = request.form['short']
 | 
					            short = request.form['short']
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            timeout = time.time() + current_app.config['random_gen_timeout']
 | 
					            timeout = time.time() + current_app.config['random_gen_timeout']
 | 
				
			||||||
            while True:
 | 
					            while True:
 | 
				
			||||||
                if time.time() >= timeout:
 | 
					                if time.time() >= timeout:
 | 
				
			||||||
                    return Response('Timeout while generating random short URL.', status=200)
 | 
					                    return response(request, None, 'Timeout while generating random short URL.')
 | 
				
			||||||
                short = generate_short()
 | 
					                short = generate_short()
 | 
				
			||||||
                if not check_short_exist(database, short):
 | 
					                if not check_short_exist(short):
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
        short_exists = check_short_exist(database, short)
 | 
					        short_exists = check_short_exist(short)
 | 
				
			||||||
        long_exists = check_long_exist(database, request.form['long'])
 | 
					        long_exists = check_long_exist(request.form['long'])
 | 
				
			||||||
        if long_exists and 'short' not in request.form:
 | 
					        if long_exists and not ('short' in request.form and request.form['short']):
 | 
				
			||||||
            return request.base_url + long_exists
 | 
					            return response(request, long_exists)
 | 
				
			||||||
        if short_exists:
 | 
					        if short_exists:
 | 
				
			||||||
            return Response('Short URL already exists.', status=200)
 | 
					            return response(request, None, "Short URL already exists.")
 | 
				
			||||||
 | 
					        database = get_db()
 | 
				
			||||||
        database.cursor().execute("INSERT INTO urls (long,short) VALUES (?,?)", (request.form['long'], short))
 | 
					        database.cursor().execute("INSERT INTO urls (long,short) VALUES (?,?)", (request.form['long'], short))
 | 
				
			||||||
        database.commit()
 | 
					        database.commit()
 | 
				
			||||||
        database.close()
 | 
					        database.close()
 | 
				
			||||||
        return "Your shortened URL is available at " + request.base_url + short
 | 
					        return response(request, short)
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return "Long URL required!"
 | 
					        return "Long URL required!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +0,0 @@
 | 
				
			||||||
<!DOCTYPE html>
 | 
					 | 
				
			||||||
<html lang="en">
 | 
					 | 
				
			||||||
<head>
 | 
					 | 
				
			||||||
    <meta charset="UTF-8">
 | 
					 | 
				
			||||||
    <title>liteshort</title>
 | 
					 | 
				
			||||||
</head>
 | 
					 | 
				
			||||||
<body>
 | 
					 | 
				
			||||||
    <form>
 | 
					 | 
				
			||||||
        Long URL:
 | 
					 | 
				
			||||||
        <br>
 | 
					 | 
				
			||||||
        <input name="long" type="url">
 | 
					 | 
				
			||||||
        <br>
 | 
					 | 
				
			||||||
        Short URL:
 | 
					 | 
				
			||||||
        <br>
 | 
					 | 
				
			||||||
        <input name="short" type="text">
 | 
					 | 
				
			||||||
        <br> <!-- TODO: Use CSS to do linebreaks -->
 | 
					 | 
				
			||||||
        <input type="submit" value="Shorten" formmethod="post">
 | 
					 | 
				
			||||||
    </form>
 | 
					 | 
				
			||||||
</body>
 | 
					 | 
				
			||||||
</html>
 | 
					 | 
				
			||||||
							
								
								
									
										46
									
								
								static/styles.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								static/styles.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					div.form {
 | 
				
			||||||
 | 
					  margin-top: 5%;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					input {
 | 
				
			||||||
 | 
					  margin: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div.success {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  font-family: Open Sans;
 | 
				
			||||||
 | 
					  border-radius: 2vh;
 | 
				
			||||||
 | 
					  padding: 2vh;
 | 
				
			||||||
 | 
					  color: #62ad2c;
 | 
				
			||||||
 | 
					  background-color: #E9FFD9;
 | 
				
			||||||
 | 
					  border: 1px solid #62ad2c;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div.error {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  font-family: Open Sans;
 | 
				
			||||||
 | 
					  border-radius: 2vh;
 | 
				
			||||||
 | 
					  padding: 2vh;
 | 
				
			||||||
 | 
					  color: #a86464;
 | 
				
			||||||
 | 
					  background-color: #FCE9E9;
 | 
				
			||||||
 | 
					  border: 1px solid #a86464;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
					  text-align: center
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div.success > a:link {
 | 
				
			||||||
 | 
					  color: #62ad2c;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					div.success > a:visited {
 | 
				
			||||||
 | 
					  color: #507c52;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					h2 {
 | 
				
			||||||
 | 
					  font-weight: normal;
 | 
				
			||||||
 | 
					  font-family: Open Sans;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								templates/main.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								templates/main.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
 | 
					    <title>{{ config.site_name }}</title>
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css">
 | 
				
			||||||
 | 
					    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">  
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					    <div class="form">
 | 
				
			||||||
 | 
					        <h2>{{ config.site_name }}</h2>
 | 
				
			||||||
 | 
					        <form class="pure-form">
 | 
				
			||||||
 | 
					            <input name="long" type="url" placeholder="Long URL">
 | 
				
			||||||
 | 
					            <p>
 | 
				
			||||||
 | 
					            <input name="short" type="text" placeholder="Custom link (optional)">
 | 
				
			||||||
 | 
					            <p>
 | 
				
			||||||
 | 
					            <button type="submit" class="pure-button pure-button-primary" formmethod="post">Shorten</button>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% if result is defined and result[0] %}
 | 
				
			||||||
 | 
					    <div class="success">
 | 
				
			||||||
 | 
					        ✓ Shortlink successfully generated! Available at  <a href="{{ result[1] }}">{{ result[1] }}</a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% elif result is defined and not result[0] %}
 | 
				
			||||||
 | 
					    <div class="error">
 | 
				
			||||||
 | 
					        ✖ Shortlink failed to generate! {{ result[1] }}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
		Reference in a new issue