Создана основа главной страницы, страницы регистрации и входа. Подключена Яндекс.Почта для отправки ссылки подтверждения почты
This commit is contained in:
parent
64cbdc1a0f
commit
13a05c68f2
@ -23,8 +23,9 @@ class User(SqlAlchemyBase, UserMixin):
|
||||
data_reg = sqlalchemy.Column(sqlalchemy.Date,
|
||||
default=date.today())
|
||||
role = sqlalchemy.Column(sqlalchemy.String, nullable=True)
|
||||
activity = sqlalchemy.Column(sqlalchemy.Date, nullable=True)
|
||||
activity = sqlalchemy.Column(sqlalchemy.DateTime, nullable=True)
|
||||
birthday = sqlalchemy.Column(sqlalchemy.Date, nullable=True)
|
||||
activated = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False, default=False)
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password, password)
|
||||
|
||||
10
forms/login.py
Normal file
10
forms/login.py
Normal file
@ -0,0 +1,10 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import EmailField, PasswordField, BooleanField, SubmitField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
email = EmailField('Почта', validators=[DataRequired()])
|
||||
password = PasswordField('Пароль', validators=[DataRequired()])
|
||||
remember_me = BooleanField('Запомнить меня')
|
||||
submit = SubmitField('Войти')
|
||||
12
forms/register.py
Normal file
12
forms/register.py
Normal file
@ -0,0 +1,12 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_wtf.file import FileAllowed
|
||||
from wtforms import EmailField, StringField, PasswordField, SubmitField, FileField, DateField, TextAreaField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
email = EmailField('Почта', validators=[DataRequired()])
|
||||
name = StringField('Имя', validators=[DataRequired()])
|
||||
login = StringField('Логин', validators=[DataRequired()])
|
||||
password = PasswordField('Пароль', validators=[DataRequired()])
|
||||
submit = SubmitField('Регистрация')
|
||||
37
functions.py
Normal file
37
functions.py
Normal file
@ -0,0 +1,37 @@
|
||||
import smtplib
|
||||
from email.message import EmailMessage
|
||||
|
||||
|
||||
def check_password(password=''):
|
||||
smb = 'qwertyuiopasdfghjklzxcvbnm'
|
||||
if len(password) < 6:
|
||||
return 'Пароль должен быть длиннее 6 символов'
|
||||
elif False in [True if i.isalpha() and i.lower() in smb or i.isdigit() else False for i in password]:
|
||||
return 'Пароль может содержать только буквы латинского алфавита и цифры'
|
||||
elif True not in [True if i.isdigit() else False for i in password]:
|
||||
return 'Пароль должен содержать буквы разного регистра и цифры'
|
||||
elif False not in [True if i.islower() and i.isalpha() else False for i in password]:
|
||||
return 'Пароль должен содержать буквы разного регистра и цифры'
|
||||
else:
|
||||
return 'OK'
|
||||
|
||||
|
||||
def mail(msg, to, topic='Подтверждение почты'):
|
||||
file = open('mail.incepted', 'r', encoding='utf-8').readline().split()
|
||||
login, password = file[0], file[1]
|
||||
email_server = "smtp.yandex.ru"
|
||||
sender = "incepted@yandex.ru"
|
||||
em = EmailMessage()
|
||||
em.set_content(msg)
|
||||
em['To'] = to
|
||||
em['From'] = sender
|
||||
em['Subject'] = topic
|
||||
mailServer = smtplib.SMTP(email_server)
|
||||
mailServer.set_debuglevel(1)
|
||||
mailServer.ehlo()
|
||||
mailServer.starttls()
|
||||
mailServer.ehlo()
|
||||
mailServer.login(login, password)
|
||||
mailServer.ehlo()
|
||||
mailServer.send_message(em)
|
||||
mailServer.quit()
|
||||
115
main.py
115
main.py
@ -1,15 +1,124 @@
|
||||
from flask import Flask, render_template
|
||||
import datetime
|
||||
|
||||
from flask import Flask, render_template, request, url_for
|
||||
from flask_login import login_user, current_user, LoginManager, logout_user, login_required
|
||||
from werkzeug.utils import redirect
|
||||
from itsdangerous import URLSafeTimedSerializer, SignatureExpired
|
||||
|
||||
from functions import check_password, mail
|
||||
from forms.login import LoginForm
|
||||
from forms.register import RegisterForm
|
||||
from data.users import User
|
||||
from waitress import serve
|
||||
from data import db_session
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'test_secret_key'
|
||||
key = 'test_secret_key'
|
||||
app.config['SECRET_KEY'] = key
|
||||
s = URLSafeTimedSerializer(key)
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def base():
|
||||
return render_template('main.html')
|
||||
return render_template('main.html', title='Главная')
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
db_sess = db_session.create_session()
|
||||
return db_sess.query(User).get(user_id)
|
||||
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if not current_user.is_authenticated:
|
||||
message = request.args.get('message') if request.args.get('message') else ''
|
||||
email_repeat = request.args.get('email_repeat') if request.args.get('email_repeat') else False
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
db_sess = db_session.create_session()
|
||||
user = db_sess.query(User).filter(User.email == form.email.data).first()
|
||||
if user and user.check_password(form.password.data):
|
||||
if user.activated:
|
||||
login_user(user, remember=form.remember_me.data)
|
||||
return redirect('/')
|
||||
else:
|
||||
return render_template('login.html',
|
||||
message="Ваша почта не подтверждена",
|
||||
form=form)
|
||||
return render_template('login.html',
|
||||
message="Неправильный логин или пароль",
|
||||
form=form)
|
||||
return render_template('login.html', title='Авторизация', form=form, message=message, email_repeat=email_repeat)
|
||||
else:
|
||||
return redirect('/')
|
||||
|
||||
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect("/")
|
||||
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if not current_user.is_authenticated:
|
||||
form = RegisterForm()
|
||||
if form.validate_on_submit():
|
||||
data_session = db_session.create_session()
|
||||
if data_session.query(User).filter(User.login == form.login.data).first():
|
||||
return render_template('register.html', form=form, message="Такой пользователь уже есть",
|
||||
title='Регистрация')
|
||||
if data_session.query(User).filter(User.email == form.email.data).first():
|
||||
return render_template('register.html', form=form, message="Такая почта уже есть", title='Регистрация')
|
||||
status_password = check_password(form.password.data)
|
||||
if status_password != 'OK':
|
||||
return render_template('register.html', form=form, message=status_password, title='Регистрация')
|
||||
user = User(
|
||||
email=form.email.data,
|
||||
name=form.name.data,
|
||||
login=form.login.data,
|
||||
activity=datetime.datetime.now()
|
||||
)
|
||||
user.set_password(form.password.data)
|
||||
data_session.add(user)
|
||||
data_session.commit()
|
||||
data_session.close()
|
||||
token = s.dumps(form.email.data)
|
||||
link_conf = url_for('confirmation', token=token, _external=True)
|
||||
mail(f'Для завершения регистрации пройдите по ссылке: {link_conf}', form.email.data,
|
||||
'Подтверждение регистрации')
|
||||
return redirect('/login?message=Мы выслали ссылку для подтверждения почты')
|
||||
return render_template('register.html', form=form, message='', title='Регистрация')
|
||||
else:
|
||||
return redirect('/')
|
||||
|
||||
|
||||
@app.route('/confirmation/<token>')
|
||||
def confirmation(token):
|
||||
try:
|
||||
user_email = s.loads(token, max_age=86400)
|
||||
data_session = db_session.create_session()
|
||||
user = data_session.query(User).filter(User.email == user_email).first()
|
||||
if user:
|
||||
user.activated = True
|
||||
data_session.commit()
|
||||
data_session.close()
|
||||
return redirect('/login?message=Почта успешно подтверждена')
|
||||
else:
|
||||
return redirect('/login?message=Пользователь не найден')
|
||||
except SignatureExpired:
|
||||
data_session = db_session.create_session()
|
||||
users = data_session.query(User).filter(
|
||||
User.activated == 0 and User.activated < datetime.datetime.now() - datetime.timedelta(days=1)).all()
|
||||
if users:
|
||||
list(map(lambda x: data_session.delete(x), users))
|
||||
data_session.commit()
|
||||
data_session.close()
|
||||
return redirect('/login?message=Срок действия ссылки истек, данные удалены')
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -1,4 +1,21 @@
|
||||
html {
|
||||
background-color: #fdf5e6;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
min-height: 100%;
|
||||
}
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
background-color: #dcb495;
|
||||
display: inline-flex;
|
||||
}
|
||||
.auth_button {
|
||||
color: #ffffff;
|
||||
font-size: 1.5vw;
|
||||
transition: font-size 0.5s ease-in, text-shadow 1s ease-in;
|
||||
}
|
||||
.auth_button:hover {
|
||||
font-size: 1.55vw;
|
||||
color: #ffffff;
|
||||
text-shadow: 0px 0px 20px #ffffff;
|
||||
}
|
||||
69
static/css/login.css
Normal file
69
static/css/login.css
Normal file
@ -0,0 +1,69 @@
|
||||
body {
|
||||
background-image: url(../images/back_main_one.jpg);
|
||||
}
|
||||
.login_page {
|
||||
background-color: #dbc3af;
|
||||
width: 90%;
|
||||
height: 55%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
margin-top: 5%;
|
||||
border-radius: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.header_title {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
transition: font-size 0.5s ease-in, text-shadow 1s ease-in;
|
||||
}
|
||||
.header_title:hover {
|
||||
font-size: 53;
|
||||
text-shadow: 0px 0px 20px #ffffff;
|
||||
}
|
||||
.login {
|
||||
width: 60%;
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.buttons_from {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.button {
|
||||
width: 150px;
|
||||
height: 35px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
font: bold;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
transition: background-color 0.5s ease-in, border-radius 1s ease-in, box-shadow 1s ease-in;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #61350f;
|
||||
border-color: #61350f;
|
||||
color: #ffffff;
|
||||
border-radius: 7px;
|
||||
box-shadow: 0px 0px 50px #fff;
|
||||
}
|
||||
.data_block {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.box {
|
||||
display: inline-flex;
|
||||
align-content: center;
|
||||
}
|
||||
.register {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
.header_block {
|
||||
background-image: url(../images/back_main_one.jpg);
|
||||
background-repeat: no-repeat;
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.header_title {
|
||||
color: #ffffff;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.486);
|
||||
font: bold;
|
||||
font-size: 3.5vw;
|
||||
transition: font-size 0.5s ease-in, text-shadow 1s ease-in;
|
||||
}
|
||||
.header_title:hover {
|
||||
font-size: 3.55vw;
|
||||
text-shadow: 0px 0px 20px #ffffff;
|
||||
}
|
||||
.header_title_2 {
|
||||
color: #ffffff;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.486);
|
||||
font: bold;
|
||||
font-size: 2vw;
|
||||
transition: font-size 0.5s ease-in, text-shadow 1s ease-in;
|
||||
}
|
||||
.header_title_2:hover {
|
||||
font-size: 2.05vw;
|
||||
text-shadow: 0px 0px 20px #ffffff;
|
||||
}
|
||||
69
static/css/register.css
Normal file
69
static/css/register.css
Normal file
@ -0,0 +1,69 @@
|
||||
body {
|
||||
background-image: url(../images/back_main_one.jpg);
|
||||
}
|
||||
.register_page {
|
||||
background-color: #dbc3af;
|
||||
width: 90%;
|
||||
height: 55%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
margin-top: 5%;
|
||||
border-radius: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.header_title {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
transition: font-size 0.5s ease-in, text-shadow 1s ease-in;
|
||||
}
|
||||
.header_title:hover {
|
||||
font-size: 53;
|
||||
text-shadow: 0px 0px 20px #ffffff;
|
||||
}
|
||||
.register {
|
||||
width: 60%;
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.buttons_from {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.button {
|
||||
width: 150px;
|
||||
height: 35px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
font: bold;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
transition: background-color 0.5s ease-in, border-radius 1s ease-in, box-shadow 1s ease-in;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #61350f;
|
||||
border-color: #61350f;
|
||||
color: #ffffff;
|
||||
border-radius: 7px;
|
||||
box-shadow: 0px 0px 50px #fff;
|
||||
}
|
||||
.data_block {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.box {
|
||||
display: inline-flex;
|
||||
align-content: center;
|
||||
}
|
||||
.register {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
BIN
static/images/back_main_one.jpg
Normal file
BIN
static/images/back_main_one.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 199 KiB |
BIN
static/images/logo_b.ico
Normal file
BIN
static/images/logo_b.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
BIN
static/images/logo_b.png
Normal file
BIN
static/images/logo_b.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
static/images/logo_w.ico
Normal file
BIN
static/images/logo_w.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 218 KiB |
BIN
static/images/logo_w.png
Normal file
BIN
static/images/logo_w.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
@ -1,21 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../static/css/base.css">
|
||||
<link rel="stylesheet"
|
||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
||||
crossorigin="anonymous">
|
||||
<meta charset="UTF-8"/>
|
||||
<link rel="stylesheet" href="../static/css/base.css"/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link rel="icon" href="../static/images/logo_b.ico" type="image/x-icon"/>
|
||||
<title>{{title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8"
|
||||
crossorigin="anonymous"></script>
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<nav class="navbar">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img
|
||||
src="../static/images/logo_b.png"
|
||||
alt="Logo"
|
||||
width="80"
|
||||
height="65"
|
||||
class="d-inline-block align-text-top"
|
||||
/>
|
||||
</a>
|
||||
{% if current_user.is_authenticated %}
|
||||
<a class="auth_button" href="/logout">Выход</a>
|
||||
{% else %}
|
||||
<a class="auth_button" href="/login">Авторизация</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
<!-- Begin page content -->
|
||||
<main role="main">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
<main role="main">{% block content %}{% endblock %}</main>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
45
templates/login.html
Normal file
45
templates/login.html
Normal file
@ -0,0 +1,45 @@
|
||||
<link rel="stylesheet" href="../static/css/login.css" />
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="login_page">
|
||||
<div class="login">
|
||||
<div><h1 class="header_title">Авторизация</h1></div>
|
||||
<div>
|
||||
<form action="" method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="data_block">
|
||||
<div>
|
||||
<label class="form-label">{{ form.email.label }}</label>
|
||||
{{ form.email(class="form-control data", type="email") }} {% for
|
||||
error in form.email.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">{{ form.password.label }}</label>
|
||||
{{ form.password(class="form-control data", type="password") }} {%
|
||||
for error in form.password.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons_from">
|
||||
<div class="box">
|
||||
{{ form.remember_me(class="form-check-input data")}} {{
|
||||
form.remember_me.label }}<br />
|
||||
{% for error in form.remember_me.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ form.submit(type="submit", class="button") }}
|
||||
<a class="button" type="submit" href="/register"
|
||||
><div class="register"><p>Регистрация</p></div></a
|
||||
>
|
||||
</div>
|
||||
{% if message != '' %}
|
||||
<div class="alert alert-danger" role="alert">{{ message }}</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
1095
templates/main.html
1095
templates/main.html
File diff suppressed because it is too large
Load Diff
49
templates/register.html
Normal file
49
templates/register.html
Normal file
@ -0,0 +1,49 @@
|
||||
<link rel="stylesheet" href="../static/css/register.css"/>
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="register_page">
|
||||
<div class="register">
|
||||
<div><h1 class="header_title">Регистрация</h1></div>
|
||||
<div>
|
||||
<form action="" method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
<div class="data_block">
|
||||
<div>
|
||||
<label class="form-label">{{ form.email.label }}</label>
|
||||
{{ form.email(class="form-control data", type="email") }} {% for
|
||||
error in form.email.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">{{ form.name.label }}</label>
|
||||
{{ form.name(class="form-control data", type="name") }} {%
|
||||
for error in form.name.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">{{ form.login.label }}</label>
|
||||
{{ form.login(class="form-control data", type="login") }} {%
|
||||
for error in form.login.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">{{ form.password.label }}</label>
|
||||
{{ form.password(class="form-control data", type="password") }} {%
|
||||
for error in form.password.errors %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons_from">
|
||||
{{ form.submit(type="submit", class="button") }}
|
||||
</div>
|
||||
{% if message != '' %}
|
||||
<div class="alert alert-danger" role="alert">{{ message }}</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user