From 85850a9d63dc8048767ffbbfef3cb21c6cd32156 Mon Sep 17 00:00:00 2001 From: Raphael Maenle Date: Thu, 12 Aug 2021 11:45:31 +0200 Subject: [PATCH] initial commit, adding flask structure, README, routes - routes defined in routes.py uses the function to catch every non-empty url comming along - request.full_path is used to get the entire url, as would remove eg GET '?' from the url, which we don't want for the link forwarding. - request.full_path[1:] removes the '/' from the url --- .gitignore | 1 + README.md | 16 +++++++++ app/__init__.py | 8 +++++ app/routes.py | 42 +++++++++++++++++++++++ app/static/css/style.css | 10 ++++++ app/static/res/favicon.ico | Bin 0 -> 15406 bytes app/static/res/short.json | 1 + app/template/short.html | 18 ++++++++++ docker/README.md | 7 ++++ docker/docker-compose.yml | 28 +++++++++++++++ docker/short-server/Dockerfile | 8 +++++ docker/short-server/docker-entrypoint.sh | 12 +++++++ server.py | 8 +++++ 13 files changed, 159 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/__init__.py create mode 100644 app/routes.py create mode 100644 app/static/css/style.css create mode 100644 app/static/res/favicon.ico create mode 100644 app/static/res/short.json create mode 100644 app/template/short.html create mode 100644 docker/README.md create mode 100644 docker/docker-compose.yml create mode 100644 docker/short-server/Dockerfile create mode 100755 docker/short-server/docker-entrypoint.sh create mode 100755 server.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a9aaa9 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +## Link shortener + +this project is a tiny flask implementation that automatically generates +shortened links for any url. + +If you're on a website, `add short.maenle.tech` to the beginning of the url, +and the flask app returns a shortened url that it saves in a json file on the +server and forwards to the original url when accessed. + + +## Further Todos + +- add https encryption +- add uWSGI +- use sqlite3 instead of json + diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..2abee70 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,8 @@ +from flask import Flask + + +app = Flask(__name__, + static_folder='static', + template_folder='template') + +from app import routes diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000..83b4ecf --- /dev/null +++ b/app/routes.py @@ -0,0 +1,42 @@ +from flask import render_template,redirect,request, jsonify +from app import app + +import os, json +import random, string + +app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24) +app.view_functions['static'] + +SHORT_HOST = 'short.maenle.tech' + + +@app.route("/to/", host=SHORT_HOST) +def short(short): + f = open('app/static/res/short.json', 'r') + data = json.load(f) + for s in data: + if s['short'] == short: + return redirect(s['path'], code=302) + + return jsonify(success=False) + +@app.route("/", host=SHORT_HOST) +def long(path): + full_path = request.full_path[1:] + f = open('app/static/res/short.json', 'r') + data = json.load(f) + for s in data: + if s['path'] == full_path: + return jsonify(path="short.maenle.tech/to/"+s['short']) + + letters = string.ascii_lowercase + short = ''.join(random.choice(letters) for i in range(12)) + + data.append({'path': full_path, 'short': short}) + f = open('app/static/res/short.json', 'w') + json.dump(data, f) + f.close() + template = render_template("short.html", path="short.maenle.tech/to/"+short) + return template + + diff --git a/app/static/css/style.css b/app/static/css/style.css new file mode 100644 index 0000000..6653bba --- /dev/null +++ b/app/static/css/style.css @@ -0,0 +1,10 @@ +html +{ + font-family: Segoe UI, Frutiger, sans-serif; + width: 100%; height: 100% +} + +body { + margin: 0px; +} + diff --git a/app/static/res/favicon.ico b/app/static/res/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..774f1ca8f302a0e7bdc3edc08b4f41df01ff2cc0 GIT binary patch literal 15406 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 + + + + + Link Shortening + + + + + +

+ Your Link: +

+ {{ path }} + + + diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..db506b9 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,7 @@ +## Goal + +run short-server in a docker-compose environment + +## Usage + +1. `docker-compose up -d` starts the containers in background diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..fd56b22 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3' +services: + short-server: + build: + context: ./short-server + image: daria/short-server:latest + container_name: short-server + environment: + - FLASK_APP=/home/short-server/server.py + - FLASK_ENV=development + volumes: + - ../:/home/short-server + ports: + - "0.0.0.0:8086:8086" + networks: + net: + ipv4_address: '172.25.0.12' + +volumes: + database: + driver: local + +networks: + net: + ipam: + driver: default + config: + - subnet: 172.25.0.0/24 diff --git a/docker/short-server/Dockerfile b/docker/short-server/Dockerfile new file mode 100644 index 0000000..3d2b44a --- /dev/null +++ b/docker/short-server/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.8-slim-buster +RUN apt-get update && apt-get upgrade -y +RUN apt-get install gcc libmariadbclient-dev -y +RUN pip3 install google-oauth google-api-python-client +RUN pip3 install flask Flask-SQLAlchemy flask_migrate flask_wtf python-dotenv mysqlclient +COPY docker-entrypoint.sh /usr/local/bin/ +EXPOSE 3001 +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/docker/short-server/docker-entrypoint.sh b/docker/short-server/docker-entrypoint.sh new file mode 100755 index 0000000..50bbc64 --- /dev/null +++ b/docker/short-server/docker-entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +sleep 5 + +if [[ "$1" = "shell" ]]; then + exec /bin/bash +fi + +cd /home/short-server +python3 server.py + diff --git a/server.py b/server.py new file mode 100755 index 0000000..9677b95 --- /dev/null +++ b/server.py @@ -0,0 +1,8 @@ +from app import app + +@app.shell_context_processor +def make_shell_context(): + return{'app': app, 'position': Position} + +if __name__ == '__main__': + app.run('0.0.0.0', 8088, debug=True)