Bladeren bron

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

Andrei 3 jaren geleden
bovenliggende
commit
13a05c68f2

+ 2 - 1
data/users.py

@@ -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 - 0
forms/login.py

@@ -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 - 0
forms/register.py

@@ -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 - 0
functions.py

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

+ 112 - 3
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():

+ 19 - 2
static/css/base.css

@@ -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 - 0
static/css/login.css

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

+ 33 - 0
static/css/main.css

@@ -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 - 0
static/css/register.css

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


BIN
static/images/logo_b.ico


BIN
static/images/logo_b.png


BIN
static/images/logo_w.ico


BIN
static/images/logo_w.png


+ 0 - 0
static/main_files/заглушка


+ 33 - 12
templates/base.html

@@ -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 - 0
templates/login.html

@@ -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
+ 0 - 0
templates/main.html


+ 49 - 0
templates/register.html

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

Some files were not shown because too many files changed in this diff