import os from flask import Flask, render_template, request, redirect, url_for, flash, session from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime, date from dotenv import load_dotenv from functools import wraps load_dotenv() app = Flask(__name__) app.secret_key = os.getenv('SECRET_KEY', 'supersecretkey') app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', 'postgresql://user:password@localhost/finance_db') app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) # Модель пользователя class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password = db.Column(db.String(200), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) def set_password(self, password): self.password = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password, password) # Модель транзакции (обновлена с привязкой к пользователю) class Transaction(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) type = db.Column(db.String(10), nullable=False) amount = db.Column(db.Float, nullable=False) category = db.Column(db.String(50), nullable=False) date = db.Column(db.Date, nullable=False, default=date.today()) description = db.Column(db.Text) user = db.relationship('User', backref=db.backref('transactions', lazy=True)) # Декоратор для проверки аутентификации def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'user_id' not in session: flash('Пожалуйста, войдите в систему для доступа к этой странице', 'warning') return redirect(url_for('login')) return f(*args, **kwargs) return decorated_function # Регистрация пользователя @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': username = request.form['username'] email = request.form['email'] password = request.form['password'] confirm_password = request.form['confirm_password'] # Проверки if password != confirm_password: flash('Пароли не совпадают', 'danger') return redirect(url_for('register')) if User.query.filter_by(username=username).first(): flash('Это имя пользователя уже занято', 'danger') return redirect(url_for('register')) if User.query.filter_by(email=email).first(): flash('Этот email уже используется', 'danger') return redirect(url_for('register')) # Создание пользователя new_user = User(username=username, email=email) new_user.set_password(password) db.session.add(new_user) db.session.commit() flash('Регистрация успешна! Теперь вы можете войти', 'success') return redirect(url_for('login')) return render_template('register.html') # Вход в систему @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] remember = 'remember' in request.form user = User.query.filter_by(username=username).first() if user and user.check_password(password): session['user_id'] = user.id session['username'] = user.username flash('Вы успешно вошли в систему!', 'success') return redirect(url_for('index')) flash('Неверное имя пользователя или пароль', 'danger') return render_template('login.html') # Выход из системы @app.route('/logout') @login_required def logout(): session.pop('user_id', None) session.pop('username', None) flash('Вы успешно вышли из системы', 'info') return redirect(url_for('login')) # Главная страница (требует аутентификации) @app.route('/') @login_required def index(): user_id = session['user_id'] transactions = Transaction.query.filter_by(user_id=user_id).order_by(Transaction.date.desc()).all() total_income = sum(t.amount for t in transactions if t.type == 'income') total_expense = sum(t.amount for t in transactions if t.type == 'expense') balance = total_income - total_expense categories = {} for t in transactions: if t.category not in categories: categories[t.category] = {'income': 0, 'expense': 0} categories[t.category][t.type] += t.amount return render_template('index.html', transactions=transactions, total_income=total_income, total_expense=total_expense, balance=balance, categories=categories, current_date=date.today(), username=session['username']) # Остальные роуты (add, edit, delete) аналогично защищаем @login_required # и добавляем user_id при создании/редактировании транзакций @app.route('/add', methods=['GET', 'POST']) @login_required def add_transaction(): if request.method == 'POST': user_id = session['user_id'] # ... остальной код ... new_transaction = Transaction( user_id=user_id, # Добавляем привязку к пользователю # ... остальные поля ... ) # ... сохранение ... return render_template('add.html', current_date=date.today()) # Аналогично для edit и delete добавляем проверку, что пользователь редактирует свою транзакцию if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True)