#!/usr/bin/env python3 #Import of Libraries from waitress import serve #Used as webserver (Production) from flask import Flask, request, redirect, make_response, abort #Used to prepare the dynamic pages from os import environ #Used for getting the enviorement variables from itsdangerous import URLSafeSerializer #used for signing the cookies from random import choice#used for signing the cookies from string import ascii_lowercase #used for signing the cookies from requests import get #used to get the current version on the Server #Import of shorter specific files from table_check import table_check #import the table check file from makeqr import makeQR #Qr code generation tool from grecaptcha_verify import grecaptcha_verify #Tool to verify the google recaptcha response from newurl import newurl #Script to add the new urls to the database from redirectShortenURL import redirectShortenURL #Script to redirect the user to the long site from login import login #thandles the login from home import home #returns the main page from googlecallback import googleCallback #Script to handle the google oauth login from githubcallback import githubCallback #Script to handle the Github oauth login from userprofile import userProfile #This script handles the overview of the own links from deletelink import deleteLink #Script to delete Links of Users, if they want. from api import apiGet,apiPost #Scripts to handle the API domain_to_index = {} try: domain = environ["domains"].split(";") #Get the domains from the enviorement variable. If no enviorement variable is set it will be set to 127.0.0.1:5000 (for testing) except: domain = ["localhost:5000", "127.0.0.1:5000"] try: if(environ["show_build_date"] == "1"): #If you want to see the builddate you can enable this enviorement variable builddate = open("builddate.txt", "r").read() else: builddate = "" except: builddate = "" #If the enviorement Variable is not set also skip the builddate try: if(environ["show_version"] == "1"): #If you want to see the builddate you can enable this enviorement variable version = open("VERSION", "r").read() else: version = "" except: version = "" #If the enviorement Variable is not set also skip the version try: recaptchaPrivateKey = environ["recaptcha_private"] #Get the recaptcha keys, if not set set skipRecaptcha to true to skip the check. If the publicKey is not set the user also will not get the code needed for recaptcha delivered in the page. recaptchaPublicKey = environ["recaptcha_public"] if(recaptchaPrivateKey != "") and (recaptchaPublicKey != ""): #If the variables are empty also skip the captcha skipCaptcha = False else: skipCaptcha = True except: recaptchaPrivateKey = "" recaptchaPublicKey = "" skipCaptcha = True try: if(environ["production"] == "1"): #If you use this in production, please set this to 1, because the Flask Testserver is not very secure (e.g. shows error on Website) production = True else: production = False except: production = False try: #If you use https with a proxy afterwards you can set this to https and internal redirects will be https not http. This is to prevent bugs with modern browsers, bacause they block http content if the main page is loaded via https. url_scheme = environ["url_scheme"] except: url_scheme = "http" try: host=environ["host"] except: host="127.0.0.1" try: if(environ["login"] == "1"): loginEnabled = True else: loginEnabled = False except: loginEnabled = False try: if(environ["passwordToShort"] != ""): passwordProtected = True password = environ["passwordToShort"] else: passwordProtected = False password = "" except: passwordProtected = False password = "" if(loginEnabled): try: #Try to get the oauth keys, if it fails, abort and print a message to console GITHUB_CLIENT_ID = environ['GITHUB_CLIENT_ID'] GITHUB_CLIENT_SECRET = environ['GITHUB_CLIENT_SECRET'] GOOGLE_CLIENT_ID = environ['GOOGLE_CLIENT_ID'] GOOGLE_CLIENT_SECRET = environ['GOOGLE_CLIENT_SECRET'] except: print("please set the oauth keys and run again.") exit() try: #check, if admin wants to show a cookie notice to the user. if(environ["cookieNotice"] == "1"): cookieNotice = True else: cookieNotice = False except: cookieNotice = True try: #The secret key is used to crypt the auth cookie. There will be a seccond key to make the api key. secretKey = open("db/secretKey.txt", "r").read() except: secretKey = ''.join(choice(ascii_lowercase) for i in range(100)) #If we can't find the secret key(first run) we generate it in this step and write it to a file print("generated secret Key. Key is: " + secretKey) f = open("db/secretKey.txt", "w") f.write(secretKey) f.close() secretKey = open("db/secretKey.txt", "r").read() s = URLSafeSerializer(secretKey) sAPI = URLSafeSerializer("api_key_" + secretKey) index = 0 domain_prepared = "" for domains in domain: #Make from every domnain a entry for the select box later domain_prepared = domain_prepared + '<option value="' + str(domains) + '">' + str(domains) + '</option>' domain_to_index[domains] = str(index) index = index + 1 if(index > 1): showDomainSelect=True #Show only domain select, if there are more than one available else: showDomainSelect=False domain_prepared = domain[0] app = Flask(__name__) @app.route('/', methods=['GET']) def home_get(): return home(request, builddate, version, domain_prepared, recaptchaPublicKey, showDomainSelect, cookieNotice, domain_to_index, s, loginEnabled, passwordProtected) @app.route('/', methods=['POST']) #This function is used to create a new url def home_post(): return newurl(request, skipCaptcha, recaptchaPrivateKey, recaptchaPublicKey, builddate, version, domain_prepared, domain_to_index, showDomainSelect, cookieNotice, s, url_scheme, loginEnabled, passwordProtected, password) @app.route('/favicon.ico') #Redirect to the static url of the favicon def favicon(): return redirect("/static/favicon.ico", code=301) @app.route('/<short_url>') def redirect_short_url(short_url): return redirectShortenURL(request, short_url) @app.route('/user/login') def loginPage(): if(loginEnabled): return login(request, GITHUB_CLIENT_ID, cookieNotice, GOOGLE_CLIENT_ID, url_scheme, domain) else: abort(404) @app.route("/user/google-callback") def authorizeGoogle(): if(loginEnabled): return googleCallback(request, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, url_scheme, domain, s) else: abort(404) @app.route('/user/github-callback') #Github redirects to this link after the user authenticated. Then we use the Token we get from github and request via the github api the username and the userid def authorizeGithub(): if(loginEnabled): return githubCallback(request, GITHUB_CLIENT_SECRET, GITHUB_CLIENT_ID, s) else: abort(404) @app.route('/user/logout') def logout(): resp = make_response('<style>\n@media (prefers-color-scheme: dark) {\nbody {\ncolor: #b3b3b3;\nbackground: #151d28;\n}\n}\n</style>\n<h2>Logout successful. Redirecting you back in 2 sec.</h2>\n<script> window.setTimeout(function(){\nwindow.location.href = "/";\n}, 2000);</script>') resp.set_cookie('userID', "", max_age=0) #Set the max age of the cookies to 0, this means delete the cookies. resp.set_cookie('username', "", max_age=0) return resp @app.route('/user/links')#This function gives the user the posibility to see and delete his links def redirectOwnLinks(): return redirect("/user/links0", code=301) @app.route('/user/links<pageNumber>')#This function gives the user the posibility to see and delete his links def ownLinks(pageNumber): if(loginEnabled): return userProfile(request, cookieNotice, s, pageNumber, url_scheme) else: abort(404) @app.route('/user/delete') #This function is called if a user deletes an entrie def delete(): if(loginEnabled): return deleteLink(request, s) else: abort(404) @app.route('/user/makeqr', methods=['POST']) def makeQrCode(): link = request.form.get('link') return "data:image/jpeg;base64," + makeQR(url_scheme + "://" + link) @app.route('/user/api', methods=['POST']) def api(): return apiPost(request, url_scheme, domain, sAPI, passwordProtected, password) @app.route('/user/api', methods=['GET']) def apiDocs(): return apiGet(request, url_scheme, s, sAPI, passwordProtected) def startup(production): table_check()# This code checks whether database table is created or not versionServer = get("https://gitlab.jonasled.de/jonasled/url_shorter_docker/raw/master/VERSION").text if(open("VERSION", "r").read() != versionServer): print("Different version on the server found. Your version: " + open("VERSION", "r").read() + ", server version: " + versionServer + ".") 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=host, port= 5000, url_scheme=url_scheme) #Start the production Webserver for all users on port 5000 else: app.run(host=host, port=5000, debug=True) #Start the Webserver in Debug mode. This means, if the script runs in an error, it will show the error message in Browser. return app if (__name__ == "__main__"): startup(production) #Only run the startup script, if this file is directly called.