move Git repository kontor-docker to directory bottle-docker
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
@@ -0,0 +1,2 @@
|
||||
# kontor-bottle
|
||||
Kontor with Python Bottle Framework
|
||||
@@ -0,0 +1,19 @@
|
||||
# set base image (host OS)
|
||||
FROM python:3.8
|
||||
|
||||
# set the working directory in the container
|
||||
WORKDIR /code
|
||||
|
||||
# copy the dependencies file to the working directory
|
||||
COPY requirements.txt /code/
|
||||
|
||||
# copy the content of the local src directory to the working directory
|
||||
COPY src/ /code/
|
||||
|
||||
# install dependencies
|
||||
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
# command to run on container start
|
||||
CMD [ "python", "./kontor.py" ]
|
||||
@@ -0,0 +1,2 @@
|
||||
pymongo
|
||||
bottle
|
||||
@@ -0,0 +1,193 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import pymongo
|
||||
import comics.publisherDAO
|
||||
import comics.artistDAO
|
||||
import comics.comicDAO
|
||||
import bottle
|
||||
import cgi
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
def __init__(self, app, database, sessions):
|
||||
self.app = app
|
||||
self.db = database
|
||||
self.sessions = sessions
|
||||
self.publishers = publisherDAO.PublisherDAO(database)
|
||||
self.artists = artistDAO.ArtistDAO(database)
|
||||
self.comics = comicDAO.ComicDAO(database)
|
||||
self.routing()
|
||||
|
||||
|
||||
def routing(self):
|
||||
self.app.route('/comics/', 'GET', self.comic_index)
|
||||
self.app.route('/comics/comic', 'GET', self.comic_list)
|
||||
self.app.route('/comics/comic/<id>', 'GET', self.comic_details)
|
||||
self.app.route('/comics/comic/create', 'GET', self.get_comic_create)
|
||||
self.app.route('/comics/comic/create', 'POST', self.post_create_comic)
|
||||
self.app.route('/comics/publisher', 'GET', self.publisher_list)
|
||||
self.app.route('/comics/publisher/<id>', 'GET', self.publisher_details)
|
||||
self.app.route('/comics/publisher/create', 'GET', self.get_publisher_create)
|
||||
self.app.route('/comics/publisher/create', 'POST', self.post_create_publisher)
|
||||
self.app.route('/comics/artist', 'GET', self.artist_list)
|
||||
self.app.route('/comics/artist/<id>', 'GET', self.artist_details)
|
||||
self.app.route('/comics/artist/create', 'GET', self.get_artist_create)
|
||||
self.app.route('/comics/artist/create', 'POST', self.post_create_artist)
|
||||
self.app.route('/comics/storyarc', 'GET', self.storyarc_list)
|
||||
self.app.route('/comics/storyarc/<id>', 'GET', self.storyarc_details)
|
||||
self.app.route('/comics/storyarc/create', 'GET', self.get_storyarc_create)
|
||||
self.app.route('/comics/storyarc/create', 'POST', self.post_create_storyarc)
|
||||
|
||||
def comic_index(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template('comic_index', dict(username=username))
|
||||
|
||||
|
||||
def comic_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.comics.get_comics()
|
||||
return bottle.template('comic_list', dict(comics=l, username=username))
|
||||
|
||||
|
||||
def comic_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
comic = self.comics.get_comic(id)
|
||||
errors = ""
|
||||
if comic == None:
|
||||
errors = "Entry not found"
|
||||
return bottle.template('comic_template', dict(title=comic['title'],
|
||||
id=comic['_id'],
|
||||
current_order=comic['current_order'],
|
||||
completed=comic['completed'],
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
|
||||
def get_comic_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("comic_template", dict(title="",
|
||||
id='newentry',
|
||||
current_order=False,
|
||||
completed=False,
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
|
||||
def post_create_comic(self):
|
||||
comic_id = bottle.request.forms.get("id")
|
||||
comic_title = bottle.request.forms.get("title")
|
||||
comic_order = bottle.request.forms.get("current_order")
|
||||
comic_completed = bottle.request.forms.get("completed")
|
||||
if comic_id == "newentry":
|
||||
self.comics.insert_entry(comic_title, None, comic_order, comic_completed)
|
||||
else:
|
||||
self.comics.update_entry(comic_id, comic_title, None, comic_order, comic_completed)
|
||||
bottle.redirect("/comics/comic")
|
||||
|
||||
|
||||
def publisher_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.publishers.get_publishers()
|
||||
return bottle.template('publisher_list', dict(publishers=l, username=username))
|
||||
|
||||
|
||||
def publisher_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
publisher = self.publishers.get_publisher(id)
|
||||
errors = ""
|
||||
if publisher == None:
|
||||
errors= "Entry not found"
|
||||
return bottle.template('publisher_template', dict(name=publisher['name'], id=publisher['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_publisher_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("publisher_template", dict(name="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_publisher(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
publisher_id = bottle.request.forms.get("id")
|
||||
publisher_name = bottle.request.forms.get("name")
|
||||
if publisher_id == "newentry":
|
||||
self.publishers.insert_entry(publisher_name)
|
||||
else:
|
||||
self.publishers.update_entry(publisher_id, publisher_name)
|
||||
bottle.redirect("/comics/publisher")
|
||||
|
||||
|
||||
def artist_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.artists.get_artists()
|
||||
return bottle.template('artist_list', dict(artists=l, username=username))
|
||||
|
||||
|
||||
def artist_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
artist = self.artists.get_artist(id)
|
||||
errors = ""
|
||||
if artist == None:
|
||||
errors= "Entry not found"
|
||||
return bottle.template('artist_template', dict(name=artist['name'], id=artist['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_artist_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("artist_template", dict(name="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_artist(self):
|
||||
artist_id = bottle.request.forms.get("id")
|
||||
artist_name = bottle.request.forms.get("name")
|
||||
if artist_id == "newentry":
|
||||
self.artists.insert_entry(artist_name)
|
||||
else:
|
||||
self.artists.update_entry(artist_id, artist_name)
|
||||
bottle.redirect("/comics/artist")
|
||||
|
||||
|
||||
def storyarc_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.storyarcs.get_storyarcs()
|
||||
return bottle.template('storyarc_list', dict(storyarcs=l, username=username))
|
||||
|
||||
|
||||
def storyarc_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
storyarc = self.storyarcs.get_storyarc(id)
|
||||
errors = ""
|
||||
if storyarc == None:
|
||||
errors = "Entry not found"
|
||||
return bottle.template('storyarc_template', dict(title=storyarc['title'], id=storyarc['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_storyarc_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("storyarc_template", dict(title="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_storyarc(self):
|
||||
storyarc_id = bottle.request.forms.get("id")
|
||||
storyarc_title = bottle.request.forms.get("title")
|
||||
if storyarc_id == "newentry":
|
||||
self.storyarcs.insert_entry(storyarc_title)
|
||||
else:
|
||||
self.storyarcs.update_entry(storyarc_id, storyarc_title)
|
||||
bottle.redirect("/comics/storyarc")
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class ArtistDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.artists = database.artists
|
||||
|
||||
def get_artists(self):
|
||||
cursor = self.artists.find()
|
||||
l = []
|
||||
for artist in cursor:
|
||||
l.append({'name':artist['name'], '_id':artist['_id']})
|
||||
return l
|
||||
|
||||
def get_artist(self, artist_id):
|
||||
artist = self.artists.find_one({"_id": ObjectId(artist_id)})
|
||||
return artist
|
||||
|
||||
def insert_entry(self, artist_name):
|
||||
print("inserting artist entry", artist_name)
|
||||
|
||||
artist = {"name": artist_name}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.artists.insert_one(artist)
|
||||
print("Matching artist: ", result.matched_count)
|
||||
print("Modified artist: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting artist")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, artist_id, artist_name):
|
||||
print("upserting artist entry", artist_name)
|
||||
|
||||
filter_doc = {"_id": ObjectId(artist_id)}
|
||||
artist = { "$set": {"name": artist_name}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.artists.update_one(filter_doc, artist)
|
||||
print("Modified artist: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting artist")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class ComicDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.comics = database.comics
|
||||
|
||||
def get_comics(self):
|
||||
cursor = self.comics.find()
|
||||
l = []
|
||||
for comic in cursor:
|
||||
l.append({'title':comic['title'],
|
||||
'_id':comic['_id'],
|
||||
'current_order':comic['current_order'],
|
||||
'completed':comic['completed']})
|
||||
return l
|
||||
|
||||
def get_comic(self, comic_id):
|
||||
comic = self.comics.find_one({"_id": ObjectId(comic_id)})
|
||||
return comic
|
||||
|
||||
def insert_entry(self, comic_title, comic_publisher=None, comic_order=False, comic_completed=False):
|
||||
print("inserting comic entry", comic_title)
|
||||
|
||||
comic = {"title": comic_title, "current_order": comic_order, "completed": comic_completed}
|
||||
|
||||
# now insert the comic
|
||||
try:
|
||||
result = self.comics.insert_one(comic)
|
||||
print("Modified comic: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting comic")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, comic_id, comic_title, comic_publisher=None, comic_order=False, comic_completed=False):
|
||||
print("upserting comic entry", comic_title)
|
||||
|
||||
filter_doc = {"_id": ObjectId(comic_id)}
|
||||
comic = { "$set": {"title": comic_title, 'current_order': comic_order, 'completed': comic_completed}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.comics.update_one(filter_doc, comic)
|
||||
print("Matching comic: ", result.matched_count)
|
||||
print("Modified comic: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting comic")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from mongoengine import connect, Document
|
||||
from mongoengine import StringField, BooleanField
|
||||
from mongoengine import ListField, ReferenceField, GenericReferenceField
|
||||
import pprint
|
||||
|
||||
|
||||
class Publisher(Document):
|
||||
name = StringField(required=True)
|
||||
|
||||
def __str__(self):
|
||||
s = "Publisher(%s)" % self.name
|
||||
return s
|
||||
|
||||
|
||||
class Artist(Document):
|
||||
name = StringField(required=True)
|
||||
className = StringField()
|
||||
|
||||
def __str__(self):
|
||||
s = "Artist(%s)" % self.name
|
||||
return s
|
||||
|
||||
|
||||
class Issue(Document):
|
||||
number = StringField()
|
||||
comic = GenericReferenceField()
|
||||
is_read = BooleanField(default=False)
|
||||
is_stock = BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
s = "Issue(%s # %s, %s)" % (self.comic.title, self.number, self.is_read)
|
||||
return s
|
||||
|
||||
|
||||
class StoryArc(Document):
|
||||
name = StringField(required=True)
|
||||
comic = GenericReferenceField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
|
||||
def __str__(self):
|
||||
s = "StoryArc(%s, %s)" % (self.name, self.comic.title)
|
||||
return s
|
||||
|
||||
|
||||
class Volume(Document):
|
||||
name = StringField(required=True)
|
||||
comic = GenericReferenceField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
|
||||
def __str__(self):
|
||||
s = "Volume(%s, %s, %s)" % (self.id, self.name, self.comic.title)
|
||||
return s
|
||||
|
||||
|
||||
class Comic(Document):
|
||||
title = StringField(required=True)
|
||||
publisher = ReferenceField(Publisher)
|
||||
current_order = BooleanField()
|
||||
completed = BooleanField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
stories = ListField(ReferenceField(StoryArc))
|
||||
|
||||
def __str__(self):
|
||||
if self.publisher is None:
|
||||
s = "Comic(%s, %s, %s, %s)" % (self.title, self.publisher, self.current_order, self.completed)
|
||||
return s
|
||||
else:
|
||||
s = "Comic(%s, %s, %s, %s)" % (
|
||||
self.title, self.publisher.name, self.current_order, self.completed)
|
||||
return s
|
||||
|
||||
|
||||
class TradePaperback(Document):
|
||||
comic = ReferenceField(Comic)
|
||||
issue_start = StringField()
|
||||
issue_end = StringField()
|
||||
|
||||
def __str__(self):
|
||||
s = "TPB(%s)" % self.comic.title
|
||||
return s
|
||||
|
||||
|
||||
def get_publisher(name):
|
||||
publisher = Publisher.objects(name=name)
|
||||
if publisher.count() > 0:
|
||||
return publisher[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_comic(title):
|
||||
comic = Comic.objects(title=title)
|
||||
if comic.count() > 0:
|
||||
return comic[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_issue(title, number):
|
||||
comic = get_comic(title)
|
||||
issues = Issue.objects(number=number, comic=comic)
|
||||
if issues.count() > 0:
|
||||
return issues[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
connect('comics')
|
||||
for publisher in Publisher.objects:
|
||||
pprint.pprint(publisher)
|
||||
for artist in Artist.objects:
|
||||
pprint.pprint(artist)
|
||||
for comic in Comic.objects:
|
||||
pprint.pprint(comic)
|
||||
for issue in Issue.objects:
|
||||
pprint.pprint(issue)
|
||||
for story in StoryArc.objects:
|
||||
pprint.pprint(story)
|
||||
for tpb in TradePaperback.objects:
|
||||
pprint.pprint(tpb)
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class PublisherDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.publishers = database.publishers
|
||||
|
||||
def get_publishers(self):
|
||||
cursor = self.publishers.find()
|
||||
l = []
|
||||
for publisher in cursor:
|
||||
l.append({'name':publisher['name'], '_id':publisher['_id']})
|
||||
return l
|
||||
|
||||
def get_publisher(self, publisher_id):
|
||||
publisher = self.publishers.find_one({"_id": ObjectId(publisher_id)})
|
||||
return publisher
|
||||
|
||||
def insert_entry(self, publisher_name):
|
||||
print("inserting publisher entry", publisher_name)
|
||||
|
||||
publisher = {"name": publisher_name}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.publishers.insert_one(publisher)
|
||||
print("Matching publisher: ", result.matched_count)
|
||||
print("Modified publisher: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting publisher")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, publisher_id, publisher_name):
|
||||
print("upserting publisher entry", publisher_name)
|
||||
|
||||
filter_doc = {"_id": ObjectId(publisher_id)}
|
||||
publisher = { "$set": {"name": publisher_name}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.publishers.update_one(filter_doc, publisher)
|
||||
print("Modified publisher: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting publisher")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
|
||||
class StoryArcDAO:
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.storyarcs = database.storyarcs
|
||||
|
||||
def get_storyarcs(self):
|
||||
cursor = self.storyarcs.find()
|
||||
l = []
|
||||
for storyarc in cursor:
|
||||
l.append({'title': storyarc['title'], '_id': storyarc['_id']})
|
||||
return l
|
||||
|
||||
def get_storyarc(self, storyarc_id):
|
||||
storyarc = self.storyarcs.find_one({"_id": ObjectId(storyarc_id)})
|
||||
return storyarc
|
||||
|
||||
def insert_entry(self, storyarc_title):
|
||||
print("inserting publisher entry", storyarc_title)
|
||||
|
||||
storyarc = {"name": storyarc_title}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.storyarcs.insert_one(storyarc)
|
||||
print("Matching storyarc: ", result.matched_count)
|
||||
print("Modified storyarc: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting storyarc")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, storyarc_id, storyarc_title):
|
||||
print("upserting storyarc entry", storyarc_title)
|
||||
|
||||
filter_doc = {"_id": ObjectId(storyarc_id)}
|
||||
storyarc = {"$set": {"name": storyarc_title}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.storyarcs.update_one(filter_doc, storyarc)
|
||||
print("Modified storyarc: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting storyarc")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,89 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
color: #333333;
|
||||
padding:4em 0 4em;
|
||||
}
|
||||
body,
|
||||
.wrapper {
|
||||
margin: 10px auto;
|
||||
/*max-width: 60em;*/
|
||||
}
|
||||
|
||||
header, nav, nav a, main, section, footer {
|
||||
border-radius: 0px 0.5em 0.5em;
|
||||
border: 1px solid;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
header {
|
||||
position:fixed;
|
||||
top:0px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
text-align:center;
|
||||
padding:10px;
|
||||
background: lightgrey;
|
||||
/*border-bottom: 1px solid #d5d5d5;*/
|
||||
}
|
||||
|
||||
nav {
|
||||
position: fixed;
|
||||
padding-top: 10em;
|
||||
|
||||
font-size: 0.91em;
|
||||
float: left;
|
||||
width: 15em;
|
||||
padding: 0;
|
||||
background: lightskyblue;
|
||||
border-color: skyblue;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: block;
|
||||
padding: 0.2em 10px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
background-color: skyblue;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
nav ul a:hover,
|
||||
nav ul a:active {
|
||||
color: #fffbf0;
|
||||
background-color: #dfac20;
|
||||
}
|
||||
|
||||
main {
|
||||
display: block;
|
||||
background: lightblue;
|
||||
border-color: #8a9da8;
|
||||
margin-left: 15em;
|
||||
min-width: 16em; /* Mindestbreite (der Überschrift) verhindert Anzeigefehler in modernen Browsern */
|
||||
}
|
||||
|
||||
footer {
|
||||
position:fixed;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
bottom:0;
|
||||
left: 0;
|
||||
right:0;
|
||||
background: lightgrey;
|
||||
border-color: grey;
|
||||
}
|
||||
|
||||
footer p {
|
||||
float:right;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymongo
|
||||
import sessionDAO
|
||||
import userDAO
|
||||
import comics
|
||||
#import library
|
||||
import bottle
|
||||
import cgi
|
||||
import re
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
app = bottle.Bottle()
|
||||
|
||||
|
||||
def index():
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = sessions.get_username(cookie)
|
||||
return bottle.template('kontor', dict(username=username))
|
||||
|
||||
|
||||
def show_signup():
|
||||
return bottle.template("signup", dict(username="",
|
||||
password="",
|
||||
password_error="",
|
||||
email="",
|
||||
username_error="",
|
||||
email_error="",
|
||||
verify_error =""))
|
||||
|
||||
|
||||
def process_signup():
|
||||
email = bottle.request.forms.get("email")
|
||||
username = bottle.request.forms.get("username")
|
||||
password = bottle.request.forms.get("password")
|
||||
verify = bottle.request.forms.get("verify")
|
||||
|
||||
# set these up in case we have an error case
|
||||
errors = {'username': cgi.escape(username), 'email': cgi.escape(email)}
|
||||
if validate_signup(username, password, verify, email, errors):
|
||||
|
||||
if not users.add_user(username, password, email):
|
||||
# this was a duplicate
|
||||
errors['username_error'] = "Username already in use. Please choose another"
|
||||
return bottle.template("signup", errors)
|
||||
|
||||
session_id = sessions.start_session(username)
|
||||
print(session_id)
|
||||
bottle.response.set_cookie("session", session_id)
|
||||
bottle.redirect("/welcome")
|
||||
else:
|
||||
print("user did not validate")
|
||||
return bottle.template("signup", errors)
|
||||
|
||||
|
||||
def show_login():
|
||||
return bottle.template('login', dict(username="", password="", login_error=""))
|
||||
|
||||
def process_login():
|
||||
username = bottle.request.forms.get("username")
|
||||
password = bottle.request.forms.get("password")
|
||||
|
||||
print("user submitted ", username, "pass ", password)
|
||||
|
||||
user_record = users.validate_login(username, password)
|
||||
if user_record:
|
||||
# username is stored in the user collection in the _id key
|
||||
session_id = sessions.start_session(user_record['_id'])
|
||||
|
||||
if session_id is None:
|
||||
bottle.redirect("/internal_error")
|
||||
|
||||
cookie = session_id
|
||||
|
||||
# Warning, if you are running into a problem whereby the cookie being set here is
|
||||
# not getting set on the redirect, you are probably using the experimental version of bottle (.12).
|
||||
# revert to .11 to solve the problem.
|
||||
bottle.response.set_cookie("session", cookie)
|
||||
|
||||
bottle.redirect("/")
|
||||
|
||||
else:
|
||||
return bottle.template("login", dict(username=cgi.escape(username), password="", login_error="Invalid Login"))
|
||||
|
||||
|
||||
def process_logout():
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
sessions.end_session(cookie)
|
||||
bottle.response.set_cookie("session", "")
|
||||
bottle.redirect("/")
|
||||
|
||||
|
||||
def send_stylesheet(filename):
|
||||
return bottle.static_file(filename, root='.', mimetype='text/css')
|
||||
|
||||
|
||||
def setup_routing(app):
|
||||
app.route('/', 'GET', index)
|
||||
app.route('/signup', 'GET', show_signup)
|
||||
app.route('/signup', 'POST', process_signup)
|
||||
app.route('/login', 'GET', show_login)
|
||||
app.route('/login', 'POST', process_login)
|
||||
app.route('/logout', 'GET', process_logout)
|
||||
app.route('/css/<filename:re:.*\.css>', 'GET', send_stylesheet)
|
||||
|
||||
|
||||
# validates that the user information is valid for new signup, return True of False
|
||||
# and fills in the error string if there is an issue
|
||||
def validate_signup(username, password, verify, email, errors):
|
||||
USER_RE = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
|
||||
PASS_RE = re.compile(r"^.{3,20}$")
|
||||
EMAIL_RE = re.compile(r"^[\S]+@[\S]+\.[\S]+$")
|
||||
|
||||
errors['username_error'] = ""
|
||||
errors['password_error'] = ""
|
||||
errors['verify_error'] = ""
|
||||
errors['email_error'] = ""
|
||||
|
||||
if not USER_RE.match(username):
|
||||
errors['username_error'] = "invalid username. try just letters and numbers"
|
||||
return False
|
||||
|
||||
if not PASS_RE.match(password):
|
||||
errors['password_error'] = "invalid password."
|
||||
return False
|
||||
if password != verify:
|
||||
errors['verify_error'] = "password must match"
|
||||
return False
|
||||
if email != "":
|
||||
if not EMAIL_RE.match(email):
|
||||
errors['email_error'] = "invalid email address"
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
setup_routing(app)
|
||||
|
||||
connection = pymongo.MongoClient("mongodb://mongodb")
|
||||
database = connection.kontor
|
||||
|
||||
users = userDAO.UserDAO(database)
|
||||
sessions = sessionDAO.SessionDAO(database)
|
||||
|
||||
comics_plugin = comics.Plugin(app, database, sessions)
|
||||
#library_plugin = library.Plugin(app, database, sessions)
|
||||
print("starting app Kontor")
|
||||
bottle.run(app, host="0.0.0.0", port=9000, debug=True)
|
||||
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
# The session Data Access Object handles interactions with the sessions collection
|
||||
|
||||
class SessionDAO:
|
||||
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.sessions = database.sessions
|
||||
|
||||
# will start a new session id by adding a new document to the sessions collection
|
||||
# returns the sessionID or None
|
||||
def start_session(self, username):
|
||||
|
||||
session_id = self.get_random_str(32)
|
||||
session = {'username': username, '_id': session_id}
|
||||
|
||||
try:
|
||||
self.sessions.insert_one(session)
|
||||
except:
|
||||
print("Unexpected error on start_session:", sys.exc_info()[0])
|
||||
return None
|
||||
|
||||
return str(session['_id'])
|
||||
|
||||
# will send a new user session by deleting from sessions table
|
||||
def end_session(self, session_id):
|
||||
|
||||
if session_id is None:
|
||||
return
|
||||
|
||||
self.sessions.delete_one({'_id': session_id})
|
||||
|
||||
return
|
||||
|
||||
# if there is a valid session, it is returned
|
||||
def get_session(self, session_id):
|
||||
|
||||
if session_id is None:
|
||||
return None
|
||||
|
||||
session = self.sessions.find_one({'_id': session_id})
|
||||
|
||||
return session
|
||||
|
||||
# get the username of the current session, or None if the session is not valid
|
||||
def get_username(self, session_id):
|
||||
|
||||
session = self.get_session(session_id)
|
||||
if session is None:
|
||||
return None
|
||||
else:
|
||||
return session['username']
|
||||
|
||||
def get_random_str(self, num_chars):
|
||||
random_string = ""
|
||||
for i in range(num_chars):
|
||||
random_string = random_string + random.choice(string.ascii_letters)
|
||||
return random_string
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import hmac
|
||||
import random
|
||||
import string
|
||||
import hashlib
|
||||
import pymongo
|
||||
|
||||
|
||||
# The User Data Access Object handles all interactions with the User collection.
|
||||
class UserDAO:
|
||||
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
self.users = self.db.users
|
||||
self.SECRET = 'verysecret'
|
||||
|
||||
# makes a little salt
|
||||
def make_salt(self):
|
||||
salt = ""
|
||||
for i in range(5):
|
||||
salt = salt + random.choice(string.ascii_letters)
|
||||
return salt
|
||||
|
||||
# implement the function make_pw_hash(name, pw) that returns a hashed password
|
||||
# of the format:
|
||||
# HASH(pw + salt),salt
|
||||
# use sha256
|
||||
|
||||
def make_pw_hash(self, pw,salt=None):
|
||||
if salt == None:
|
||||
salt = self.make_salt();
|
||||
return hashlib.sha256(pw + salt).hexdigest()+","+ salt
|
||||
|
||||
# Validates a user login. Returns user record or None
|
||||
def validate_login(self, username, password):
|
||||
|
||||
user = None
|
||||
try:
|
||||
user = self.users.find_one({'_id': username})
|
||||
except:
|
||||
print("Unable to query database for user")
|
||||
|
||||
if user is None:
|
||||
print("User not in database")
|
||||
return None
|
||||
|
||||
salt = user['password'].split(',')[1]
|
||||
|
||||
if user['password'] != self.make_pw_hash(password, salt):
|
||||
print("user password is not a match")
|
||||
return None
|
||||
|
||||
# Looks good
|
||||
return user
|
||||
|
||||
|
||||
# creates a new user in the users collection
|
||||
def add_user(self, username, password, email):
|
||||
password_hash = self.make_pw_hash(password)
|
||||
|
||||
user = {'_id': username, 'password': password_hash}
|
||||
if email != "":
|
||||
user['email'] = email
|
||||
|
||||
try:
|
||||
self.users.insert_one(user)
|
||||
except pymongo.errors.OperationFailure:
|
||||
print("oops, mongo error")
|
||||
return False
|
||||
except pymongo.errors.DuplicateKeyError as e:
|
||||
print("oops, username is already taken")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for artist in artists:
|
||||
<li><a href="/comics/artist/{{artist['_id']}}">{{artist['name']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/artist/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="name" size="60" value="{{name}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<table border="1">
|
||||
<tr><td>Title</td><td>Current Order</td><td>Completed</td></tr>
|
||||
<tr>
|
||||
%for comic in comics:
|
||||
<td><a href="/comics/comic/{{comic['_id']}}">{{comic['title']}}</a></td>
|
||||
<td>{{comic['current_order']}}</td>
|
||||
<td>{{comic['completed']}}</td>
|
||||
%end
|
||||
</tr>
|
||||
</table>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/comic/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="title" size="60" value="{{title}}"><br>
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="current_order" size="60" value="{{current_order}}">
|
||||
Current Order
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" name="completed" size="60" value="{{completed}}">
|
||||
Completed
|
||||
</label>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
</details>
|
||||
</main>
|
||||
%if (username == None):
|
||||
<footer><a href="/login">Login</a><p>Ingenieurbüro Thomas Peetz</p></footer>
|
||||
%end
|
||||
%if (username != None):
|
||||
<footer><a href="/logout">{{username}}</a><p>Ingenieurbüro Thomas Peetz</p></footer>
|
||||
%end
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="label">Username</td>
|
||||
<td><input type="text" name="username" value="{{username}}"></td>
|
||||
<td class="error"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="label">Password</td>
|
||||
<td><input type="password" name="password" value=""></td>
|
||||
<td class="error">{{login_error}}</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for publisher in publishers:
|
||||
<li><a href="/comics/publisher/{{publisher['_id']}}">{{publisher['name']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics/comic">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/publisher/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="name" size="60" value="{{name}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<h2>Signup</h2>
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td class="label">Username</td>
|
||||
<td><input type="text" name="username" value="{{username}}"></td>
|
||||
<td class="error">{{username_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Password</td>
|
||||
<td><input type="password" name="password" value=""></td>
|
||||
<td class="error">{{password_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Verify Password</td>
|
||||
<td><input type="password" name="verify" value=""></td>
|
||||
<td class="error">{{verify_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Email (optional)</td>
|
||||
<td><input type="text" name="email" value="{{email}}"></td>
|
||||
<td class="error">{{email_error}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics/comic">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
<li><a href="/comics/storyarc">StoryArcs</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for storyarc in storyarcs:
|
||||
<li><a href="/comics/storyarc/{{storyarc['_id']}}">{{storyarc['title']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
<li><a href="/comics/storyarc">StoryArcs</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/storyarc/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="title" size="60" value="{{title}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo
|
||||
restart: always
|
||||
volumes:
|
||||
- db-data:/var/lib/mongo
|
||||
networks:
|
||||
- backend-network
|
||||
app:
|
||||
build: app
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
networks:
|
||||
- backend-network
|
||||
- frontend-network
|
||||
nginx:
|
||||
image: nginx
|
||||
restart: always
|
||||
volumes:
|
||||
- ./nginx:/etc/nginx/conf.d
|
||||
- ./front-end:/var/www/front-end
|
||||
ports:
|
||||
- 8070:80
|
||||
networks:
|
||||
- frontend-network
|
||||
volumes:
|
||||
db-data:
|
||||
networks:
|
||||
backend-network:
|
||||
frontend-network:
|
||||
@@ -0,0 +1,82 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Hello!</title>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||
<meta content="utf-8" http-equiv="encoding">
|
||||
<style>
|
||||
*, ::after, ::before {
|
||||
box-sizing: inherit;
|
||||
background-color: #9eb5c2;
|
||||
color: rgb(8, 8, 71);
|
||||
}
|
||||
.moby {
|
||||
padding: 2rem 1rem;
|
||||
margin-bottom: 2rem;
|
||||
border-radius: .3rem;
|
||||
border-bottom: 0;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
.moby button {
|
||||
color: #fff;
|
||||
background-color: rgb(6, 105, 138);
|
||||
border-color: rgb(6, 105, 138);
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
.moby .query {
|
||||
text-align: left;
|
||||
border: 3px dotted #e9ecef;
|
||||
padding: 1.5rem 1.2rem;
|
||||
max-width: 800px;
|
||||
min-height: 200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: .3rem;
|
||||
}
|
||||
.moby .query button:hover{
|
||||
cursor: pointer;
|
||||
background-color: #73abff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="moby">
|
||||
<img src="https://www.docker.com/sites/default/files/Whale%20Logo332_5.png" />
|
||||
<h1>Hello there!</h1>
|
||||
<p>Simple DEV environment setup with Docker and Docker Compose</p>
|
||||
<div class="query">
|
||||
<p>Set url path(default is '/'), then query the app service.</p>
|
||||
<div>
|
||||
<button type="submit" onclick="queryServer()">GET</button>
|
||||
<input id="path" type="text">
|
||||
</div>
|
||||
<h3>Server response</h3>
|
||||
<div id="response">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function queryServer() {
|
||||
const Http = new XMLHttpRequest();
|
||||
const path = document.getElementById('path').value;
|
||||
const url = "/";
|
||||
Http.open("GET", url + path);
|
||||
Http.send();
|
||||
|
||||
Http.onreadystatechange = (e) => {
|
||||
document.getElementById('response').innerHTML = Http.responseText;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /var/www/front-end;
|
||||
|
||||
location /api {
|
||||
proxy_pass http://app:9000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user