| version: "3.9" | |||||
| services: | |||||
| web: | |||||
| build: ./web | |||||
| container_name: python | |||||
| ports: | |||||
| - "5000:5000" | |||||
| volumes: | |||||
| - ./web:/code | |||||
| env_file: | |||||
| - ./.env.dev | 
| # 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"] | 
| 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 | 
| 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')) | 
| 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) | 
| 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) | 
| <!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> | 
| {% extends "base.html" %} | |||||
| {% block content %} | |||||
| <h1 class="title"> | |||||
| COPIM online toolkit | |||||
| </h1> | |||||
| <h2 class="subtitle"> | |||||
| testing online toolkit site | |||||
| </h2> | |||||
| {% endblock %} | 
| {% 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 %} | 
| {% extends "base.html" %} | |||||
| {% block content %} | |||||
| <h1 class="title"> | |||||
| Welcome, {{ name }}! | |||||
| </h1> | |||||
| {% endblock %} | 
| {% 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 %} | 
| flask | |||||
| requests | |||||
| flask-sqlalchemy | |||||
| flask-login |