first commit

master
tiyn 2 years ago
parent 1463dca012
commit a457f355d1

@ -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" ]

@ -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`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -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

1
src/.gitignore vendored

@ -0,0 +1 @@
__pycache__/

@ -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/<path>')
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')

@ -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'

@ -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 ""

@ -0,0 +1,4 @@
Flask==2.1.2
flask_login==0.6.2
flask_wtf==1.0.1
WTForms==3.0.1

@ -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);
}

@ -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);
}

@ -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;
}

@ -0,0 +1,11 @@
{% extends 'template.html' %}
{% block content %}
<div class="container">
<div class="blogarchive">
<h1>Archive</h1><br>
{% autoescape off %}
{{ content_string }}
{% endautoescape %}
</div>
</div>
{% endblock %}

@ -0,0 +1,9 @@
{% extends "template.html" %}
{% block content %}
<div class="container">
<div class="important">
Error<br>
<span>{{ errorcode }}</span>
</div>
</div>
{% endblock %}

@ -0,0 +1,11 @@
{% extends "template.html" %}
{% block content %}
<div class="container">
<div class="blog">
<h1>Index</h1><br>
{% autoescape off %}
{{ content_string }}
{% endautoescape %}
</div>
</div>
{% endblock %}

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ title }}</title>
<description>{{ description }}</description>
<language>en-us</language>
<link>{{ website }}/feed.xml</link>
<atom:link href="/feed.xml" rel="self" type="application/rss+xml" />
{% autoescape off %}
{{ content_string }}
{% endautoescape %}
</channel>
</rss>

@ -0,0 +1,10 @@
{% extends 'template.html' %}
{% block content %}
<div class="container">
<div class="standalone">
{% autoescape off %}
{{ content_string }}
{% endautoescape %}
</div>
</div>
{% endblock %}

@ -0,0 +1,32 @@
<html>
<head>
<title>{{ title }}</title>
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width" initial-scale=1.0>
</head>
<body>
<!-- Menu -->
<div class="main-menu-dropdown">
<!-- <img class="logo" src="/static/images/logo.png"> -->
<span>{{ title }}</span>
<input type="checkbox" id="main-menu-check">
<label for="main-menu-check" class="show-menu">&#9776;</label>
<div class="main-menu">
<a href="/">Blog</a>
<a href="/archive">Archive</a>
<label for="main-menu-check" class="hide-menu">X</label>
</div>
</div>
<!-- Menu -->
<!-- Content -->
{% block content %}
{% endblock %}
<!-- Content -->
<footer>
<div class="center">
Made with <a href="https://github.com/tiyn/container-critique">Container Critique </a>.
</div>
</footer>
</body>
</html>
Loading…
Cancel
Save