init
commit
aa469e758d
@ -0,0 +1,3 @@
|
|||||||
|
venv/
|
||||||
|
.DS_Store
|
||||||
|
txt/
|
@ -0,0 +1,15 @@
|
|||||||
|
class PrefixMiddleware(object):
|
||||||
|
def __init__(self, app, prefix=""):
|
||||||
|
self.app = app
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
def __call__(self, environ, start_response):
|
||||||
|
|
||||||
|
if environ["PATH_INFO"].startswith(self.prefix):
|
||||||
|
environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :]
|
||||||
|
environ["SCRIPT_NAME"] = self.prefix
|
||||||
|
return self.app(environ, start_response)
|
||||||
|
else:
|
||||||
|
start_response("404", [("Content-Type", "text/plain")])
|
||||||
|
return ["This url does not belong to the app.".encode()]
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
click==8.1.3
|
||||||
|
Flask==2.2.2
|
||||||
|
importlib-metadata==5.1.0
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.1
|
||||||
|
python-dotenv==0.21.0
|
||||||
|
python-frontmatter==1.0.0
|
||||||
|
PyYAML==6.0
|
||||||
|
Werkzeug==2.2.2
|
||||||
|
zipp==3.11.0
|
@ -0,0 +1,80 @@
|
|||||||
|
import os
|
||||||
|
from flask import Flask, render_template, url_for, send_from_directory
|
||||||
|
import frontmatter
|
||||||
|
import markdown
|
||||||
|
from prefix import PrefixMiddleware
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from time import strftime, localtime
|
||||||
|
import random
|
||||||
|
import glob
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
prefix = os.environ.get('URL_PREFIX', '')
|
||||||
|
port = os.environ.get('PORT', 3000)
|
||||||
|
debug = os.environ.get('DEBUG', False)
|
||||||
|
|
||||||
|
# ---
|
||||||
|
# Functions
|
||||||
|
# ---
|
||||||
|
|
||||||
|
def list_files(folder, remove_ext=False):
|
||||||
|
''' Read all the functions in a folder '''
|
||||||
|
names = []
|
||||||
|
for entry in os.scandir(folder):
|
||||||
|
# add to the list only proper files
|
||||||
|
if entry.is_file(follow_symlinks=False):
|
||||||
|
# remove the extension from the filename
|
||||||
|
n = entry.name
|
||||||
|
if remove_ext:
|
||||||
|
n = os.path.splitext(entry.name)[0]
|
||||||
|
names.append(n)
|
||||||
|
return names
|
||||||
|
|
||||||
|
def txt_list():
|
||||||
|
''' Generate list of writings '''
|
||||||
|
files = sorted(filter(os.path.isfile, glob.glob('txt/[!.]*.md')), key=lambda file: os.path.getmtime(file), reverse=True)
|
||||||
|
writings = []
|
||||||
|
for file in files:
|
||||||
|
with open(file) as f:
|
||||||
|
meta, content = frontmatter.parse(f.read())
|
||||||
|
meta['slug'] = os.path.splitext(os.path.basename(file))[0]
|
||||||
|
meta['last_edit'] = strftime('%d.%m.%Y', localtime(os.path.getmtime(file)))
|
||||||
|
writings.append(meta)
|
||||||
|
|
||||||
|
return writings
|
||||||
|
|
||||||
|
|
||||||
|
# ---
|
||||||
|
# Create Flask App
|
||||||
|
# ---
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix=prefix)
|
||||||
|
|
||||||
|
|
||||||
|
# ---
|
||||||
|
# Routes
|
||||||
|
# ---
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def home():
|
||||||
|
return render_template('home.html', writings=txt_list())
|
||||||
|
|
||||||
|
@app.route('/txt/<slug>')
|
||||||
|
def txt(slug):
|
||||||
|
try:
|
||||||
|
with open(f'txt/{slug}.md') as f:
|
||||||
|
meta, content = frontmatter.parse(f.read())
|
||||||
|
text = {**meta}
|
||||||
|
text['content']=markdown.markdown(content)
|
||||||
|
return render_template('text.html', text=text)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return 'File not found!'
|
||||||
|
|
||||||
|
# ---
|
||||||
|
# Run the app
|
||||||
|
# ---
|
||||||
|
|
||||||
|
app.run(port=port, debug=debug)
|
@ -0,0 +1,57 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
* + * {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 8px 0;
|
||||||
|
font-family: serif;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.excerpt {
|
||||||
|
margin: 16px 0;
|
||||||
|
max-width: 60ch;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: currentColor;
|
||||||
|
background-color: #eee;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-edit {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
max-width: 80ch;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
main ul,
|
||||||
|
main ol {
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
main code {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Souptxt</title>
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='style.css')}}" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Souptxt</h1>
|
||||||
|
<p>Writings for the thesis</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for writing in writings %}
|
||||||
|
<li>
|
||||||
|
<a href="{{url_for('txt', slug=writing['slug'])}}">{{writing.title}}</a>
|
||||||
|
<span class="last-edit"> last edit: {{writing.last_edit}}</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>{{text.title}}</title>
|
||||||
|
<link rel="stylesheet" href="{{url_for('static', filename='style.css')}}" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>{{text.title}}</h1>
|
||||||
|
<a href="{{url_for('home')}}">back</a>
|
||||||
|
<main>{{text.content | safe}}</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue