From a457f355d128a33c0d9074ac05f11e73b88b5350 Mon Sep 17 00:00:00 2001 From: tiyn Date: Fri, 29 Jul 2022 16:51:58 +0200 Subject: [PATCH] first commit --- Dockerfile | 17 ++++ README.md | 68 ++++++++++++++ beaker_blog_alt.png | Bin 0 -> 3729 bytes rebuild.sh | 9 ++ src/.gitignore | 1 + src/app.py | 90 ++++++++++++++++++ src/config.py | 11 +++ src/content.py | 45 +++++++++ src/requirements.txt | 4 + src/static/css/dark.css | 83 +++++++++++++++++ src/static/css/light.css | 83 +++++++++++++++++ src/static/css/style.css | 169 ++++++++++++++++++++++++++++++++++ src/static/images/logo.png | 0 src/templates/archive.html | 11 +++ src/templates/error.html | 9 ++ src/templates/index.html | 11 +++ src/templates/rss.xml | 16 ++++ src/templates/standalone.html | 10 ++ src/templates/template.html | 32 +++++++ 19 files changed, 669 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 beaker_blog_alt.png create mode 100755 rebuild.sh create mode 100644 src/.gitignore create mode 100644 src/app.py create mode 100644 src/config.py create mode 100644 src/content.py create mode 100644 src/requirements.txt create mode 100644 src/static/css/dark.css create mode 100644 src/static/css/light.css create mode 100644 src/static/css/style.css create mode 100644 src/static/images/logo.png create mode 100644 src/templates/archive.html create mode 100644 src/templates/error.html create mode 100644 src/templates/index.html create mode 100644 src/templates/rss.xml create mode 100644 src/templates/standalone.html create mode 100644 src/templates/template.html diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3f2c87f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3 + +MAINTAINER tiyn tiyn@mail-mk.eu + +COPY src /blog + +WORKDIR /blog + +RUN pip3 install -r requirements.txt + +VOLUME /blog/templates/entry + +EXPOSE 5000 + +ENTRYPOINT [ "python3" ] + +CMD [ "app.py" ] diff --git a/README.md b/README.md new file mode 100644 index 0000000..6949af2 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# Container Critique + +This is a blog based on Pythons Flask framework. +The blog is intended to be used to review and critique things. + +## Features/To-Dos + +- [ ] Plain text support for blog entries + - [ ] HTML files (.html) + - [ ] Markdown Files (.md) +- [ ] Infinite-scroll blog page +- [ ] Archive page + - [ ] Months as headings + - [ ] Links to scrolling blog page + - [ ] Links to standalone article +- [ ] Standalone article page + - [ ] Links to scrolling blog page +- [ ] RSS feed +- [ ] Navigation + - [ ] Header + - [ ] Footer +- [ ] Switchable CSS + - [ ] CSS dark-theme + - [ ] CSS light-theme +- [ ] Config file +- [ ] Docker installation +- [ ] Logo + +## Usage + +## Deployment + +### PIP/Python + +- `git clone https://github.com/tiyn/container-critique` +- `cd container-critique/src` +- edit the `config.py` file according to your needs +- `pip3install -r requirements.txt` - install depenencies +- run `python app.py` +- blog is available on port 5000 + +### Docker + +Make sure you copy an example `config.py` and edit it before running the container. +The `config.py` can be found in the `src` folder. + +#### Volumes + +Set the following volumes with the -v tag. + +| Volume-Name | Container mount | Description | +| ------------- | --------------------------- | ------------------------------------------------------------ | +| `config-file` | `/blog/src/config.py` | Config file | +| `entries` | `/blog/src/templates/entry` | Directory for blog entries | +| `css` | `/blog/src/static/css` | (optional) Directory for css files | +| `html` | `/blog/src/templates` | (optional) Directory for templates (entry-volume not needed) | + +#### Ports + +Set the following ports with the -p tag. + +| Container-Port | Recommended outside port | Protocol | Description | +| -------------- | ------------------------ | -------- | ----------- | +| `5000` | `80` | TCP | HTTP port | + +#### Example run-command + +An example run command is shown in `rebuild.sh`. diff --git a/beaker_blog_alt.png b/beaker_blog_alt.png new file mode 100644 index 0000000000000000000000000000000000000000..5ad0e0f3fd933c03c82132f08f92d01733e9870c GIT binary patch literal 3729 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 UY97XRtV1V2Ff_we8IW%N3qZ`0g8%>k literal 0 HcmV?d00001 diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 0000000..816ac62 --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,9 @@ +#!/bin/sh +docker stop container-critique +docker rm container-critique +docker build . -t tiyn/container-critique +docker run --name container-critique \ + --restart unless-stopped \ + -p "5000:5000" \ + -e FLASK_ENV=development \ + -d tiyn/container-critique diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..0c10f93 --- /dev/null +++ b/src/app.py @@ -0,0 +1,90 @@ +from flask import Flask, flash, make_response, render_template, request, redirect, abort, url_for +from flask_login import current_user, login_user, LoginManager + +import content as con_gen +import config + +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, SubmitField, BooleanField +from wtforms.validators import DataRequired + +class LoginForm(FlaskForm): + username = StringField('Username', validators=[DataRequired()]) + password = PasswordField('Password', validators=[DataRequired()]) + remember_me = BooleanField('Remember Me') + submit = SubmitField('Sign In') + + +app = Flask(__name__) +login = LoginManager(app) + +TITLE = config.TITLE +STYLE = config.STYLE +DESCRIPTION = config.DESCRIPTION +WEBSITE = config.WEBSITE + + +class Config(object): + SECRET_KEY = '123534' + +app.config.from_object(Config) + + +@app.errorhandler(404) +def page_not_found(e): + return render_template('error.html', title=TITLE, errorcode='404', style=STYLE), 404 + + +@app.route('/') +@app.route('/index.html') +def index(): + content = con_gen.gen_index_string() + return render_template('index.html', title=TITLE, content_string=content, style=STYLE) + + +@app.route('/archive') +@app.route('/archive.html') +def blog_archive(): + content = con_gen.gen_arch_string() + return render_template('archive.html', title=TITLE, content_string=content, style=STYLE) + + +@app.route('/entry/') +def entry(path): + content = con_gen.gen_stand_string(path) + if content != '': + return render_template('standalone.html', title=TITLE, content_string=content, style=STYLE) + abort(404) + + +@app.route('/feed.xml') +@app.route('/rss.xml') +def feed(): + content = con_gen.get_rss_string() + rss_xml = render_template('rss.xml', content_string=content, title=TITLE, + description=DESCRIPTION, website=WEBSITE) + response = make_response(rss_xml) + response.headers['Content-Type'] = 'application/rss+xml' + return response + +@login.user_loader +def load_user(id): + return "" + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if current_user.is_authenticated: + return redirect(url_for('index')) + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user is None or not user.check_password(form.password.data): + flash('Invalid username or password') + return redirect(url_for('login')) + login_user(user, remember=form.remember_me.data) + return redirect(url_for('index')) + return render_template('login.html', title='Sign In', form=form) + + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000..bb76e62 --- /dev/null +++ b/src/config.py @@ -0,0 +1,11 @@ +# Name/title of your blog +TITLE = 'Container Critique' + +# Description for RSS of your blog +DESCRIPTION = 'This is your personal Container Critique.' + +# URL for your website: e.g. https://domain.tld +WEBSITE = 'localhost:5000' + +# Theme for the blog: dark, light +STYLE = 'dark' diff --git a/src/content.py b/src/content.py new file mode 100644 index 0000000..45e2615 --- /dev/null +++ b/src/content.py @@ -0,0 +1,45 @@ +ENTRY_DIR = 'templates/entry' + +def gen_arch_string(): + """ + Creates and returns a archive string of every file in ENTRY_DIR. + + Returns: + string: html-formatted archive-string + """ + return "" + + +def gen_index_string(): + """ + Create and returns a string including every file in the ENTRY_DIR as an index. + + Returns: + string: html-formatted index string + """ + return "" + + +def gen_stand_string(path_ex): + """ + Creates a html-string for a file. + If the file is markdown it will convert it. + This functions ensures upscaling for future formats. + + Parameters: + path_ex: path to a file. + + Returns: + string: html-formatted string string equivalent to the file + """ + return "" + + +def get_rss_string(): + """ + Create a rss-string of the blog and return it. + + Returns: + string: rss-string of everything that is in the ENTRY_DIR. + """ + return "" diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000..6b32b3a --- /dev/null +++ b/src/requirements.txt @@ -0,0 +1,4 @@ +Flask==2.1.2 +flask_login==0.6.2 +flask_wtf==1.0.1 +WTForms==3.0.1 diff --git a/src/static/css/dark.css b/src/static/css/dark.css new file mode 100644 index 0000000..75a2a05 --- /dev/null +++ b/src/static/css/dark.css @@ -0,0 +1,83 @@ +@import 'style.css'; + +:root { + --bg0: rgb(29,32,33); + --color0: rgb(220,120,0); + --error: rgb(255,0,0); + --footerbg0: rgb(29,32,33); + --link0: rgb(220, 120, 0); + --link1: rgb(255,255,255); + --menulink0: rgb(220, 120, 0); + --menulink1: rgb(255,255,255); + --menubg0: rgb(29,32,33); + --text0: rgb(235,219,178); + --text1: rgb(220, 120, 0); +} + +a { + color: var(--link0); + transition: var(--transtime); +} + +a:hover { + color: var(--link1); +} + +body { + background: var(--bg0); +} + +footer { + background: var(--footerbg0); + color: var(--text0); +} + +span { + color: var(--text1); +} + +.container { + color: var(--text0); +} + +.container h1, +.container h2 { + color: var(--text1); +} + +.container .flash { + background-color: var(--error); +} + +.hide-menu:hover, +.main-menu a:hover, +.show-menu:hover { + color: var(--menulink1); +} + +.main-menu a { + color: var(--menulink0); +} + +.main-menu-dropdown { + background: var(--menubg0); + color: var(--menulink0); +} + +@media screen and (max-width:800px) { + + .main-menu { + background: var(--menubg0); + } +} + +.entry { + background: var(--bg0); + border-left: 10px solid var(--color0); + color: var(--text0); +} + +.entry h1, +.entry h2 { + color: var(--text1); +} diff --git a/src/static/css/light.css b/src/static/css/light.css new file mode 100644 index 0000000..ce6379d --- /dev/null +++ b/src/static/css/light.css @@ -0,0 +1,83 @@ +@import 'style.css'; + +:root { + --bg0: rgb(255,255,255); + --color0: rgb(0,0,120); + --error: rgb(255,0,0); + --footerbg0: rgb(192,192,192); + --link0: rgb(0,0,120); + --link1: rgb(255,255,255); + --menulink0: rgb(0,0,120); + --menulink1: rgb(255,255,255); + --menubg0: rgb(192,192,192); + --text0: rgb(0,0,0); + --text1: rgb(0,0,120); +} + +a { + color: var(--link0); + transition: var(--transtime); +} + +a:hover { + color: var(--link1); +} + +body { + background: var(--bg0); +} + +footer { + background: var(--footerbg0); + color: var(--text0); +} + +span { + color: var(--text1); +} + +.container { + color: var(--text0); +} + +.container h1, +.container h2 { + color: var(--text1); +} + +.container .flash { + background-color: var(--error); +} + +.hide-menu:hover, +.main-menu a:hover, +.show-menu:hover { + color: var(--menulink1); +} + +.main-menu a { + color: var(--menulink0); +} + +.main-menu-dropdown { + background: var(--menubg0); + color: var(--menulink0); +} + +@media screen and (max-width:800px) { + + .main-menu { + background: var(--menubg0); + } +} + +.entry { + background: var(--bg0); + border-left: 10px solid var(--color0); + color: var(--text0); +} + +.entry h1, +.entry h2 { + color: var(--text1); +} diff --git a/src/static/css/style.css b/src/static/css/style.css new file mode 100644 index 0000000..1d1a16d --- /dev/null +++ b/src/static/css/style.css @@ -0,0 +1,169 @@ +:root { + --error: rgb(255,0,0); + --transtime: 0.7s; +} + +* { + margin: 0; + padding: 0; +} + +a { + text-decoration: none; + transition: var(--transtime); +} + +a:hover { + cursor: pointer; +} + +body { + margin: 0; +} + +body, +html { + font-family: sans-serif; + height: 100%; + max-width: 100%; + overflow-x: hidden; +} + +footer { + height: 100px; + padding-top: 20px; +} + +footer .center { + text-align: center; +} + +.container { + min-height: 100%; + padding-bottom: 50px; + padding-left: 10%; + padding-right: 10%; + padding-top: 5%; +} + +.container .flash { + padding: 10px; + width: 400px; +} + +.hide-menu, +.show-menu { + cursor: pointer; + display: none; + font-size: 30px; + transition: var(--transtime); +} + +.important { + font-size: xx-large; + padding-left: 25vw; + padding-right: 25vw; + padding-top: 30vh; + text-align: left; +} + +.important span { + font-weight: bold; +} + +.logo { + height: 80px; + padding-top: 10px; +} + +.main-menu-dropdown span { + float: left; + font-family: monospace; + font-size: 30px; + font-weight: bold; + line-height: 100px; + padding: 0 10px; + text-decoration: none; + text-transform: uppercase; + transition: 0.7s; +} + +.main-menu { + float: right; + font-family: monospace; + font-size: 30px; + font-weight: bold; + line-height: 100px; +} + +.main-menu a { + padding: 0 10px; + text-decoration: none; + text-transform: uppercase; + transition: 0.7s; +} + +.main-menu-dropdown { + height: 100px; + padding: 0 20px; +} + +.show-menu { + float: right; + line-height: 100px; +} + +#main-menu-check { + position: absolute; + visibility: hidden; + z-index: -1111; +} + +@media screen and (max-width:800px) { + .hide-menu { + position: absolute; + right: 40px; + top: 40px; + } + + .hide-menu, + .show-menu { + display: block; + } + + .main-menu { + height: 100vh; + line-height: normal; + padding: 80px 0; + position: fixed; + right: -100%; + text-align: center; + top: 0; + transition: var(--transtime); + width: 100%; + } + .main-menu a { + display: block; + padding: 20px; + } + + #main-menu-check:checked ~ .main-menu { + right: 0; + } +} + +.entry { + border-radius: 0 10px 30px 0; + margin-bottom: 20px; + padding: 10px; +} + +.entry h1, +.entry h2 { + margin: 5px auto 2px auto; +} + +.entry ul { + padding-left: 20; +} + diff --git a/src/static/images/logo.png b/src/static/images/logo.png new file mode 100644 index 0000000..e69de29 diff --git a/src/templates/archive.html b/src/templates/archive.html new file mode 100644 index 0000000..4b9de72 --- /dev/null +++ b/src/templates/archive.html @@ -0,0 +1,11 @@ +{% extends 'template.html' %} +{% block content %} +
+
+

Archive


+ {% autoescape off %} + {{ content_string }} + {% endautoescape %} +
+
+{% endblock %} diff --git a/src/templates/error.html b/src/templates/error.html new file mode 100644 index 0000000..84571ce --- /dev/null +++ b/src/templates/error.html @@ -0,0 +1,9 @@ +{% extends "template.html" %} +{% block content %} +
+
+ Error
+ {{ errorcode }} +
+
+{% endblock %} diff --git a/src/templates/index.html b/src/templates/index.html new file mode 100644 index 0000000..db050ab --- /dev/null +++ b/src/templates/index.html @@ -0,0 +1,11 @@ +{% extends "template.html" %} +{% block content %} +
+
+

Index


+ {% autoescape off %} + {{ content_string }} + {% endautoescape %} +
+
+{% endblock %} diff --git a/src/templates/rss.xml b/src/templates/rss.xml new file mode 100644 index 0000000..9f8b6b1 --- /dev/null +++ b/src/templates/rss.xml @@ -0,0 +1,16 @@ + + + + + {{ title }} + {{ description }} +en-us +{{ website }}/feed.xml + + +{% autoescape off %} +{{ content_string }} +{% endautoescape %} + + + diff --git a/src/templates/standalone.html b/src/templates/standalone.html new file mode 100644 index 0000000..f80f5f8 --- /dev/null +++ b/src/templates/standalone.html @@ -0,0 +1,10 @@ +{% extends 'template.html' %} +{% block content %} +
+
+ {% autoescape off %} + {{ content_string }} + {% endautoescape %} +
+
+{% endblock %} diff --git a/src/templates/template.html b/src/templates/template.html new file mode 100644 index 0000000..c7cae8e --- /dev/null +++ b/src/templates/template.html @@ -0,0 +1,32 @@ + + + {{ title }} + + + + + + + + + + {% block content %} + {% endblock %} + + + +