diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..bbf6c2a --- /dev/null +++ b/settings.py @@ -0,0 +1,11 @@ +#!/usr/bin/python3 +DEBUG = True +DATABASE = "urlshort.db" +FOOBAR = "PONG" +DEFAULT_SCHEMA = ''' +CREATE TABLE IF NOT EXISTS pastes (id INT PRIMARY KEY, paste_url TEXT NOT NULL, target TEXT NOT NULL, method_id INT NOT NULL, ip TEXT NOT NULL, timestamp TEXT NOT NULL); + +CREATE TABLE IF NOT EXISTS methods (id INT PRIMARY KEY, name TEXT NOT NULL); + +CREATE TABLE IF NOT EXISTS access (id INT PRIMARY KEY NOT NULL, paste_id TEXT NOT NULL, ip TEXT NOT NULL, user_agent TEXT NOT NULL); +''' diff --git a/templates/new_paste.html b/templates/new_paste.html new file mode 100644 index 0000000..fa314b7 --- /dev/null +++ b/templates/new_paste.html @@ -0,0 +1,2 @@ + +http://localhost:5000/{{paste_id}} diff --git a/urlshort.db b/urlshort.db new file mode 100644 index 0000000..57aceec Binary files /dev/null and b/urlshort.db differ diff --git a/urlshort.py b/urlshort.py new file mode 100644 index 0000000..bf87ca3 --- /dev/null +++ b/urlshort.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +from flask import Flask, g, request, render_template, redirect +import sqlite3 +import random +import string +import math +import time +from flask_limiter import Limiter +from contextlib import closing +from urllib.parse import urlparse + +app = Flask(__name__) +app.config.from_object("settings") +limiter = Limiter(app, global_limits=["2 per minute", "100 per day"]) + +@app.before_request +def before_request(): + g.db = connect_db() + +@app.teardown_request +def teardown_request(exception): + db = getattr(g, "db", None) + if db is not None: + db.close() + +@app.route("/") +@limiter.exempt +def root(): + return "Welcome to root!" + +@app.route("/") +@limiter.exempt +def paste(pasteID): + target = query_db("SELECT target FROM pastes WHERE paste_url = ?", [pasteID], one=True) + if target is None: + return "Shorturl not found!" + else: + url = urlparse(target["target"]) + if url.scheme is "": + target = "http://" + url.path + else: + target = url.scheme + "://" + url.netloc + url.path + return redirect(target, 301) + +@app.route("/new") +@limiter.exempt +def new_paste(): + target_url = request.args.get("target", "") + paste_id = add_redirect(target_url, 1) + return render_template("new_paste.html", paste_id=paste_id) + +def add_redirect(target_url, method_id): + paste_id = gen_new_id(5) + ip = request.remote_addr + timestamp = int(time.time()) + db = getattr(g, "db", None) + if db is not None: + query = "INSERT INTO pastes (paste_url, target, method_id, ip, timestamp) VALUES (:paste_url, :target, :method_id, :ip, :timestamp);" + args = {"paste_url" : paste_id, "target" : target_url, "method_id" : "1", "ip" : ip, "timestamp" : timestamp} + db.cursor().execute(query, args) + db.commit() + db.close() + return paste_id + +def gen_new_id(length): + while True: + new_id = "".join([random.choice(string.ascii_letters + string.digits) for n in range(length)]) + if query_db("SELECT * FROM pastes WHERE paste_url = ?", [new_id], one=True) is None: + break + return new_id + +def query_db(query, args=(), one=False): + with closing(connect_db()) as db: + db.row_factory = sqlite3.Row + cur = db.cursor().execute(query, args) + rv = cur.fetchall() + cur.close() + return (rv[0] if rv else None) if one else rv + +def init_db(): + with closing(connect_db()) as db: + db.cursor().executescript(app.config["DEFAULT_SCHEMA"]) + db.commit() + +def connect_db(): + return sqlite3.connect(app.config["DATABASE"]) + +if __name__ == "__main__": + app.run(debug=app.config["DEBUG"])