Eddie
2 years ago
21 changed files with 434 additions and 280 deletions
Binary file not shown.
@ -1,121 +1,16 @@ |
|||||||
Creative Commons Legal Code |
MIT No Attribution |
||||||
|
|
||||||
CC0 1.0 Universal |
Copyright <YEAR> <COPYRIGHT HOLDER> |
||||||
|
|
||||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE |
Permission is hereby granted, free of charge, to any person obtaining a copy of this |
||||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN |
software and associated documentation files (the "Software"), to deal in the Software |
||||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS |
without restriction, including without limitation the rights to use, copy, modify, |
||||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES |
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to |
||||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS |
permit persons to whom the Software is furnished to do so. |
||||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM |
|
||||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
||||||
HEREUNDER. |
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
||||||
Statement of Purpose |
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
||||||
The laws of most jurisdictions throughout the world automatically confer |
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||||
exclusive Copyright and Related Rights (defined below) upon the creator |
|
||||||
and subsequent owner(s) (each and all, an "owner") of an original work of |
|
||||||
authorship and/or a database (each, a "Work"). |
|
||||||
|
|
||||||
Certain owners wish to permanently relinquish those rights to a Work for |
|
||||||
the purpose of contributing to a commons of creative, cultural and |
|
||||||
scientific works ("Commons") that the public can reliably and without fear |
|
||||||
of later claims of infringement build upon, modify, incorporate in other |
|
||||||
works, reuse and redistribute as freely as possible in any form whatsoever |
|
||||||
and for any purposes, including without limitation commercial purposes. |
|
||||||
These owners may contribute to the Commons to promote the ideal of a free |
|
||||||
culture and the further production of creative, cultural and scientific |
|
||||||
works, or to gain reputation or greater distribution for their Work in |
|
||||||
part through the use and efforts of others. |
|
||||||
|
|
||||||
For these and/or other purposes and motivations, and without any |
|
||||||
expectation of additional consideration or compensation, the person |
|
||||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she |
|
||||||
is an owner of Copyright and Related Rights in the Work, voluntarily |
|
||||||
elects to apply CC0 to the Work and publicly distribute the Work under its |
|
||||||
terms, with knowledge of his or her Copyright and Related Rights in the |
|
||||||
Work and the meaning and intended legal effect of CC0 on those rights. |
|
||||||
|
|
||||||
1. Copyright and Related Rights. A Work made available under CC0 may be |
|
||||||
protected by copyright and related or neighboring rights ("Copyright and |
|
||||||
Related Rights"). Copyright and Related Rights include, but are not |
|
||||||
limited to, the following: |
|
||||||
|
|
||||||
i. the right to reproduce, adapt, distribute, perform, display, |
|
||||||
communicate, and translate a Work; |
|
||||||
ii. moral rights retained by the original author(s) and/or performer(s); |
|
||||||
iii. publicity and privacy rights pertaining to a person's image or |
|
||||||
likeness depicted in a Work; |
|
||||||
iv. rights protecting against unfair competition in regards to a Work, |
|
||||||
subject to the limitations in paragraph 4(a), below; |
|
||||||
v. rights protecting the extraction, dissemination, use and reuse of data |
|
||||||
in a Work; |
|
||||||
vi. database rights (such as those arising under Directive 96/9/EC of the |
|
||||||
European Parliament and of the Council of 11 March 1996 on the legal |
|
||||||
protection of databases, and under any national implementation |
|
||||||
thereof, including any amended or successor version of such |
|
||||||
directive); and |
|
||||||
vii. other similar, equivalent or corresponding rights throughout the |
|
||||||
world based on applicable law or treaty, and any national |
|
||||||
implementations thereof. |
|
||||||
|
|
||||||
2. Waiver. To the greatest extent permitted by, but not in contravention |
|
||||||
of, applicable law, Affirmer hereby overtly, fully, permanently, |
|
||||||
irrevocably and unconditionally waives, abandons, and surrenders all of |
|
||||||
Affirmer's Copyright and Related Rights and associated claims and causes |
|
||||||
of action, whether now known or unknown (including existing as well as |
|
||||||
future claims and causes of action), in the Work (i) in all territories |
|
||||||
worldwide, (ii) for the maximum duration provided by applicable law or |
|
||||||
treaty (including future time extensions), (iii) in any current or future |
|
||||||
medium and for any number of copies, and (iv) for any purpose whatsoever, |
|
||||||
including without limitation commercial, advertising or promotional |
|
||||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each |
|
||||||
member of the public at large and to the detriment of Affirmer's heirs and |
|
||||||
successors, fully intending that such Waiver shall not be subject to |
|
||||||
revocation, rescission, cancellation, termination, or any other legal or |
|
||||||
equitable action to disrupt the quiet enjoyment of the Work by the public |
|
||||||
as contemplated by Affirmer's express Statement of Purpose. |
|
||||||
|
|
||||||
3. Public License Fallback. Should any part of the Waiver for any reason |
|
||||||
be judged legally invalid or ineffective under applicable law, then the |
|
||||||
Waiver shall be preserved to the maximum extent permitted taking into |
|
||||||
account Affirmer's express Statement of Purpose. In addition, to the |
|
||||||
extent the Waiver is so judged Affirmer hereby grants to each affected |
|
||||||
person a royalty-free, non transferable, non sublicensable, non exclusive, |
|
||||||
irrevocable and unconditional license to exercise Affirmer's Copyright and |
|
||||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the |
|
||||||
maximum duration provided by applicable law or treaty (including future |
|
||||||
time extensions), (iii) in any current or future medium and for any number |
|
||||||
of copies, and (iv) for any purpose whatsoever, including without |
|
||||||
limitation commercial, advertising or promotional purposes (the |
|
||||||
"License"). The License shall be deemed effective as of the date CC0 was |
|
||||||
applied by Affirmer to the Work. Should any part of the License for any |
|
||||||
reason be judged legally invalid or ineffective under applicable law, such |
|
||||||
partial invalidity or ineffectiveness shall not invalidate the remainder |
|
||||||
of the License, and in such case Affirmer hereby affirms that he or she |
|
||||||
will not (i) exercise any of his or her remaining Copyright and Related |
|
||||||
Rights in the Work or (ii) assert any associated claims and causes of |
|
||||||
action with respect to the Work, in either case contrary to Affirmer's |
|
||||||
express Statement of Purpose. |
|
||||||
|
|
||||||
4. Limitations and Disclaimers. |
|
||||||
|
|
||||||
a. No trademark or patent rights held by Affirmer are waived, abandoned, |
|
||||||
surrendered, licensed or otherwise affected by this document. |
|
||||||
b. Affirmer offers the Work as-is and makes no representations or |
|
||||||
warranties of any kind concerning the Work, express, implied, |
|
||||||
statutory or otherwise, including without limitation warranties of |
|
||||||
title, merchantability, fitness for a particular purpose, non |
|
||||||
infringement, or the absence of latent or other defects, accuracy, or |
|
||||||
the present or absence of errors, whether or not discoverable, all to |
|
||||||
the greatest extent permissible under applicable law. |
|
||||||
c. Affirmer disclaims responsibility for clearing rights of other persons |
|
||||||
that may apply to the Work or any use thereof, including without |
|
||||||
limitation any person's Copyright and Related Rights in the Work. |
|
||||||
Further, Affirmer disclaims responsibility for obtaining any necessary |
|
||||||
consents, permissions or other rights required for any use of the |
|
||||||
Work. |
|
||||||
d. Affirmer understands and acknowledges that Creative Commons is not a |
|
||||||
party to this document and has no duty or obligation with respect to |
|
||||||
this CC0 or use of the Work. |
|
||||||
|
@ -1,3 +1,3 @@ |
|||||||
# Markdown-Presenter |
# flask_boilerplate |
||||||
|
|
||||||
Ein kleines Projekt um Markdown Dokumente zu Presenten. |
Kleines Startsetup für eine Flask Webapp mit Login und Datenbank |
@ -1,13 +0,0 @@ |
|||||||
from services import (UserService, UserListService) |
|
||||||
from config import app, api, docs, CORS |
|
||||||
|
|
||||||
#*______________ Service Registration ______________ |
|
||||||
api.add_resource(UserService, '/api/user/<user_id>') |
|
||||||
docs.register(UserService) |
|
||||||
api.add_resource(UserListService, '/api/list/user') |
|
||||||
docs.register(UserListService) |
|
||||||
#*______________ Application Creation ______________ |
|
||||||
if __name__ == '__main__': |
|
||||||
app.run(debug=True) |
|
||||||
|
|
||||||
|
|
@ -0,0 +1,43 @@ |
|||||||
|
import os |
||||||
|
|
||||||
|
from flask import Flask |
||||||
|
|
||||||
|
|
||||||
|
def create_app(test_config=None): |
||||||
|
# create and configure the app |
||||||
|
app = Flask(__name__, instance_relative_config=True) |
||||||
|
app.config.from_mapping( |
||||||
|
SECRET_KEY='dev', |
||||||
|
DATABASE=os.path.join(app.instance_path, 'app.sqlite'), |
||||||
|
) |
||||||
|
|
||||||
|
if test_config is None: |
||||||
|
# load the instance config, if it exists, when not testing |
||||||
|
app.config.from_pyfile('config.py', silent=True) |
||||||
|
else: |
||||||
|
# load the test config if passed in |
||||||
|
app.config.from_mapping(test_config) |
||||||
|
|
||||||
|
# ensure the instance folder exists |
||||||
|
try: |
||||||
|
os.makedirs(app.instance_path) |
||||||
|
except OSError: |
||||||
|
pass |
||||||
|
|
||||||
|
# a simple page that says hello |
||||||
|
@app.route('/hello') |
||||||
|
def hello(): |
||||||
|
return 'Hello, World!' |
||||||
|
|
||||||
|
|
||||||
|
from . import db |
||||||
|
db.init_app(app) |
||||||
|
|
||||||
|
from . import auth |
||||||
|
app.register_blueprint(auth.bp) |
||||||
|
|
||||||
|
from . import index |
||||||
|
app.register_blueprint(index.bp) |
||||||
|
app.add_url_rule('/', endpoint='index') |
||||||
|
|
||||||
|
return app |
@ -0,0 +1,95 @@ |
|||||||
|
import functools |
||||||
|
|
||||||
|
from flask import ( |
||||||
|
Blueprint, flash, g, redirect, render_template, request, session, url_for |
||||||
|
) |
||||||
|
from werkzeug.security import check_password_hash, generate_password_hash |
||||||
|
|
||||||
|
from app.db import get_db |
||||||
|
|
||||||
|
bp = Blueprint('auth', __name__, url_prefix='/auth') |
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/register', methods=('GET', 'POST')) |
||||||
|
def register(): |
||||||
|
if request.method == 'POST': |
||||||
|
username = request.form['username'] |
||||||
|
password = request.form['password'] |
||||||
|
db = get_db() |
||||||
|
error = None |
||||||
|
|
||||||
|
if not username: |
||||||
|
error = 'Username is required.' |
||||||
|
elif not password: |
||||||
|
error = 'Password is required.' |
||||||
|
elif db.execute( |
||||||
|
'SELECT id FROM user WHERE username = ?', (username,) |
||||||
|
).fetchone() is not None: |
||||||
|
error = 'User {} is already registered.'.format(username) |
||||||
|
|
||||||
|
if error is None: |
||||||
|
db.execute( |
||||||
|
'INSERT INTO user (username, password) VALUES (?, ?)', |
||||||
|
(username, generate_password_hash(password)) |
||||||
|
) |
||||||
|
db.commit() |
||||||
|
return redirect(url_for('auth.login')) |
||||||
|
|
||||||
|
flash(error) |
||||||
|
|
||||||
|
return render_template('auth/register.html') |
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/login', methods=('GET', 'POST')) |
||||||
|
def login(): |
||||||
|
if request.method == 'POST': |
||||||
|
username = request.form['username'] |
||||||
|
password = request.form['password'] |
||||||
|
db = get_db() |
||||||
|
error = None |
||||||
|
user = db.execute( |
||||||
|
'SELECT * FROM user WHERE username = ?', (username,) |
||||||
|
).fetchone() |
||||||
|
|
||||||
|
if user is None: |
||||||
|
error = 'Incorrect username.' |
||||||
|
elif not check_password_hash(user['password'], password): |
||||||
|
error = 'Incorrect password.' |
||||||
|
|
||||||
|
if error is None: |
||||||
|
session.clear() |
||||||
|
session['user_id'] = user['id'] |
||||||
|
return redirect(url_for('index')) |
||||||
|
|
||||||
|
flash(error) |
||||||
|
|
||||||
|
return render_template('auth/login.html') |
||||||
|
|
||||||
|
|
||||||
|
@bp.before_app_request |
||||||
|
def load_logged_in_user(): |
||||||
|
user_id = session.get('user_id') |
||||||
|
|
||||||
|
if user_id is None: |
||||||
|
g.user = None |
||||||
|
else: |
||||||
|
g.user = get_db().execute( |
||||||
|
'SELECT * FROM user WHERE id = ?', (user_id,) |
||||||
|
).fetchone() |
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/logout') |
||||||
|
def logout(): |
||||||
|
session.clear() |
||||||
|
return redirect(url_for('index')) |
||||||
|
|
||||||
|
|
||||||
|
def login_required(view): |
||||||
|
@functools.wraps(view) |
||||||
|
def wrapped_view(**kwargs): |
||||||
|
if g.user is None: |
||||||
|
return redirect(url_for('auth.login')) |
||||||
|
|
||||||
|
return view(**kwargs) |
||||||
|
|
||||||
|
return wrapped_view |
@ -0,0 +1,41 @@ |
|||||||
|
import sqlite3 |
||||||
|
|
||||||
|
import click |
||||||
|
from flask import current_app, g |
||||||
|
from flask.cli import with_appcontext |
||||||
|
|
||||||
|
|
||||||
|
def get_db(): |
||||||
|
if 'db' not in g: |
||||||
|
g.db = sqlite3.connect( |
||||||
|
current_app.config['DATABASE'], |
||||||
|
detect_types=sqlite3.PARSE_DECLTYPES |
||||||
|
) |
||||||
|
g.db.row_factory = sqlite3.Row |
||||||
|
|
||||||
|
return g.db |
||||||
|
|
||||||
|
|
||||||
|
def close_db(e=None): |
||||||
|
db = g.pop('db', None) |
||||||
|
|
||||||
|
if db is not None: |
||||||
|
db.close() |
||||||
|
|
||||||
|
def init_db(): |
||||||
|
db = get_db() |
||||||
|
|
||||||
|
with current_app.open_resource('schema.sql') as f: |
||||||
|
db.executescript(f.read().decode('utf8')) |
||||||
|
|
||||||
|
|
||||||
|
@click.command('init-db') |
||||||
|
@with_appcontext |
||||||
|
def init_db_command(): |
||||||
|
"""Clear the existing data and create new tables.""" |
||||||
|
init_db() |
||||||
|
click.echo('Initialized the database.') |
||||||
|
|
||||||
|
def init_app(app): |
||||||
|
app.teardown_appcontext(close_db) |
||||||
|
app.cli.add_command(init_db_command) |
@ -0,0 +1,23 @@ |
|||||||
|
from flask import ( |
||||||
|
Blueprint, flash, g, redirect, render_template, request, url_for |
||||||
|
) |
||||||
|
from werkzeug.exceptions import abort |
||||||
|
|
||||||
|
from app.auth import login_required |
||||||
|
from app.db import get_db |
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint('index', __name__) |
||||||
|
|
||||||
|
@bp.route('/') |
||||||
|
@login_required |
||||||
|
def index(): |
||||||
|
db = get_db() |
||||||
|
loginEvents = db.execute( |
||||||
|
'SELECT *' |
||||||
|
' FROM loginEvent' |
||||||
|
' ORDER BY created DESC' |
||||||
|
).fetchall() |
||||||
|
return render_template('index.html', loginEvents = loginEvents) |
||||||
|
|
||||||
|
|
@ -0,0 +1,15 @@ |
|||||||
|
DROP TABLE IF EXISTS user; |
||||||
|
DROP TABLE IF EXISTS post; |
||||||
|
|
||||||
|
CREATE TABLE user ( |
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT, |
||||||
|
username TEXT UNIQUE NOT NULL, |
||||||
|
password TEXT NOT NULL |
||||||
|
); |
||||||
|
|
||||||
|
CREATE TABLE loginEvent ( |
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT, |
||||||
|
walletId TEXT NOT NULL, |
||||||
|
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, |
||||||
|
eventType TEXT NOT NULL |
||||||
|
); |
@ -0,0 +1,134 @@ |
|||||||
|
html { |
||||||
|
font-family: sans-serif; |
||||||
|
background: #eee; |
||||||
|
padding: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
max-width: 960px; |
||||||
|
margin: 0 auto; |
||||||
|
background: white; |
||||||
|
} |
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 { |
||||||
|
font-family: serif; |
||||||
|
color: #377ba8; |
||||||
|
margin: 1rem 0; |
||||||
|
} |
||||||
|
|
||||||
|
a { |
||||||
|
color: #377ba8; |
||||||
|
} |
||||||
|
|
||||||
|
hr { |
||||||
|
border: none; |
||||||
|
border-top: 1px solid lightgray; |
||||||
|
} |
||||||
|
|
||||||
|
nav { |
||||||
|
background: lightgray; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
padding: 0 0.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
nav h1 { |
||||||
|
flex: auto; |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
|
||||||
|
nav h1 a { |
||||||
|
text-decoration: none; |
||||||
|
padding: 0.25rem 0.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
nav ul { |
||||||
|
display: flex; |
||||||
|
list-style: none; |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
|
||||||
|
nav ul li a, nav ul li span, header .action { |
||||||
|
display: block; |
||||||
|
padding: 0.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
.content { |
||||||
|
padding: 0 1rem 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.content > header { |
||||||
|
border-bottom: 1px solid lightgray; |
||||||
|
display: flex; |
||||||
|
align-items: flex-end; |
||||||
|
} |
||||||
|
|
||||||
|
.content > header h1 { |
||||||
|
flex: auto; |
||||||
|
margin: 1rem 0 0.25rem 0; |
||||||
|
} |
||||||
|
|
||||||
|
.flash { |
||||||
|
margin: 1em 0; |
||||||
|
padding: 1em; |
||||||
|
background: #cae6f6; |
||||||
|
border: 1px solid #377ba8; |
||||||
|
} |
||||||
|
|
||||||
|
.post > header { |
||||||
|
display: flex; |
||||||
|
align-items: flex-end; |
||||||
|
font-size: 0.85em; |
||||||
|
} |
||||||
|
|
||||||
|
.post > header > div:first-of-type { |
||||||
|
flex: auto; |
||||||
|
} |
||||||
|
|
||||||
|
.post > header h1 { |
||||||
|
font-size: 1.5em; |
||||||
|
margin-bottom: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.post .about { |
||||||
|
color: slategray; |
||||||
|
font-style: italic; |
||||||
|
} |
||||||
|
|
||||||
|
.post .body { |
||||||
|
white-space: pre-line; |
||||||
|
} |
||||||
|
|
||||||
|
.content:last-child { |
||||||
|
margin-bottom: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.content form { |
||||||
|
margin: 1em 0; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
} |
||||||
|
|
||||||
|
.content label { |
||||||
|
font-weight: bold; |
||||||
|
margin-bottom: 0.5em; |
||||||
|
} |
||||||
|
|
||||||
|
.content input, .content textarea { |
||||||
|
margin-bottom: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
.content textarea { |
||||||
|
min-height: 12em; |
||||||
|
resize: vertical; |
||||||
|
} |
||||||
|
|
||||||
|
input.danger { |
||||||
|
color: #cc2f2e; |
||||||
|
} |
||||||
|
|
||||||
|
input[type=submit] { |
||||||
|
align-self: start; |
||||||
|
min-width: 10em; |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
{% extends 'base.html' %} |
||||||
|
|
||||||
|
{% block header %} |
||||||
|
<h1>{% block title %}Log In{% endblock %}</h1> |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<form method="post"> |
||||||
|
<label for="username">Username</label> |
||||||
|
<input name="username" id="username" required> |
||||||
|
<label for="password">Password</label> |
||||||
|
<input type="password" name="password" id="password" required> |
||||||
|
<input type="submit" value="Log In"> |
||||||
|
</form> |
||||||
|
{% endblock %} |
@ -0,0 +1,15 @@ |
|||||||
|
{% extends 'base.html' %} |
||||||
|
|
||||||
|
{% block header %} |
||||||
|
<h1>{% block title %}Register{% endblock %}</h1> |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<form method="post"> |
||||||
|
<label for="username">Username</label> |
||||||
|
<input name="username" id="username" required> |
||||||
|
<label for="password">Password</label> |
||||||
|
<input type="password" name="password" id="password" required> |
||||||
|
<input type="submit" value="Register"> |
||||||
|
</form> |
||||||
|
{% endblock %} |
@ -0,0 +1,24 @@ |
|||||||
|
<!doctype html> |
||||||
|
<title>{% block title %}{% endblock %} - Flaskr</title> |
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap.css') }}"> |
||||||
|
<nav> |
||||||
|
<h1>Flaskr</h1> |
||||||
|
<ul> |
||||||
|
{% if g.user %} |
||||||
|
<li><span>{{ g.user['username'] }}</span> |
||||||
|
<li><a href="{{ url_for('auth.logout') }}">Log Out</a> |
||||||
|
{% else %} |
||||||
|
<li><a href="{{ url_for('auth.register') }}">Register</a> |
||||||
|
<li><a href="{{ url_for('auth.login') }}">Log In</a> |
||||||
|
{% endif %} |
||||||
|
</ul> |
||||||
|
</nav> |
||||||
|
<section class="content"> |
||||||
|
<header> |
||||||
|
{% block header %}{% endblock %} |
||||||
|
</header> |
||||||
|
{% for message in get_flashed_messages() %} |
||||||
|
<div class="flash">{{ message }}</div> |
||||||
|
{% endfor %} |
||||||
|
{% block content %}{% endblock %} |
||||||
|
</section> |
@ -0,0 +1,11 @@ |
|||||||
|
{% extends 'base.html' %} |
||||||
|
|
||||||
|
{% block header %} |
||||||
|
<h1>{% block title %}Login Events{% endblock %}</h1> |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
|
||||||
|
<a>Hello World</a> |
||||||
|
|
||||||
|
{% endblock %} |
@ -1,40 +0,0 @@ |
|||||||
from flask import Flask |
|
||||||
from flask_restful import Api |
|
||||||
from apispec import APISpec |
|
||||||
from apispec.ext.marshmallow import MarshmallowPlugin |
|
||||||
from flask_apispec.extension import FlaskApiSpec |
|
||||||
from flask_marshmallow import Marshmallow |
|
||||||
from flask_sqlalchemy import SQLAlchemy |
|
||||||
from flask_cors import CORS |
|
||||||
#!______________ App Setup _____________ _ |
|
||||||
app = Flask(__name__, static_url_path='/static') |
|
||||||
api_v1_cors_config = { |
|
||||||
"origins": ["http://localhost:5000"] |
|
||||||
} |
|
||||||
|
|
||||||
#!______________ CORS Setup _____________ _ |
|
||||||
CORS(app, resources={"/api/*": api_v1_cors_config}) |
|
||||||
|
|
||||||
#!______________ DB Setup ______________ |
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///markdown.db' |
|
||||||
app.config['SECRET_KEY'] = 'InputSecretKeyHere' |
|
||||||
db = SQLAlchemy(app) |
|
||||||
|
|
||||||
#!______________ Marshmallow Setup ______________ |
|
||||||
ma = Marshmallow(app) |
|
||||||
|
|
||||||
#!______________ API & Swagger Setup ______________ |
|
||||||
api = Api(app) |
|
||||||
app.config.update({ |
|
||||||
'APISPEC_SPEC': APISpec( |
|
||||||
title='Markdown Presenter', |
|
||||||
version='v0.0.1', |
|
||||||
plugins=[MarshmallowPlugin()], |
|
||||||
openapi_version='2.0.0' |
|
||||||
), |
|
||||||
'APISPEC_SWAGGER_URL': '/swagger/', |
|
||||||
'APISPEC_SWAGGER_UI_URL': '/swagger-ui/' |
|
||||||
}) |
|
||||||
|
|
||||||
#!______________ Docs Setup ______________ |
|
||||||
docs = FlaskApiSpec(app) |
|
@ -1,10 +0,0 @@ |
|||||||
from config import db |
|
||||||
|
|
||||||
#!______________ DB Models ______________ |
|
||||||
class User(db.Model): |
|
||||||
__tablename__ = "User" |
|
||||||
id = db.Column(db.Integer, primary_key=True) |
|
||||||
name = db.Column(db.String(50)) |
|
||||||
image = db.Column(db.String()) |
|
||||||
description = db.Column(db.String()) |
|
||||||
|
|
@ -1,18 +0,0 @@ |
|||||||
import os |
|
||||||
from config import db |
|
||||||
from termcolor import colored |
|
||||||
from config import app |
|
||||||
|
|
||||||
def RebuildDatabase(): |
|
||||||
if os.path.exists('acceth.db'): |
|
||||||
print(colored('Removing existing DB', 'blue')) |
|
||||||
os.remove('acceth.db') |
|
||||||
|
|
||||||
with app.app_context(): |
|
||||||
db.create_all() |
|
||||||
db.session.commit() |
|
||||||
|
|
||||||
print(colored('New DB created.', 'green')) |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
RebuildDatabase() |
|
@ -1,25 +0,0 @@ |
|||||||
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema |
|
||||||
from config import db |
|
||||||
from models import User |
|
||||||
from marshmallow import fields |
|
||||||
|
|
||||||
#*______________ Base Schema ______________ |
|
||||||
class BaseScheme(SQLAlchemyAutoSchema): |
|
||||||
def __str__(self): |
|
||||||
return str(self.__class__) + ": " + str(self.__dict__) |
|
||||||
class Meta: |
|
||||||
ordered = True |
|
||||||
sqla_session = db.session |
|
||||||
include_fk = True |
|
||||||
load_instance = True |
|
||||||
|
|
||||||
#*______________ User Schemes ______________ |
|
||||||
class UserSchema(BaseScheme): |
|
||||||
class Meta(BaseScheme.Meta): |
|
||||||
model = User |
|
||||||
id = fields.Int() |
|
||||||
name = fields.Str() |
|
||||||
class UserInsertSchema(UserSchema): |
|
||||||
user_id = fields.Int() |
|
||||||
class UserResponseSchema(UserSchema): |
|
||||||
name = fields.Str() |
|
@ -1,46 +0,0 @@ |
|||||||
from flask_apispec import marshal_with, doc, use_kwargs |
|
||||||
from flask_apispec.views import MethodResource |
|
||||||
from flask_restful import Resource |
|
||||||
from schemes import (UserSchema, UserResponseSchema) |
|
||||||
from config import db |
|
||||||
from models import User |
|
||||||
|
|
||||||
#!______________ User ______________ |
|
||||||
class UserService(MethodResource, Resource): |
|
||||||
@doc(description='Get User by User_id', tags=['User']) |
|
||||||
@marshal_with(UserResponseSchema) |
|
||||||
def get(self, user_id): |
|
||||||
quser = db.session.query(User).get(user_id) |
|
||||||
return UserSchema().dump(quser) |
|
||||||
|
|
||||||
@doc(description='Add new User', tags=['User']) |
|
||||||
@use_kwargs(UserSchema, location=('json')) |
|
||||||
@marshal_with(UserResponseSchema()) |
|
||||||
def post(self, user, user_id): |
|
||||||
db.session.add(user) |
|
||||||
db.session.commit() |
|
||||||
return UserSchema().dump(user) |
|
||||||
|
|
||||||
@doc(description='Update User with PUT', tags=['User']) |
|
||||||
@use_kwargs(UserSchema, location=('json')) |
|
||||||
@marshal_with(UserResponseSchema()) |
|
||||||
def put(self, user, user_id): |
|
||||||
db.session.add(user) |
|
||||||
db.session.commit() |
|
||||||
return UserSchema().dump(user) |
|
||||||
|
|
||||||
@doc(description='Delete existing User', tags=['User']) |
|
||||||
@use_kwargs(UserSchema, location=('json')) |
|
||||||
@marshal_with(UserResponseSchema()) |
|
||||||
def delete(self, user, user_id): |
|
||||||
user = db.session.query(User).get(user_id) |
|
||||||
db.session.delete(user) |
|
||||||
db.session.commit() |
|
||||||
return UserSchema().dump(user) |
|
||||||
|
|
||||||
class UserListService(MethodResource, Resource): |
|
||||||
@doc(description='Get a List of all User', tags=['List']) |
|
||||||
@marshal_with(UserResponseSchema(many=True)) |
|
||||||
def get(self): |
|
||||||
users = db.session.query(User).all() |
|
||||||
return UserSchema(many=True).dump(users) |
|
Loading…
Reference in new issue