@ -0,0 +1,68 @@ |
|||||
|
# Python Flask Wiki |
||||
|
|
||||
|
This is a simple wiki based on Pythons Flask framework. |
||||
|
There is much great wiki software. |
||||
|
Most of them are using some kind of database. |
||||
|
I however just want to put my markdown files in a directory and get a working wiki. |
||||
|
|
||||
|
## Features/To-Dos |
||||
|
|
||||
|
- [ ] Plain text support for blog entries |
||||
|
- [ ] Markdown Files (.md) |
||||
|
- [ ] Entry page |
||||
|
- [ ] Navigation |
||||
|
- [ ] Header |
||||
|
- [ ] Footer |
||||
|
- [ ] Switchable CSS |
||||
|
- [ ] CSS dark-theme |
||||
|
- [ ] CSS light-theme |
||||
|
- [ ] Config file |
||||
|
- [ ] Docker installation |
||||
|
- [ ] Enable variables/environment variables |
||||
|
- [ ] Logo |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
### Create entries |
||||
|
|
||||
|
Wiki entries are managed by plain markdown files in the `templates/entry/` directory. |
||||
|
The first line of each document is reserved as the title of the document. |
||||
|
|
||||
|
## Deployment |
||||
|
|
||||
|
### PIP/Python |
||||
|
|
||||
|
- `git clone https://github.com/tiyn/tiyny-blog` |
||||
|
- `cd flaskblog/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 |
||||
|
|
||||
|
`docker run --name wiki --restart unless-stopped -v ./config.py:/wiki/src/config.py -v entries:/wiki/src/templates/entry -p 80:5000 -d tiynger/tiyny-wiki` |
||||
@ -0,0 +1,21 @@ |
|||||
|
FROM ubuntu |
||||
|
|
||||
|
MAINTAINER Tiyn tiyn@martenkante.eu |
||||
|
|
||||
|
RUN apt-get update |
||||
|
|
||||
|
RUN apt-get install python3 python3-pip git -y |
||||
|
|
||||
|
RUN git clone https://github.com/tiyn/tiyny-wiki /wiki |
||||
|
|
||||
|
WORKDIR /wiki/src |
||||
|
|
||||
|
RUN pip3 install -r requirements.txt |
||||
|
|
||||
|
VOLUME /wiki/src/templates/entry |
||||
|
|
||||
|
EXPOSE 5000 |
||||
|
|
||||
|
ENTRYPOINT [ "python3" ] |
||||
|
|
||||
|
CMD [ "app.py" ] |
||||
@ -0,0 +1,22 @@ |
|||||
|
from flask import Flask, flash, make_response, render_template, request, redirect, abort |
||||
|
|
||||
|
import content as con_gen |
||||
|
import config |
||||
|
|
||||
|
|
||||
|
app = Flask(__name__) |
||||
|
|
||||
|
|
||||
|
@app.errorhandler(404) |
||||
|
def page_not_found(e): |
||||
|
return render_template('error.html', title=config.TITLE, errorcode='404', style=config.STYLE), 404 |
||||
|
|
||||
|
|
||||
|
@app.route('/') |
||||
|
@app.route('/index.html') |
||||
|
def index(): |
||||
|
return 'ok' |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
app.run(host='0.0.0.0') |
||||
@ -0,0 +1,8 @@ |
|||||
|
# Name/title of your blog |
||||
|
TITLE = 'Tiyny-Wiki' |
||||
|
|
||||
|
# URL for your website: e.g. https://domain.tld |
||||
|
WEBSITE = 'localhost:5000' |
||||
|
|
||||
|
# Theme for the blog: dark, light |
||||
|
STYLE = 'dark' |
||||
@ -0,0 +1,10 @@ |
|||||
|
import datetime |
||||
|
from datetime import datetime |
||||
|
import markdown |
||||
|
import os |
||||
|
from os import path |
||||
|
import pathlib |
||||
|
|
||||
|
import config |
||||
|
|
||||
|
ENTRY_DIR = 'templates/entry' |
||||
@ -0,0 +1,2 @@ |
|||||
|
Flask==1.1.2 |
||||
|
Markdown==3.1.1 |
||||
@ -0,0 +1,72 @@ |
|||||
|
@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); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
@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); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,153 @@ |
|||||
|
: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; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
Test Entry Title 3 |
||||
|
This is a markdown file |
||||
|
|
||||
|
- list entry |
||||
|
- list entry |
||||
|
- list entry |
||||
|
|
||||
|
# md-header |
||||
|
|
||||
|
more content |
||||
@ -0,0 +1,10 @@ |
|||||
|
Test Entry Title 3 |
||||
|
This is a markdown file |
||||
|
|
||||
|
- list entry |
||||
|
- list entry |
||||
|
- list entry |
||||
|
|
||||
|
# md-header |
||||
|
|
||||
|
more content |
||||
@ -0,0 +1,10 @@ |
|||||
|
Test Entry Title 3 |
||||
|
This is a markdown file |
||||
|
|
||||
|
- list entry |
||||
|
- list entry |
||||
|
- list entry |
||||
|
|
||||
|
# md-header |
||||
|
|
||||
|
more content |
||||
@ -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,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">☰</label> |
||||
|
<div class="main-menu"> |
||||
|
<a href="/">Startpage</a> |
||||
|
<label for="main-menu-check" class="hide-menu">X</label> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- Menu --> |
||||
|
<!-- Content --> |
||||
|
{% block content %} |
||||
|
{% endblock %} |
||||
|
<!-- Content --> |
||||
|
<footer> |
||||
|
<div class="center"> |
||||
|
Dieser Blog enthält kein Javascript oder PHP.<br> |
||||
|
Dies ist eine Instanz vom <a href="https://github.com/tiyn/tiyny-wiki">Tiyny-Wiki</a>. |
||||
|
</div> |
||||
|
</footer> |
||||
|
</body> |
||||
|
</html> |
||||