Skip to content

Commit df19055

Browse files
Updated to Flask 1.x
1 parent 30176da commit df19055

34 files changed

+967
-780
lines changed

Dockerfile

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
FROM python:3.6.4-alpine3.6
1+
FROM python:3.8.2-alpine3.11
22

3-
ENV FLASK_APP=minitwit
3+
ENV FLASK_APP=flaskr
4+
ENV FLASK_ENV=development
45

56
COPY . /app
67

78
WORKDIR /app
89

910
RUN pip install --editable .
1011

11-
RUN flask initdb
12+
RUN flask init-db
1213

1314
# Unit tests
14-
# python setup.py test
15+
# RUN pip install pytest && pytest
1516

1617
EXPOSE 5000
1718

LICENSE

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Copyright © 2010 by the Pallets team.
2+
3+
Some rights reserved.
4+
5+
Redistribution and use in source and binary forms of the software as
6+
well as documentation, with or without modification, are permitted
7+
provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice,
10+
this list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright
13+
notice, this list of conditions and the following disclaimer in the
14+
documentation and/or other materials provided with the distribution.
15+
16+
* Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
21+
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
22+
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
23+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24+
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27+
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30+
THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF
31+
SUCH DAMAGE.

MANIFEST.in

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
graft minitwit/templates
2-
graft minitwit/static
3-
include minitwit/schema.sql
1+
include LICENSE
2+
include flaskr/schema.sql
3+
graft flaskr/static
4+
graft flaskr/templates
5+
graft tests
6+
global-exclude *.pyc

README.MD

+9-32
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,27 @@
11
## MiniTwit an example application written in Python/Flask
22

3-
Original source code from https://github.com/pallets/flask/tree/16d83d6bb413bec09a656a659b98d897e7abcc81/examples/minitwit
3+
Original source code from https://github.com/pallets/flask/tree/master/examples/tutorial
44

55
## Docker instructions
66

77
To create a docker image execute:
88

9-
`docker build . -t minitwit`
9+
`docker build . -t flaskr`
1010

1111
To run the docker image execute:
1212

13-
`docker run -p 5000:5000 minitwit` and visit with your browser http://localhost:5000
13+
`docker run -p 5000:5000 flaskr` and visit with your browser http://localhost:5000
1414

1515
To run unit tests inside the container execute:
1616

17-
`docker run minitwit python setup.py test`
17+
`docker run -it flaskr /bin/sh`
1818

19-
## Original Readme
19+
and then in the new command promt run
2020

21-
MiniTwit
21+
`pip install pytest && pytest`
2222

23-
because writing todo lists is not fun
23+
## To use this project in Codefresh
2424

25+
There is also a [codefresh.yml](codefresh.yml) for easy usage with the [Codefresh](codefresh.io) CI/CD platform.
2526

26-
### What is MiniTwit?
27-
28-
A SQLite and Flask powered twitter clone
29-
30-
### How do I use it?
31-
32-
1. edit the configuration in the minitwit.py file or
33-
export an `MINITWIT_SETTINGS` environment variable
34-
pointing to a configuration file.
35-
2. install the app from the root of the project directory
36-
`pip install --editable .`
37-
3. tell flask about the right application:
38-
`export FLASK_APP=minitwit`
39-
4. fire up a shell and run this:
40-
`flask initdb`
41-
5. now you can run minitwit:
42-
`flask run`
43-
44-
the application will greet you on
45-
http://localhost:5000/
46-
47-
### Is it tested?
48-
49-
You betcha. Run the `python setup.py test` file to
50-
see the tests pass.
27+
More details can be found in [Codefresh documentation](https://codefresh.io/docs/docs/getting-started/create-a-basic-pipeline/)

codefresh.yml

+6-5
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ steps:
1010
repo: '${{CF_REPO_OWNER}}/${{CF_REPO_NAME}}'
1111
revision: '${{CF_REVISION}}'
1212
stage: prepare
13-
MyAppDockerImage:
13+
my_python_docker_image:
1414
title: Building Docker Image
1515
type: build
1616
stage: build
17-
image_name: my-app-image
17+
image_name: my-flask-image
1818
working_directory: ./
1919
tag: '${{CF_BRANCH_TAG_NORMALIZED}}'
2020
dockerfile: Dockerfile
21-
MyUnitTests:
21+
my_unit_tests:
2222
title: Running Unit tests
2323
stage: test
24-
image: '${{MyAppDockerImage}}'
24+
image: '${{my_python_docker_image}}'
2525
commands:
26-
- python setup.py test
26+
- pip install pytest
27+
- pytest

flaskr/__init__.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import os
2+
3+
from flask import Flask
4+
5+
6+
def create_app(test_config=None):
7+
"""Create and configure an instance of the Flask application."""
8+
app = Flask(__name__, instance_relative_config=True)
9+
app.config.from_mapping(
10+
# a default secret that should be overridden by instance config
11+
SECRET_KEY="dev",
12+
# store the database in the instance folder
13+
DATABASE=os.path.join(app.instance_path, "flaskr.sqlite"),
14+
)
15+
16+
if test_config is None:
17+
# load the instance config, if it exists, when not testing
18+
app.config.from_pyfile("config.py", silent=True)
19+
else:
20+
# load the test config if passed in
21+
app.config.update(test_config)
22+
23+
# ensure the instance folder exists
24+
try:
25+
os.makedirs(app.instance_path)
26+
except OSError:
27+
pass
28+
29+
@app.route("/hello")
30+
def hello():
31+
return "Hello, World!"
32+
33+
# register the database commands
34+
from flaskr import db
35+
36+
db.init_app(app)
37+
38+
# apply the blueprints to the app
39+
from flaskr import auth, blog
40+
41+
app.register_blueprint(auth.bp)
42+
app.register_blueprint(blog.bp)
43+
44+
# make url_for('index') == url_for('blog.index')
45+
# in another app, you might define a separate main index here with
46+
# app.route, while giving the blog blueprint a url_prefix, but for
47+
# the tutorial the blog will be the main index
48+
app.add_url_rule("/", endpoint="index")
49+
50+
return app

flaskr/auth.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import functools
2+
3+
from flask import Blueprint
4+
from flask import flash
5+
from flask import g
6+
from flask import redirect
7+
from flask import render_template
8+
from flask import request
9+
from flask import session
10+
from flask import url_for
11+
from werkzeug.security import check_password_hash
12+
from werkzeug.security import generate_password_hash
13+
14+
from flaskr.db import get_db
15+
16+
bp = Blueprint("auth", __name__, url_prefix="/auth")
17+
18+
19+
def login_required(view):
20+
"""View decorator that redirects anonymous users to the login page."""
21+
22+
@functools.wraps(view)
23+
def wrapped_view(**kwargs):
24+
if g.user is None:
25+
return redirect(url_for("auth.login"))
26+
27+
return view(**kwargs)
28+
29+
return wrapped_view
30+
31+
32+
@bp.before_app_request
33+
def load_logged_in_user():
34+
"""If a user id is stored in the session, load the user object from
35+
the database into ``g.user``."""
36+
user_id = session.get("user_id")
37+
38+
if user_id is None:
39+
g.user = None
40+
else:
41+
g.user = (
42+
get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone()
43+
)
44+
45+
46+
@bp.route("/register", methods=("GET", "POST"))
47+
def register():
48+
"""Register a new user.
49+
50+
Validates that the username is not already taken. Hashes the
51+
password for security.
52+
"""
53+
if request.method == "POST":
54+
username = request.form["username"]
55+
password = request.form["password"]
56+
db = get_db()
57+
error = None
58+
59+
if not username:
60+
error = "Username is required."
61+
elif not password:
62+
error = "Password is required."
63+
elif (
64+
db.execute("SELECT id FROM user WHERE username = ?", (username,)).fetchone()
65+
is not None
66+
):
67+
error = "User {0} is already registered.".format(username)
68+
69+
if error is None:
70+
# the name is available, store it in the database and go to
71+
# the login page
72+
db.execute(
73+
"INSERT INTO user (username, password) VALUES (?, ?)",
74+
(username, generate_password_hash(password)),
75+
)
76+
db.commit()
77+
return redirect(url_for("auth.login"))
78+
79+
flash(error)
80+
81+
return render_template("auth/register.html")
82+
83+
84+
@bp.route("/login", methods=("GET", "POST"))
85+
def login():
86+
"""Log in a registered user by adding the user id to the session."""
87+
if request.method == "POST":
88+
username = request.form["username"]
89+
password = request.form["password"]
90+
db = get_db()
91+
error = None
92+
user = db.execute(
93+
"SELECT * FROM user WHERE username = ?", (username,)
94+
).fetchone()
95+
96+
if user is None:
97+
error = "Incorrect username."
98+
elif not check_password_hash(user["password"], password):
99+
error = "Incorrect password."
100+
101+
if error is None:
102+
# store the user id in a new session and return to the index
103+
session.clear()
104+
session["user_id"] = user["id"]
105+
return redirect(url_for("index"))
106+
107+
flash(error)
108+
109+
return render_template("auth/login.html")
110+
111+
112+
@bp.route("/logout")
113+
def logout():
114+
"""Clear the current session, including the stored user id."""
115+
session.clear()
116+
return redirect(url_for("index"))

0 commit comments

Comments
 (0)