Initial commit
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# app/__init__.py
|
||||
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_login import LoginManager
|
||||
|
||||
db = SQLAlchemy()
|
||||
migrate = Migrate()
|
||||
login_manager = LoginManager()
|
||||
|
||||
def create_app(config_class='config.Config'):
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(config_class)
|
||||
|
||||
db.init_app(app)
|
||||
migrate.init_app(app, db)
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'auth.login' # IMPORTANT: now uses auth Blueprint
|
||||
|
||||
from app.main import main
|
||||
from app.auth import auth
|
||||
|
||||
app.register_blueprint(main)
|
||||
app.register_blueprint(auth)
|
||||
|
||||
return app
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+41
@@ -0,0 +1,41 @@
|
||||
# app/auth.py
|
||||
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_user, logout_user, login_required
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
from app import db
|
||||
from app.models import User
|
||||
from app.forms import RegistrationForm, LoginForm
|
||||
|
||||
auth = Blueprint('auth', __name__, url_prefix='/auth')
|
||||
|
||||
@auth.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
form = RegistrationForm()
|
||||
if form.validate_on_submit():
|
||||
hashed_password = generate_password_hash(form.password.data)
|
||||
user = User(username=form.username.data, email=form.email.data, password_hash=hashed_password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
flash('Account created! Please log in.', 'success')
|
||||
return redirect(url_for('auth.login'))
|
||||
return render_template('register.html', form=form)
|
||||
|
||||
@auth.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(email=form.email.data).first()
|
||||
if user and check_password_hash(user.password_hash, form.password.data):
|
||||
login_user(user)
|
||||
return redirect(url_for('main.index'))
|
||||
else:
|
||||
flash('Login failed. Check username/password.', 'danger')
|
||||
return render_template('login.html', form=form)
|
||||
|
||||
@auth.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for('main.index'))
|
||||
@@ -0,0 +1,26 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, SubmitField
|
||||
from wtforms.validators import DataRequired, Email, EqualTo, ValidationError, Length
|
||||
from .models import User
|
||||
|
||||
class RegistrationForm(FlaskForm):
|
||||
username = StringField('Username', validators=[DataRequired(), Length(min=3, max=64)])
|
||||
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||
password = PasswordField('Password', validators=[DataRequired(), Length(min=8, max=64)])
|
||||
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
|
||||
submit = SubmitField('Register')
|
||||
|
||||
def validate_username(self, username):
|
||||
user = User.query.filter_by(username=username.data).first()
|
||||
if user:
|
||||
raise ValidationError('Username already exists.')
|
||||
|
||||
def validate_email(self, email):
|
||||
user = User.query.filter_by(email=email.data).first()
|
||||
if user:
|
||||
raise ValidationError('Email already registered.')
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||
password = PasswordField('Password', validators=[DataRequired()])
|
||||
submit = SubmitField('Login')
|
||||
@@ -0,0 +1,9 @@
|
||||
# app/main.py
|
||||
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
main = Blueprint('main', __name__)
|
||||
|
||||
@main.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
@@ -0,0 +1,13 @@
|
||||
from . import db
|
||||
from flask_login import UserMixin
|
||||
from . import login_manager
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(64), unique=True, nullable=False)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||
password_hash = db.Column(db.String(256), nullable=False)
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.query.get(int(user_id))
|
||||
@@ -0,0 +1,4 @@
|
||||
.error {
|
||||
color: red;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block title %}Home{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Welcome to the Flask Template App!</h1>
|
||||
<p>This is the index page.</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Flask Template App</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
{% if current_user.is_authenticated %}
|
||||
Hello {{ current_user.username }} |
|
||||
<a href="{{ url_for('auth.logout') }}">Logout</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('auth.login') }}">Login</a> |
|
||||
<a href="{{ url_for('auth.register') }}">Register</a>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<!-- FLASH MESSAGES -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="flash {{ category }}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<h2>Login</h2>
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.email(size=32, placeholder=form.email.label.text) }}<br>
|
||||
{{ form.password(size=32, placeholder=form.password.label.text) }}<br>
|
||||
{{ form.submit() }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,39 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<h2>Register</h2>
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<p>
|
||||
{{ form.username(size=32, placeholder=form.username.label.text) }}
|
||||
{% for error in form.username.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form.email(size=32, placeholder=form.email.label.text) }}
|
||||
{% for error in form.email.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form.password(size=32, placeholder=form.password.label.text) }}
|
||||
{% for error in form.password.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form.confirm_password(size=32, placeholder=form.confirm_password.label.text) }}
|
||||
{% for error in form.confirm_password.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form.submit() }}
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user