Основной код проекта - nemopss/mpt-kpi GitHub Wiki
В данном файле routes.py определяются маршруты (routes) приложения Flask, которые используются для обработки HTTP-запросов. Он обеспечивает функциональность работы с пользователями и позициями, реализуя обработку запросов на получение и создание ресурсов, а также контроль доступа с помощью проверки административных прав текущего пользователя.
from flask import Blueprint, jsonify, request
from flask_login import current_user, login_required
from extensions import db
from models import Position, User
bp = Blueprint("main", __name__)
@bp.route("/users", methods=["GET", "POST"])
@login_required
def users():
if request.method == "GET":
users = User.query.all()
return jsonify([{"id": u.id, "email": u.email} for u in users])
elif request.method == "POST":
if not current_user.is_admin:
return jsonify({"error": "Access denied"}), 403
data = request.json
user = User(
first_name=data["first_name"],
last_name=data["last_name"],
email=data["email"],
)
db.session.add(user)
db.session.commit()
return jsonify({"message": "User created"}), 201
@bp.route("/positions", methods=["GET", "POST"])
@login_required
def positions():
if not current_user.is_admin:
return jsonify({"error": "Access denied"}), 403
if request.method == "GET":
positions = Position.query.all()
return jsonify([{"id": p.id, "name": p.name} for p in positions])
elif request.method == "POST":
data = request.json
position = Position(name=data["name"], score_threshold=data["score_threshold"])
db.session.add(position)
db.session.commit()
return jsonify({"message": "Position created"}), 201
Импортируемые модули
from flask import Blueprint, jsonify, request
from flask_login import current_user, login_required
from extensions import db
from models import Position, User
• Blueprint: Используется для создания модульной структуры приложения Flask.
• jsonify: Функция для преобразования Python-объектов в JSON-формат.
• request: Объект, который содержит данные текущего HTTP-запроса.
• current_user: Представляет текущего аутентифицированного пользователя.
• login_required: Декоратор, который гарантирует, что маршруты доступны только для аутентифицированных пользователей.
• db: Расширенный объект базы данных для работы с SQLAlchemy.
• User и Position: Модели, представляющие пользователей и позиции в базе данных соответственно.
Инициализация Blueprint
bp = Blueprint("main", __name__)
• Создается объект Blueprint с именем "main", который будет использоваться для группировки связанных маршрутов.
Маршрут /users
Метод GET
@bp.route("/users", methods=["GET", "POST"])
@login_required
def users():
if request.method == "GET":
users = User.query.all()
return jsonify([{"id": u.id, "email": u.email} for u in users])
• Этот маршрут обрабатывает запросы для получения списка пользователей. При выполнении GET-запроса возвращается список всех пользователей с их идентификаторами и электронными адресами в формате JSON.
Метод POST
elif request.method == "POST":
if not current_user.is_admin:
return jsonify({"error": "Access denied"}), 403
data = request.json
user = User(
first_name=data["first_name"],
last_name=data["last_name"],
email=data["email"],
)
db.session.add(user)
db.session.commit()
return jsonify({"message": "User created"}), 201
• Этот маршрут позволяет создавать новых пользователей. Доступ к этому маршруту имеют только администраторы. При выполнении POST-запроса требуется передать данные нового пользователя в формате JSON. Если текущий пользователь не является администратором, возвращается ошибка доступа.
Маршрут /positions
Метод GET
@bp.route("/positions", methods=["GET", "POST"])
@login_required
def positions():
if not current_user.is_admin:
return jsonify({"error": "Access denied"}), 403
if request.method == "GET":
positions = Position.query.all()
return jsonify([{"id": p.id, "name": p.name} for p in positions])
• Этот маршрут обрабатывает запросы для получения списка позиций. Доступен только для администраторов. При выполнении GET-запроса возвращает список всех позиций с их идентификаторами и названиями в формате JSON.
Метод POST
elif request.method == "POST":
data = request.json
position = Position(name=data["name"], score_threshold=data["score_threshold"])
db.session.add(position)
db.session.commit()
return jsonify({"message": "Position created"}), 201
• Этот маршрут позволяет создавать новые позиции. При выполнении POST-запроса требуется передать данные о новой позиции в формате JSON. Позволяет добавить название позиции и пороговое значение баллов.
В файле main.py осуществляется настройка и инициализация основного приложения Flask, который представляет собой ядро приложения и отвечает за его функционирование и доступность. Здесь реализована конфигурация для работы с RESTful API, инициализация необходимых расширений, регистрация маршрутов и запуск сервера.
from flask import Flask, render_template
from flask_cors import CORS
from flask_restful import Api
from os import environ
from config import Config
from extensions import db, migrate, login_manager, mail
from resources.basic_resource import BasicResource
from resources.pdf_resource import PDFResource
from resources.employees_resource import EmployeesResource
from resources.criteries_resource import CriteriesResource
from resources.certificates_resource import CertificatesResource
from auth import auth_bp
from routes import bp as main_bp
PORT = environ.get('BACKEND_PORT', 8000)
DEBUG = environ.get('DEBUG', True)
app = Flask(__name__)
app.config.from_object(Config)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024 # 5 MB limit for uploads
api = Api(app)
api.add_resource(BasicResource, '/api', '/api/<int:source_id>')
api.add_resource(PDFResource, '/api/media/', '/api/media/<string:filename>')
api.add_resource(EmployeesResource, '/api/employees/', '/api/employees/')
api.add_resource(CriteriesResource, '/api/criteries/', '/api/criteries/')
api.add_resource(CertificatesResource, '/api/certificates/', '/api/certificates/')
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)
mail.init_app(app)
# Set up login behavior
@login_manager.user_loader
def load_user(user_id):
from backend.models import User
return User.query.get(int(user_id))
login_manager.login_view = "auth.login"
login_manager.login_message = "Please log in to access this page."
# Register blueprints
app.register_blueprint(main_bp)
app.register_blueprint(auth_bp)
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
PORT = environ.get('BACKEND_PORT', 8000)
DEBUG = environ.get('DEBUG', True)
app.run(extra_files=[], debug=DEBUG, host='0.0.0.0', port=PORT)
Импортируемые модули
from flask import Flask, render_template
from flask_cors import CORS
from flask_restful import Api
from os import environ
from config import Config
from extensions import db, migrate, login_manager, mail
from resources.basic_resource import BasicResource
from resources.pdf_resource import PDFResource
from resources.employees_resource import EmployeesResource
from resources.criteries_resource import CriteriesResource
from resources.certificates_resource import CertificatesResource
from auth import auth_bp
from routes import bp as main_bp
• Flask: Основной класс приложения Flask.
• render_template: Функция для рендеринга HTML-шаблонов.
• CORS: Расширение для настройки кросс-доменных запросов.
• Api: Класс для создания RESTful API с использованием Flask-RESTful.
• environ: Модуль для работы с переменными окружения.
Конфигурация приложения
PORT = environ.get('BACKEND_PORT', 8000)
DEBUG = environ.get('DEBUG', True)
app = Flask(__name__)
app.config.from_object(Config)
• Переменные окружения BACKEND_PORT и DEBUG используются для настройки порта, на котором будет запущен сервер, и режима отладки.
• Конфигурация приложения загружается из класса Config.
Настройка CORS
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024 # 5 MB limit for uploads
• CORS: Настраивает поддержку кросс-доменных запросов для приложения.
• Устанавливается заголовок Content-Type и ограничение на размер загружаемых данных (5 MB).
Регистрация RESTful ресурсов
api = Api(app)
api.add_resource(BasicResource, '/api', '/api/<int:source_id>')
api.add_resource(PDFResource, '/api/media/', '/api/media/<string:filename>')
api.add_resource(EmployeesResource, '/api/employees/', '/api/employees/')
api.add_resource(CriteriesResource, '/api/criteries/', '/api/criteries/')
api.add_resource(CertificatesResource, '/api/certificates/', '/api/certificates/')
• Создается объект Api, который регистрирует ресурсы с соответствующими маршрутами для обработки запросов.
Инициализация расширений
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)
mail.init_app(app)
• Расширения (такие как db, migrate, login_manager, и mail) инициализируются с использованием экземпляра приложения app.
Настройка пользовательской аутентификации
@login_manager.user_loader
def load_user(user_id):
from backend.models import User
return User.query.get(int(user_id))
login_manager.login_view = "auth.login"
login_manager.login_message = "Please log in to access this page."
• Функция load_user загружает пользователя из базы данных по его идентификатору.
• Устанавливаются параметры для управления поведением аутентификации.
Регистрация Blueprints
# Register blueprints
app.register_blueprint(main_bp)
app.register_blueprint(auth_bp)
• Регистрируются ранее созданные Blueprint для основной логики приложения (main_bp) и аутентификации (auth_bp).
Главная Страница
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
• Определяется маршрут для главной страницы, которая рендерит шаблон index.html.
Запуск приложения
PORT = environ.get('BACKEND_PORT', 8000)
DEBUG = environ.get('DEBUG', True)
app.run(extra_files=[], debug=DEBUG, host='0.0.0.0', port=PORT)
• Приложение запускается на заданном порту и в заданном режиме отладки. Установлен хост 0.0.0.0, что позволяет приложению быть доступным извне.
Файл auth.py реализует функционал аутентификации пользователей в приложении Flask и содержит маршруты, необходимые для входа в систему, выхода из нее и сброса пароля через отправку электронных писем.
from flask import Blueprint, jsonify, request, url_for
from flask_login import login_user, logout_user
from flask_mail import Message
from extensions import db, mail
from models import User
auth_bp = Blueprint("auth", __name__)
@auth_bp.route("/login", methods=["POST"])
def login():
data = request.json
user = User.query.filter_by(email=data["email"]).first()
if user and user.check_password(data["password"]):
login_user(user)
return jsonify({"message": "Login successful"}), 200
return jsonify({"error": "Invalid credentials"}), 401
@auth_bp.route("/logout", methods=["POST"])
def logout():
logout_user()
return jsonify({"message": "Logout successful"}), 200
@auth_bp.route("/reset_password", methods=["POST"])
def reset_password():
data = request.json
user = User.query.filter_by(email=data["email"]).first()
if not user:
return jsonify({"error": "User not found"}), 404
token = user.id # Using ID as an example token
reset_url = url_for("auth.complete_reset", token=token, _external=True)
msg = Message("Password Reset Request", recipients=[user.email])
msg.body = f"To reset your password, visit the following link: {reset_url}"
mail.send(msg)
return jsonify({"message": "Reset link sent"}), 200
@auth_bp.route("/reset_password/<token>", methods=["POST"])
def complete_reset(token):
user = User.query.get(int(token))
if not user:
return jsonify({"error": "Invalid token"}), 400
data = request.json
user.set_password(data["password"])
db.session.commit()
return jsonify({"message": "Password reset successful"}), 200
Импортируемые модули
from flask import Blueprint, jsonify, request, url_for
from flask_login import login_user, logout_user
from flask_mail import Message
from extensions import db, mail
from models import User
• Blueprint: Используется для создания модуля аутентификации.
• jsonify: Функция для преобразования данных в JSON-формат.
• request: Объект, представляющий текущий HTTP-запрос.
• url_for: Функция для генерации URL-адресов по имени маршрута.
• login_user, logout_user: Функции для управления сессиями пользователей.
• Message: Класс для создания и отправки электронных писем.
Инициализация Blueprint
auth_bp = Blueprint("main", __name__)
• Создается объект Blueprint с именем "auth", который будет группировать маршруты, относящиеся к аутентификации.
Маршрут /login
@auth_bp.route("/login", methods=["POST"])
def login():
data = request.json
user = User.query.filter_by(email=data["email"]).first()
if user and user.check_password(data["password"]):
login_user(user)
return jsonify({"message": "Login successful"}), 200
return jsonify({"error": "Invalid credentials"}), 401
• Этот маршрут обрабатывает POST-запросы для входа пользователя в систему. Сначала происходит проверка существования пользователя по электронной почте, а затем проверяется правильность введенного пароля. В случае успешной аутентификации, пользователь будет залогинен и получит сообщение об успешном входе. Если данные некорректны, возвращается ошибка 401.
Маршрут /logout
@auth_bp.route("/logout", methods=["POST"])
def logout():
logout_user()
return jsonify({"message": "Logout successful"}), 200
• Этот маршрут обрабатывает POST-запросы для выхода пользователя из системы. Пользователь выйдет из аккаунта, и на ответ будет отправлено сообщение об успешном выходе.
Маршрут /reset_password
@auth_bp.route("/reset_password", methods=["POST"])
def reset_password():
data = request.json
user = User.query.filter_by(email=data["email"]).first()
if not user:
return jsonify({"error": "User not found"}), 404
token = user.id # Using ID as an example token
reset_url = url_for("auth.complete_reset", token=token, _external=True)
msg = Message("Password Reset Request", recipients=[user.email])
msg.body = f"To reset your password, visit the following link: {reset_url}"
mail.send(msg)
return jsonify({"message": "Reset link sent"}), 200
• Этот маршрут обрабатывает запросы на сброс пароля. На основе переданного адреса электронной почты ищется пользователь. Если пользователь не найден, возвращается ошибка 404. Если пользователь существует, создается временный токен (в данном случае используется идентификатор пользователя) и формируется URL для сброса пароля. На указанный адрес электронной почты отправляется сообщение с инструкцией по сбросу пароля.
Маршрут /reset_password/
@auth_bp.route("/reset_password/<token>", methods=["POST"])
def complete_reset(token):
user = User.query.get(int(token))
if not user:
return jsonify({"error": "Invalid token"}), 400
data = request.json
user.set_password(data["password"])
db.session.commit()
return jsonify({"message": "Password reset successful"}), 200
• Этот маршрут обрабатывает запросы для завершения процесса сброса пароля. Проверяется корректность токена (идентификатора пользователя). Если токен некорректный, возвращается ошибка 400. В случае корректного токена, новый пароль устанавливается для пользователя, и изменения сохраняются в базе данных.