mirror of
https://git.ikl.sh/132ikl/liteshort.git
synced 2023-08-10 21:13:04 +03:00
Add POST form to shorten link, shortened links to database, configuration file
This commit is contained in:
parent
657ee72ecb
commit
513050e131
3
.gitignore
vendored
3
.gitignore
vendored
@ -105,3 +105,6 @@ venv.bak/
|
|||||||
|
|
||||||
# PyCharm
|
# PyCharm
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# Databases
|
||||||
|
*.db
|
||||||
|
22
config.yml
22
config.yml
@ -1,9 +1,29 @@
|
|||||||
# Username to make admin API requests
|
# Username to make admin API requests
|
||||||
|
# Default: 'admin'
|
||||||
admin_username: 'admin'
|
admin_username: 'admin'
|
||||||
|
|
||||||
# Plaintext password to make admin API requests
|
# Plaintext password to make admin API requests
|
||||||
# Safe to remove if admin_hashed_password is set
|
# Safe to remove if admin_hashed_password is set
|
||||||
|
# Default: commented out, 'password'
|
||||||
#admin_password: 'password'
|
#admin_password: 'password'
|
||||||
|
|
||||||
# Hashed password (bcrypt) to make admin API requests - Preferred over plaintext, use securepass.sh to generate
|
# Hashed password (bcrypt) to make admin API requests - Preferred over plaintext, use securepass.sh to generate
|
||||||
admin_hashed_password: 'test'
|
# Don't include the <username>: segment, just the hash
|
||||||
|
# Default: '$2y$15$Dhll3IY42R.JNOYazarlG.8IndwMjxmHLpFsebJzcGTJd.gbsAwna' (hash for 'password')
|
||||||
|
admin_hashed_password: '$2y$15$Dhll3IY42R.JNOYazarlG.8IndwMjxmHLpFsebJzcGTJd.gbsAwna'
|
||||||
|
|
||||||
|
# Filename of the URL database
|
||||||
|
# Default: 'urls'
|
||||||
|
database_name: 'urls'
|
||||||
|
|
||||||
|
# Length of random short URLs by default
|
||||||
|
# Default: 4
|
||||||
|
random_length: 4
|
||||||
|
|
||||||
|
# Allowed URL characters
|
||||||
|
# Default: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
|
||||||
|
allowed_chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
|
||||||
|
|
||||||
|
# Amount of time in seconds to spend generating random short URLs until timeout
|
||||||
|
# Default: 5
|
||||||
|
random_gen_timeout: 5
|
115
liteshort.py
115
liteshort.py
@ -1,27 +1,122 @@
|
|||||||
from flask import Flask
|
from flask import Flask, Response, request, current_app, g, send_from_directory
|
||||||
|
import bcrypt
|
||||||
|
import random
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
new_config = yaml.load(open("config.yml"))
|
new_config = yaml.load(open('config.yml'))
|
||||||
if "admin_hashed_password" in new_config.keys():
|
new_config = {k.lower(): v for k, v in new_config.items()} # Make config keys case insensitive
|
||||||
new_config["password"] = new_config["admin_hashed_password"]
|
|
||||||
elif "admin_password" in new_config.keys():
|
req_options = {'admin_username': 'admin', 'database_name': "urls", 'random_length': 4,
|
||||||
new_config["password"] = new_config["admin_password"]
|
'allowed_chars': 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
|
||||||
|
'random_gen_timeout': 5
|
||||||
|
}
|
||||||
|
|
||||||
|
config_types = {'admin_username': str, 'database_name': str, 'random_length': int,
|
||||||
|
'allowed_chars': str, 'random_gen_timeout': int}
|
||||||
|
|
||||||
|
for option in req_options.keys():
|
||||||
|
if option not in new_config.keys(): # Make sure everything in req_options is set in config
|
||||||
|
new_config[option] = req_options[option]
|
||||||
|
|
||||||
|
for option in new_config.keys():
|
||||||
|
if option in config_types:
|
||||||
|
if not type(new_config[option]) is config_types[option]:
|
||||||
|
raise TypeError(option + " must be type " + config_types[option].__name__)
|
||||||
|
|
||||||
|
if 'admin_hashed_password' in new_config.keys(): # Sets config value to see if bcrypt is required to check password
|
||||||
|
new_config['password_hashed'] = True
|
||||||
|
elif 'admin_password' in new_config.keys():
|
||||||
|
new_config['password_hashed'] = False
|
||||||
else:
|
else:
|
||||||
raise Exception("admin_password or admin_hashed_password must be set in config.yml")
|
raise TypeError('admin_password or admin_hashed_password must be set in config.yml')
|
||||||
return new_config
|
return new_config
|
||||||
|
|
||||||
|
|
||||||
|
def check_password(password, pass_config):
|
||||||
|
if pass_config['password_hashed']:
|
||||||
|
return bcrypt.checkpw(password.encode('utf-8'), pass_config['admin_hashed_password'].encode('utf-8'))
|
||||||
|
elif not pass_config['password_hashed']:
|
||||||
|
return password == pass_config['admin_password']
|
||||||
|
else:
|
||||||
|
raise RuntimeError('This should never occur! Bailing...')
|
||||||
|
|
||||||
|
|
||||||
|
def check_short_exist(database, short):
|
||||||
|
database.cursor().execute("SELECT long FROM urls WHERE short = ?", (short,))
|
||||||
|
result = database.cursor().fetchone()
|
||||||
|
if database.cursor().fetchone():
|
||||||
|
return result
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_long_exist(database, long):
|
||||||
|
database.cursor().execute("SELECT short FROM urls WHERE long = ?", (long,))
|
||||||
|
result = database.cursor().fetchone()
|
||||||
|
if database.cursor().fetchone():
|
||||||
|
return result
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def generate_short():
|
||||||
|
return ''.join(random.choice(current_app.config['allowed_chars'])
|
||||||
|
for i in range(current_app.config['random_length']))
|
||||||
|
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
if 'db' not in g:
|
||||||
|
g.db = sqlite3.connect(
|
||||||
|
''.join((current_app.config['database_name'], '.db')),
|
||||||
|
detect_types=sqlite3.PARSE_DECLTYPES
|
||||||
|
)
|
||||||
|
g.db.row_factory = sqlite3.Row
|
||||||
|
g.db.cursor().execute('CREATE TABLE IF NOT EXISTS urls (long,short)')
|
||||||
|
return g.db
|
||||||
|
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
print(config["password"])
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
app.config.update(config) # Add loaded YAML config to Flask config
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def hello_world():
|
def main():
|
||||||
return 'Hello World!'
|
return send_from_directory('static', 'main.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/', methods=['POST'])
|
||||||
|
def main_post():
|
||||||
|
if 'long' in request.form and request.form['long']:
|
||||||
|
database = get_db()
|
||||||
|
if 'short' in request.form and request.form['short']:
|
||||||
|
for char in request.form['short']:
|
||||||
|
if char not in current_app.config['allowed_chars']:
|
||||||
|
return Response('Character ' + char + ' not allowed in short URL.', status=200)
|
||||||
|
short = request.form['short']
|
||||||
|
else:
|
||||||
|
timeout = time.time() + current_app.config['random_gen_timeout']
|
||||||
|
while True:
|
||||||
|
if time.time() >= timeout:
|
||||||
|
return Response('Timeout while generating random short URL.', status=200)
|
||||||
|
short = generate_short()
|
||||||
|
if not check_short_exist(database, short):
|
||||||
|
break
|
||||||
|
short_exists = check_short_exist(database, short)
|
||||||
|
long_exists = check_long_exist(database, request.form['long'])
|
||||||
|
if long_exists and 'short' not in request.form:
|
||||||
|
return request.base_url + long_exists
|
||||||
|
if short_exists:
|
||||||
|
return Response('Short URL already exists.', status=200)
|
||||||
|
database.cursor().execute("INSERT INTO urls (long,short) VALUES (?,?)", (request.form['long'], short))
|
||||||
|
database.commit()
|
||||||
|
database.close()
|
||||||
|
return "Your shortened URL is available at " + request.base_url + short
|
||||||
|
else:
|
||||||
|
return "Long URL required!"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
## bcrypt passwd generator ##
|
## bcrypt passwd generator ##
|
||||||
#############################
|
#############################
|
||||||
CMD=$(which htpasswd 2>/dev/null)
|
CMD=$(which htpasswd 2>/dev/null)
|
||||||
OPTS="-nBC 15"
|
OPTS="-nBC 12"
|
||||||
|
|
||||||
read -p "Username: " USERNAME
|
read -p "Username: " USERNAME
|
||||||
|
|
||||||
|
20
static/main.html
Normal file
20
static/main.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!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>
|
Loading…
Reference in New Issue
Block a user