@@ -0,0 +1,11 @@ | |||
version: "3.9" | |||
services: | |||
web: | |||
build: ./web | |||
container_name: python | |||
ports: | |||
- "5000:5000" | |||
volumes: | |||
- ./web:/code | |||
env_file: | |||
- ./.env.dev |
@@ -0,0 +1,7 @@ | |||
# syntax=docker/dockerfile:1 | |||
FROM python:3.8-slim-buster | |||
WORKDIR /code | |||
COPY requirements.txt requirements.txt | |||
RUN pip install -r requirements.txt | |||
COPY . . | |||
CMD ["python3", "-m", "flask", "run"] |
@@ -0,0 +1,35 @@ | |||
from flask import Flask | |||
from flask_sqlalchemy import SQLAlchemy | |||
from flask_login import LoginManager | |||
# init SQLAlchemy so we can use it later in our models | |||
db = SQLAlchemy() | |||
def create_app(): | |||
app = Flask(__name__) | |||
app.config['SECRET_KEY'] = 'f9DWPyF70N' | |||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' | |||
db.init_app(app) | |||
login_manager = LoginManager() | |||
login_manager.login_view = 'auth.login' | |||
login_manager.init_app(app) | |||
from .models import User | |||
@login_manager.user_loader | |||
def load_user(user_id): | |||
# since the user_id is just the primary key of our user table, use it in the query for the user | |||
return User.query.get(int(user_id)) | |||
# blueprint for auth routes in our app | |||
from .auth import auth as auth_blueprint | |||
app.register_blueprint(auth_blueprint) | |||
# blueprint for non-auth parts of app | |||
from .main import main as main_blueprint | |||
app.register_blueprint(main_blueprint) | |||
return app |
@@ -0,0 +1,60 @@ | |||
from flask import Blueprint, render_template, redirect, url_for, request, flash | |||
from werkzeug.security import generate_password_hash, check_password_hash | |||
from flask_login import login_user, logout_user, login_required | |||
from .models import User | |||
from . import db | |||
auth = Blueprint('auth', __name__) | |||
@auth.route('/login') | |||
def login(): | |||
return render_template('login.html') | |||
@auth.route('/login', methods=['POST']) | |||
def login_post(): | |||
email = request.form.get('email') | |||
password = request.form.get('password') | |||
remember = True if request.form.get('remember') else False | |||
user = User.query.filter_by(email=email).first() | |||
# check if the user actually exists | |||
# take the user-supplied password, hash it, and compare it to the hashed password in the database | |||
if not user or not check_password_hash(user.password, password): | |||
flash('Please check your login details and try again.') | |||
return redirect(url_for('auth.login')) # if the user doesn't exist or password is wrong, reload the page | |||
# if the above check passes, then we know the user has the right credentials | |||
login_user(user, remember=remember) | |||
return redirect(url_for('main.profile')) | |||
@auth.route('/signup') | |||
def signup(): | |||
return render_template('signup.html') | |||
@auth.route('/signup', methods=['POST']) | |||
def signup_post(): | |||
email = request.form.get('email') | |||
name = request.form.get('name') | |||
password = request.form.get('password') | |||
user = User.query.filter_by(email=email).first() # if this returns a user, then the email already exists in database | |||
if user: # if a user is found, we want to redirect back to signup page so user can try again | |||
flash('Email address already exists') | |||
return redirect(url_for('auth.signup')) | |||
# create a new user with the form data. Hash the password so the plaintext version isn't saved. | |||
new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256')) | |||
# add the new user to the database | |||
db.session.add(new_user) | |||
db.session.commit() | |||
return redirect(url_for('auth.login')) | |||
@auth.route('/logout') | |||
@login_required | |||
def logout(): | |||
logout_user() | |||
return redirect(url_for('main.index')) |
@@ -0,0 +1,14 @@ | |||
from flask import Blueprint, render_template | |||
from flask_login import login_required, current_user | |||
from . import db | |||
main = Blueprint('main', __name__) | |||
@main.route('/') | |||
def index(): | |||
return render_template('index.html') | |||
@main.route('/profile') | |||
@login_required | |||
def profile(): | |||
return render_template('profile.html', name=current_user.name) |
@@ -0,0 +1,15 @@ | |||
from flask_login import UserMixin | |||
from . import db | |||
from datetime import datetime | |||
class User(UserMixin, db.Model): | |||
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy | |||
email = db.Column(db.String(100), unique=True) | |||
password = db.Column(db.String(100)) | |||
name = db.Column(db.String(1000)) | |||
class Tools(db.Model): | |||
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy | |||
created = db.Column(db.DateTime, default=datetime.utcnow) | |||
name = db.Column(db.Text) | |||
description = db.Column(db.Text) |
@@ -0,0 +1,57 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title>Flask Auth Example</title> | |||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" /> | |||
</head> | |||
<body> | |||
<section class="hero is-primary is-fullheight"> | |||
<div class="hero-head"> | |||
<nav class="navbar"> | |||
<div class="container"> | |||
<div id="navbarMenuHeroA" class="navbar-menu"> | |||
<div class="navbar-end"> | |||
<a href="{{ url_for('main.index') }}" class="navbar-item"> | |||
Home | |||
</a> | |||
{% if current_user.is_authenticated %} | |||
<a href="{{ url_for('main.profile') }}" class="navbar-item"> | |||
Profile | |||
</a> | |||
{% endif %} | |||
{% if not current_user.is_authenticated %} | |||
<a href="{{ url_for('auth.login') }}" class="navbar-item"> | |||
Login | |||
</a> | |||
<a href="{{ url_for('auth.signup') }}" class="navbar-item"> | |||
Sign Up | |||
</a> | |||
{% endif %} | |||
{% if current_user.is_authenticated %} | |||
<a href="{{ url_for('auth.logout') }}" class="navbar-item"> | |||
Logout | |||
</a> | |||
{% endif %} | |||
</div> | |||
</div> | |||
</div> | |||
</nav> | |||
</div> | |||
<div class="hero-body"> | |||
<div class="container has-text-centered"> | |||
{% block content %} | |||
{% endblock %} | |||
</div> | |||
</div> | |||
</section> | |||
</body> | |||
</html> |
@@ -0,0 +1,10 @@ | |||
{% extends "base.html" %} | |||
{% block content %} | |||
<h1 class="title"> | |||
COPIM online toolkit | |||
</h1> | |||
<h2 class="subtitle"> | |||
testing online toolkit site | |||
</h2> | |||
{% endblock %} |
@@ -0,0 +1,36 @@ | |||
{% extends "base.html" %} | |||
{% block content %} | |||
<div class="column is-4 is-offset-4"> | |||
<h3 class="title">Login</h3> | |||
<div class="box"> | |||
{% with messages = get_flashed_messages() %} | |||
{% if messages %} | |||
<div class="notification is-danger"> | |||
{{ messages[0] }} | |||
</div> | |||
{% endif %} | |||
{% endwith %} | |||
<form method="POST" action="/login"> | |||
<div class="field"> | |||
<div class="control"> | |||
<input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus=""> | |||
</div> | |||
</div> | |||
<div class="field"> | |||
<div class="control"> | |||
<input class="input is-large" type="password" name="password" placeholder="Your Password"> | |||
</div> | |||
</div> | |||
<div class="field"> | |||
<label class="checkbox"> | |||
<input type="checkbox"> | |||
Remember me | |||
</label> | |||
</div> | |||
<button class="button is-block is-info is-large is-fullwidth">Login</button> | |||
</form> | |||
</div> | |||
</div> | |||
{% endblock %} |
@@ -0,0 +1,7 @@ | |||
{% extends "base.html" %} | |||
{% block content %} | |||
<h1 class="title"> | |||
Welcome, {{ name }}! | |||
</h1> | |||
{% endblock %} |
@@ -0,0 +1,37 @@ | |||
{% extends "base.html" %} | |||
{% block content %} | |||
<div class="column is-4 is-offset-4"> | |||
<h3 class="title">Sign Up</h3> | |||
<div class="box"> | |||
{% with messages = get_flashed_messages() %} | |||
{% if messages %} | |||
<div class="notification is-danger"> | |||
{{ messages[0] }}. Go to <a href="{{ url_for('auth.login') }}">login page</a>. | |||
</div> | |||
{% endif %} | |||
{% endwith %} | |||
<form method="POST" action="/signup"> | |||
<div class="field"> | |||
<div class="control"> | |||
<input class="input is-large" type="email" name="email" placeholder="Email" autofocus=""> | |||
</div> | |||
</div> | |||
<div class="field"> | |||
<div class="control"> | |||
<input class="input is-large" type="text" name="name" placeholder="Name" autofocus=""> | |||
</div> | |||
</div> | |||
<div class="field"> | |||
<div class="control"> | |||
<input class="input is-large" type="password" name="password" placeholder="Password"> | |||
</div> | |||
</div> | |||
<button class="button is-block is-info is-large is-fullwidth">Sign Up</button> | |||
</form> | |||
</div> | |||
</div> | |||
{% endblock %} |
@@ -0,0 +1,4 @@ | |||
flask | |||
requests | |||
flask-sqlalchemy | |||
flask-login |