diff options
Diffstat (limited to 'server.py')
-rw-r--r-- | server.py | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/server.py b/server.py new file mode 100644 index 0000000..8614fe3 --- /dev/null +++ b/server.py @@ -0,0 +1,198 @@ +import os +from flask import Flask +from flask import request, send_from_directory, abort +from flask import render_template, render_template_string +from siteconfig import siteconfig + +app = Flask(__name__) + +# bit of a hack. +# Brackets don't play nicely with Jinja so instead of using .format, +# we just replace the special charcater $ +CONTENT_BLOCK = ( + "{% extends 'base.html' %}" + "{% block content %}" + "$" + "{% endblock %}" +) + + +def default_context(): + """ + default_context - returns the minimum info needed to render a template--the + domain name (for the home directory), and the top site directories which + make up the navbar + """ + return { + 'domain': app.config['DOMAIN'], + 'navbar': sorted(app.config['MAIN_SITE_DIRS']) + } + + +def index_dir(path): + """ + index_dir - Given a directory at `path`, list it's contents, + and sort each item as a file or a directory. + + return - a tuple with the values: + > a list of directories in `path`, + > a list of files in `path`, + > (if present), a list of external links to add to the index.html of the + directory + > (if present), a short description (string) of what the directory contains + + Lists are sorted alphabetically + """ + dirs = [] + files = [] + links = [] + description = "" + contents = os.listdir(path) + for obj in contents: + if os.path.isfile(path + '/' + obj): + if obj == siteconfig.LINKS_FILE: + with open(path + '/' + obj) as f: + links = f.readlines() + elif obj == siteconfig.DESC_FILE: + with open(path + '/' + obj) as f: + description = f.read() + elif obj.startswith('.'): + continue + else: + files.append(obj) + elif os.path.isdir(path + '/' + obj): + if obj.startswith('.'): + continue + else: + dirs.append(obj) + + return sorted(dirs), sorted(files), sorted(links), description + + +def is_hidden_path(path): + """ + Tests if last object specified in `path` is hidden. + Inspired by Unix. On Windows, diretories won't actually be "hidden" but + they are still not indexed by this program + """ + return path.split('/')[-1].startswith('.') + + +@app.route("/") +def home(): + """ + home - renders the template `home.html` as the main index file + + If you'd like to customize your home page, that is the file you want to + edit, though you can optionally change the title here if you wish + """ + context = default_context() + context.update( + { + 'title': app.config['HOME_TITLE'], + 'parent_dir': '/' + } + ) + return render_template("site/home.html", **context) + + +# from: https://pythonise.com/series/learning-flask/sending-files-with-flask +@app.route("/<path:path>") +def render_file(path): + """ + render_file - renders an HTML document for the given `path`. + + If `path` is an HTML file it is rendered within the base template, + otherwise, the raw file is returned. If `path` points to a directory, this + function instead creates an index for the directory containing it's files, + links, and other info. + """ + if is_hidden_path(path): + abort(404) + abs_path = "./templates/site/" + path + context = default_context() + context.update( + { + 'title': path.split('.')[0].upper(), + 'parent_dir': '/' + '/'.join(path.split('/')[:-1]) + } + ) + if os.path.isfile(abs_path): + if abs_path.endswith('.html'): + with open(abs_path) as f: + content = f.read() + print(path.split('/')[-2] + '/') + return render_template_string(CONTENT_BLOCK.replace('$', content), **context) + else: + # not an html file, so don't render it + return send_from_directory('templates/site/', path, + mimetype=siteconfig.MIMETYPES.get( + f".{ path.split('.')[-1] }", siteconfig.DEFAULT_MIMETYPE + ) + ) + elif os.path.isdir(abs_path): + dirs, files, links, description = index_dir(abs_path) + context.update( + { + 'cur_dir': path.split('/')[-1] + '/', + 'dirs': dirs, + 'files': files, + 'links': links, + 'description': description + } + ) + return render_template("index.html", **context) + else: + context.update({'errors': "404 File not found"}) + return render_template("base.html", **context) + + +@app.route("/raw/<path:path>") +def send_file_from_site(path): + """ + send_file - instead of rendering a file within a template as with + `render_file`, send the raw file to the user + """ + return send_from_directory('template/site/', path, + mimetype=siteconfig.MIMETYPES.get( + f".{ path.split('.')[-1] }", siteconfig.DEFAULT_MIMETYPE + ) + ) + + +@app.route("/static/<path:path>") +def send_file_from_static(path): + """ + send_file - instead of rendering a file within a template as with + `render_file`, send the raw file to the user + """ + return send_from_directory('static/', path, + mimetype=siteconfig.MIMETYPES.get( + f".{ path.split('.')[-1] }", siteconfig.DEFAULT_MIMETYPE + ) + ) + + +def setup(): + """ + setup - sets up the app according to the settings specified (or not + speified) in `siteconfig` + """ + if siteconfig.SECRET_KEY: + app.config['SECRET_KEY'] = siteconfig.SECRET_KEY + else: + SECRET_KEY = os.urandom(32) + app.config['SECRET_KEY'] = SECRET_KEY + + if siteconfig.MAIN_SITE_DIRS: + app.config.update({'MAIN_SITE_DIRS': siteconfig.MAIN_SITE_DIRS}) + else: + app.config.update({'MAIN_SITE_DIRS': index_dir('./templates/site')[0]}) + + app.config.update({'DOMAIN': siteconfig.DOMAIN}) + app.config.update({'HOME_TITLE': siteconfig.HOME_TITLE}) + + +if __name__ == '__main__': + setup() + app.run(host=siteconfig.HOST, port=siteconfig.PORT) |