From 98bbc486e69b6d5cd5d31d709ae7cb16d55122a3 Mon Sep 17 00:00:00 2001 From: kosarewmihail Date: Sat, 4 Apr 2026 09:21:40 +0500 Subject: [PATCH] Fix --- app.py | 83 ++++++++++++++++++++++++++++++++++++++-- horse_farm.db | Bin 45056 -> 45056 bytes templates/register.html | 27 ++++--------- 3 files changed, 87 insertions(+), 23 deletions(-) diff --git a/app.py b/app.py index 65c15f2..6a0953c 100644 --- a/app.py +++ b/app.py @@ -16,10 +16,43 @@ def get_db_connection(): return conn def is_valid_email(email): + """Проверяет корректность email адреса (только популярные почтовые домены)""" if not email: return True - pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.(ru|com)$' - return re.match(pattern, email.lower()) is not None + + # Ограничение длины + if len(email) > 50: + return False + + if '@' not in email: + return False + + local_part = email.split('@')[0] + domain = email.split('@')[1].lower() + + # Локальная часть: минимум 2 символа, не только цифры + if len(local_part) < 2 or len(local_part) > 30: + return False + + if local_part.isdigit(): + return False + + if not local_part[0].isalpha(): + return False + + # Разрешенные почтовые домены + allowed_domains = [ + 'mail.ru', 'yandex.ru', 'gmail.com', 'gmail.ru', + 'list.ru', 'bk.ru', 'inbox.ru', 'rambler.ru', + 'ya.ru', 'google.com', 'outlook.com', 'hotmail.com', + 'yahoo.com', 'protonmail.com', 'icloud.com' + ] + + # Проверяем, что домен есть в списке разрешенных + if domain not in allowed_domains: + return False + + return True def login_required(f): @wraps(f) @@ -191,15 +224,18 @@ def register(): username = request.form['username'] password = request.form['password'] email = request.form.get('email', '') + if email and not is_valid_email(email): - flash('Введите корректный email адрес (должен заканчиваться на .ru или .com)', 'danger') + flash('Введите корректный email адрес (допустимые домены: mail.ru, yandex.ru, gmail.com, list.ru, bk.ru, inbox.ru, rambler.ru, ya.ru, google.com, outlook.com, hotmail.com, yahoo.com, protonmail.com, icloud.com)', 'danger') return redirect(url_for('register')) + conn = get_db_connection() user = conn.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone() if user: flash('Пользователь с таким именем уже существует', 'danger') conn.close() return redirect(url_for('register')) + hashed_password = generate_password_hash(password) conn.execute('INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, ?)', (username, hashed_password, email if email else None, 'user')) @@ -247,17 +283,20 @@ def order(): persons = request.form.get('persons', 1) price = request.form.get('price', 0) comment = request.form.get('comment', '') + try: persons = int(float(persons)) if service_type != 'certificate' else 1 if persons < 1: persons = 1 if persons > 20: persons = 20 except: persons = 1 + conn = get_db_connection() final_price = int(price) discount_applied = False discount_amount = 0 original_price = int(price) + if service_type != 'certificate': duration_minutes = 0 duration_str = '' @@ -270,15 +309,18 @@ def order(): elif 'час' in duration_str: hours = int(duration_str.split()[0]) duration_minutes = hours * 60 + try: hour = int(time.split(':')[0]) minute = int(time.split(':')[1]) start_minutes = hour * 60 + minute end_minutes = start_minutes + duration_minutes + if start_minutes < 540: flash('Заказы принимаются с 9:00', 'danger') conn.close() return redirect(url_for('order')) + if end_minutes > 1200: max_hour = (1200 - duration_minutes) // 60 max_minute = (1200 - duration_minutes) % 60 @@ -289,15 +331,18 @@ def order(): flash('Неверный формат времени', 'danger') conn.close() return redirect(url_for('order')) + today = datetime.now().strftime('%Y-%m-%d') if date < today: flash('Нельзя выбрать прошедшую дату', 'danger') conn.close() return redirect(url_for('order')) + if persons >= 4 and service_type == 'service': discount_amount = int(int(price) * 0.15) final_price = int(int(price) * 0.85) discount_applied = True + conn.execute(''' INSERT INTO orders (user_id, service_type, service_name, service_id, date, time, persons, price, comment, discount_applied, discount_amount, original_price, payment_status) @@ -305,11 +350,13 @@ def order(): ''', (session['user_id'], service_type, service_name, service_id, date, time, persons, final_price, comment, discount_applied, discount_amount, original_price)) conn.commit() conn.close() + if discount_applied: flash(f'Заказ успешно создан! Применена семейная скидка 15%! Итоговая цена: {final_price} руб.', 'success') else: flash('Заказ успешно создан!', 'success') return redirect(url_for('my_orders')) + return render_template('order_form.html') @app.route('/my-orders') @@ -318,6 +365,7 @@ def my_orders(): sort = request.args.get('sort', 'newest') status = request.args.get('status', 'all') conn = get_db_connection() + query = 'SELECT * FROM orders WHERE user_id = ?' params = [session['user_id']] if status != 'all': @@ -331,6 +379,7 @@ def my_orders(): query += ' ORDER BY price DESC' elif sort == 'price_low': query += ' ORDER BY price ASC' + orders = conn.execute(query, params).fetchall() orders_list = [] for order in orders: @@ -341,6 +390,7 @@ def my_orders(): ''', (order['id'],)).fetchone() order_dict['unread_count'] = unread['count'] if unread else 0 orders_list.append(order_dict) + conn.close() return render_template('my_orders.html', orders=orders_list, sort=sort, status=status) @@ -351,9 +401,11 @@ def reviews(): if 'user_id' not in session: flash('Пожалуйста, войдите в систему, чтобы оставить отзыв', 'warning') return redirect(url_for('login')) + order_id = request.form.get('order_id') rating = int(request.form['rating']) comment = request.form['comment'] + conn = get_db_connection() existing_review = conn.execute('SELECT * FROM reviews WHERE user_id = ? AND order_id = ?', (session['user_id'], order_id)).fetchone() @@ -361,12 +413,14 @@ def reviews(): flash('Вы уже оставляли отзыв на этот заказ', 'danger') conn.close() return redirect(url_for('reviews')) + order = conn.execute('SELECT * FROM orders WHERE id = ? AND user_id = ? AND status = "completed"', (order_id, session['user_id'])).fetchone() if not order: flash('Вы можете оставить отзыв только на выполненные заказы', 'danger') conn.close() return redirect(url_for('reviews')) + conn.execute(''' INSERT INTO reviews (user_id, order_id, username, rating, comment, status) VALUES (?, ?, ?, ?, ?, 'pending') @@ -375,9 +429,11 @@ def reviews(): conn.close() flash('Ваш отзыв отправлен на модерацию!', 'success') return redirect(url_for('reviews')) + sort = request.args.get('sort', 'newest') rating_filter = request.args.get('rating', 'all') conn = get_db_connection() + completed_orders = [] if 'user_id' in session: completed_orders = conn.execute(''' @@ -386,6 +442,7 @@ def reviews(): WHERE o.user_id = ? AND o.status = 'completed' AND r.id IS NULL ORDER BY o.created_at DESC ''', (session['user_id'], session['user_id'])).fetchall() + query = 'SELECT * FROM reviews WHERE status = "approved"' params = [] if rating_filter != 'all': @@ -399,6 +456,7 @@ def reviews(): query += ' ORDER BY rating DESC' elif sort == 'rating_low': query += ' ORDER BY rating ASC' + approved_reviews = conn.execute(query, params).fetchall() conn.close() return render_template('reviews.html', reviews=approved_reviews, @@ -417,8 +475,10 @@ def chat(order_id): if session['user_id'] != order['user_id'] and session.get('role') != 'admin': flash('Доступ запрещен', 'danger') return redirect(url_for('index')) + if session.get('role') == 'admin': conn.execute('UPDATE messages SET is_read = 1 WHERE order_id = ? AND sender_type = "user"', (order_id,)) + messages = conn.execute(''' SELECT m.*, CASE @@ -442,6 +502,7 @@ def send_message(): message = request.form['message'] if not message or not message.strip(): return jsonify({'error': 'Сообщение не может быть пустым'}), 400 + conn = get_db_connection() order = conn.execute('SELECT * FROM orders WHERE id = ?', (order_id,)).fetchone() if not order: @@ -450,6 +511,7 @@ def send_message(): if session['user_id'] != order['user_id'] and session.get('role') != 'admin': conn.close() return jsonify({'error': 'Доступ запрещен'}), 403 + if session.get('role') == 'admin': sender_type = 'admin' admin_id = session['user_id'] @@ -458,6 +520,7 @@ def send_message(): sender_type = 'user' user_id = session['user_id'] admin_id = None + conn.execute(''' INSERT INTO messages (order_id, user_id, admin_id, message, sender_type) VALUES (?, ?, ?, ?, ?) @@ -497,6 +560,7 @@ def admin_orders(): query += ' ORDER BY o.price DESC' elif sort == 'price_low': query += ' ORDER BY o.price ASC' + orders = conn.execute(query, params).fetchall() conn.close() return render_template('admin/orders.html', orders=orders, sort=sort, status=status) @@ -556,6 +620,7 @@ def admin_reviews(): query += ' ORDER BY rating DESC' elif sort == 'rating_low': query += ' ORDER BY rating ASC' + reviews = conn.execute(query, params).fetchall() conn.close() return render_template('admin/reviews.html', reviews=reviews, sort=sort, status=status, rating=rating) @@ -606,6 +671,7 @@ def admin_users(): query += ' ORDER BY username ASC' elif sort == 'name_desc': query += ' ORDER BY username DESC' + users = conn.execute(query, params).fetchall() conn.close() return render_template('admin/users.html', users=users, sort=sort, role=role) @@ -659,18 +725,25 @@ def admin_messages(): if __name__ == '__main__': conn = sqlite3.connect('horse_farm.db') cursor = conn.cursor() + cursor.execute("PRAGMA table_info(orders)") existing = [col[1] for col in cursor.fetchall()] for col in ['discount_applied', 'discount_amount', 'original_price', 'payment_status']: if col not in existing: - cursor.execute(f"ALTER TABLE orders ADD COLUMN {col} DEFAULT 'not_paid' if col == 'payment_status' else 0") + if col == 'payment_status': + cursor.execute(f"ALTER TABLE orders ADD COLUMN {col} TEXT DEFAULT 'not_paid'") + else: + cursor.execute(f"ALTER TABLE orders ADD COLUMN {col} INTEGER DEFAULT 0") print(f"Добавлена колонка {col}") + cursor.execute("PRAGMA table_info(reviews)") if 'order_id' not in [c[1] for c in cursor.fetchall()]: cursor.execute("ALTER TABLE reviews ADD COLUMN order_id INTEGER") + cursor.execute("PRAGMA table_info(messages)") if 'is_read' not in [c[1] for c in cursor.fetchall()]: cursor.execute("ALTER TABLE messages ADD COLUMN is_read BOOLEAN DEFAULT 0") + cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='messages'") if not cursor.fetchone(): cursor.execute(''' @@ -685,12 +758,14 @@ if __name__ == '__main__': is_read BOOLEAN DEFAULT 0 ) ''') + cursor.execute("SELECT * FROM users WHERE username = 'admin'") if not cursor.fetchone(): hashed = generate_password_hash('admin123') cursor.execute("INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, ?)", ('admin', hashed, 'admin@koniydvor.ru', 'admin')) print("Создан администратор: admin / admin123") + conn.commit() conn.close() app.run(debug=True) diff --git a/horse_farm.db b/horse_farm.db index 9c472f0e7c911751f2317202c6f78eaf4153b661..b06f84c16c52291fc8d7d5154b3e3b9b1e842213 100644 GIT binary patch delta 454 zcmZp8z|`=7X@WH4uZc3wjK4M}nCnXzbFnc?GUgYhq!ty6ankww zZ@!>!q9DP{o58@Z!sp7H!84uvFxOiyIZiM3RqU%aD+)|tXJTBic_Qb0M#d|XAF^L& zJP#z#F71Gf%?|en$O?je3j*6B~8B;Bt%k-MD)} zko^UD5NP{6b{6he%u9i+#mr1B#Xx!oGh^(;#uyM0$-@P6$4~xGn|T&I;|GNcQfL4G D_ke=S literal 45056 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 @@ -98,18 +87,18 @@
- +
- - Формат: name@domain.ru + + Максимум 50 символов, локальная часть (до @) - до 20 символов
- +