Создана основа главной страницы, страницы регистрации и входа. Подключена Яндекс.Почта для отправки ссылки подтверждения почты

This commit is contained in:
Andrei 2022-12-05 23:05:36 +05:00
parent 64cbdc1a0f
commit 13a05c68f2
19 changed files with 494 additions and 1109 deletions

View File

@ -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
View 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
View 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
View 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
View File

@ -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():

View File

@ -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
View 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;
}

View File

@ -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
View 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -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
View 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 %}

File diff suppressed because it is too large Load Diff

49
templates/register.html Normal file
View 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 %}