697 lines
28 KiB
Python
697 lines
28 KiB
Python
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify
|
||
import sqlite3
|
||
import os
|
||
import re
|
||
from werkzeug.security import generate_password_hash, check_password_hash
|
||
from functools import wraps
|
||
from datetime import datetime
|
||
|
||
app = Flask(__name__)
|
||
app.secret_key = 'your_secret_key_here_change_in_production'
|
||
|
||
# --------------------- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ---------------------
|
||
def get_db_connection():
|
||
conn = sqlite3.connect('horse_farm.db')
|
||
conn.row_factory = sqlite3.Row
|
||
return conn
|
||
|
||
def is_valid_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
|
||
|
||
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
|
||
|
||
def admin_required(f):
|
||
@wraps(f)
|
||
def decorated_function(*args, **kwargs):
|
||
if 'user_id' not in session:
|
||
flash('Пожалуйста, войдите в систему', 'warning')
|
||
return redirect(url_for('login'))
|
||
if session.get('role') != 'admin':
|
||
flash('Доступ запрещен. Требуются права администратора.', 'danger')
|
||
return redirect(url_for('index'))
|
||
return f(*args, **kwargs)
|
||
return decorated_function
|
||
|
||
# ------------------------- API -------------------------
|
||
@app.route('/api/services')
|
||
def api_services():
|
||
conn = get_db_connection()
|
||
services = conn.execute('SELECT id, name, price, duration FROM services').fetchall()
|
||
abonements = conn.execute('SELECT id, name, price, description FROM abonements').fetchall()
|
||
certificates = conn.execute('SELECT id, name, price, description FROM certificates').fetchall()
|
||
conn.close()
|
||
return jsonify({
|
||
'services': [dict(row) for row in services],
|
||
'abonements': [dict(row) for row in abonements],
|
||
'certificates': [dict(row) for row in certificates]
|
||
})
|
||
|
||
@app.route('/api/unread_count')
|
||
@login_required
|
||
def api_unread_count():
|
||
conn = get_db_connection()
|
||
count = conn.execute('''
|
||
SELECT COUNT(*) as count FROM messages m
|
||
JOIN orders o ON m.order_id = o.id
|
||
WHERE o.user_id = ? AND m.is_read = 0 AND m.sender_type != 'user'
|
||
''', (session['user_id'],)).fetchone()
|
||
conn.close()
|
||
return jsonify({'count': count['count'] if count else 0})
|
||
|
||
@app.route('/api/unread_by_order')
|
||
@login_required
|
||
def api_unread_by_order():
|
||
conn = get_db_connection()
|
||
unread = conn.execute('''
|
||
SELECT o.id, COUNT(*) as count
|
||
FROM messages m
|
||
JOIN orders o ON m.order_id = o.id
|
||
WHERE o.user_id = ? AND m.is_read = 0 AND m.sender_type != 'user'
|
||
GROUP BY o.id
|
||
''', (session['user_id'],)).fetchall()
|
||
conn.close()
|
||
return jsonify([dict(row) for row in unread])
|
||
|
||
@app.route('/api/mark_read', methods=['POST'])
|
||
@login_required
|
||
def mark_read():
|
||
data = request.get_json()
|
||
order_id = data.get('order_id')
|
||
conn = get_db_connection()
|
||
conn.execute('''
|
||
UPDATE messages SET is_read = 1
|
||
WHERE order_id = ? AND user_id = ? AND sender_type != 'user'
|
||
''', (order_id, session['user_id']))
|
||
conn.commit()
|
||
conn.close()
|
||
return jsonify({'success': True})
|
||
|
||
@app.route('/api/set_timezone', methods=['POST'])
|
||
def set_timezone():
|
||
data = request.get_json()
|
||
if data and 'timezone_offset' in data:
|
||
session['timezone_offset'] = data['timezone_offset']
|
||
return jsonify({'success': True})
|
||
return jsonify({'success': False}), 400
|
||
|
||
@app.route('/api/admin_unread_counts')
|
||
@admin_required
|
||
def api_admin_unread_counts():
|
||
conn = get_db_connection()
|
||
counts = conn.execute('''
|
||
SELECT order_id, COUNT(*) as count
|
||
FROM messages
|
||
WHERE is_read = 0 AND sender_type = 'user'
|
||
GROUP BY order_id
|
||
''').fetchall()
|
||
conn.close()
|
||
return jsonify({str(row['order_id']): row['count'] for row in counts})
|
||
|
||
@app.route('/api/mark_order_read', methods=['POST'])
|
||
@admin_required
|
||
def mark_order_read():
|
||
data = request.get_json()
|
||
order_id = data.get('order_id')
|
||
conn = get_db_connection()
|
||
conn.execute('UPDATE messages SET is_read = 1 WHERE order_id = ? AND sender_type = "user"', (order_id,))
|
||
conn.commit()
|
||
conn.close()
|
||
return jsonify({'success': True})
|
||
|
||
@app.route('/api/toggle_payment', methods=['POST'])
|
||
@admin_required
|
||
def toggle_payment():
|
||
data = request.get_json()
|
||
order_id = data.get('order_id')
|
||
conn = get_db_connection()
|
||
current = conn.execute('SELECT payment_status FROM orders WHERE id = ?', (order_id,)).fetchone()
|
||
new_status = 'paid' if current['payment_status'] != 'paid' else 'not_paid'
|
||
conn.execute('UPDATE orders SET payment_status = ? WHERE id = ?', (new_status, order_id))
|
||
conn.commit()
|
||
conn.close()
|
||
return jsonify({'status': new_status})
|
||
|
||
# ---------------------- СТРАНИЦЫ ----------------------
|
||
@app.route('/')
|
||
def index():
|
||
return render_template('index.html')
|
||
|
||
@app.route('/about')
|
||
def about():
|
||
return render_template('about.html')
|
||
|
||
@app.route('/services')
|
||
def services():
|
||
conn = get_db_connection()
|
||
services = conn.execute('SELECT * FROM services').fetchall()
|
||
conn.close()
|
||
return render_template('services.html', services=services)
|
||
|
||
@app.route('/abonements')
|
||
def abonements():
|
||
conn = get_db_connection()
|
||
abonements = conn.execute('SELECT * FROM abonements').fetchall()
|
||
conn.close()
|
||
return render_template('abonements.html', abonements=abonements)
|
||
|
||
@app.route('/certificates')
|
||
def certificates():
|
||
conn = get_db_connection()
|
||
certificates = conn.execute('SELECT * FROM certificates').fetchall()
|
||
conn.close()
|
||
return render_template('certificates.html', certificates=certificates)
|
||
|
||
@app.route('/gallery')
|
||
def gallery():
|
||
images_dir = os.path.join(app.static_folder, 'images/gallery')
|
||
images = []
|
||
if os.path.exists(images_dir):
|
||
images = [f for f in os.listdir(images_dir)
|
||
if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp'))]
|
||
return render_template('gallery.html', images=images)
|
||
|
||
@app.route('/discounts')
|
||
def discounts():
|
||
return render_template('discounts.html')
|
||
|
||
# ---------------------- РЕГИСТРАЦИЯ / ВХОД ----------------------
|
||
@app.route('/register', methods=['GET', 'POST'])
|
||
def register():
|
||
if request.method == 'POST':
|
||
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')
|
||
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'))
|
||
conn.commit()
|
||
conn.close()
|
||
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']
|
||
conn = get_db_connection()
|
||
user = conn.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone()
|
||
conn.close()
|
||
if user and check_password_hash(user['password'], password):
|
||
session.permanent = True
|
||
session['user_id'] = user['id']
|
||
session['username'] = user['username']
|
||
session['role'] = user['role']
|
||
flash(f'Добро пожаловать, {username}!', 'success')
|
||
return redirect(url_for('index'))
|
||
else:
|
||
flash('Неверное имя пользователя или пароль', 'danger')
|
||
return render_template('login.html')
|
||
|
||
@app.route('/logout')
|
||
def logout():
|
||
session.clear()
|
||
flash('Вы вышли из системы', 'info')
|
||
return redirect(url_for('index'))
|
||
|
||
# ---------------------- ЗАКАЗЫ ----------------------
|
||
@app.route('/order', methods=['GET', 'POST'])
|
||
@login_required
|
||
def order():
|
||
if request.method == 'POST':
|
||
service_type = request.form['service_type']
|
||
service_id = request.form.get('service_id')
|
||
service_name = request.form['service_name']
|
||
date = request.form.get('date')
|
||
time = request.form.get('time')
|
||
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 = ''
|
||
if service_type == 'service':
|
||
service = conn.execute('SELECT duration FROM services WHERE id = ?', (service_id,)).fetchone()
|
||
if service:
|
||
duration_str = service['duration']
|
||
if 'минут' in duration_str:
|
||
duration_minutes = int(duration_str.split()[0])
|
||
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
|
||
flash(f'Услуга длится {duration_str}. Необходимо закончить до 20:00. Максимальное время начала: {max_hour}:{max_minute:02d}', 'danger')
|
||
conn.close()
|
||
return redirect(url_for('order'))
|
||
except:
|
||
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)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'not_paid')
|
||
''', (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')
|
||
@login_required
|
||
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':
|
||
query += ' AND status = ?'
|
||
params.append(status)
|
||
if sort == 'newest':
|
||
query += ' ORDER BY created_at DESC'
|
||
elif sort == 'oldest':
|
||
query += ' ORDER BY created_at ASC'
|
||
elif sort == 'price_high':
|
||
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:
|
||
order_dict = dict(order)
|
||
unread = conn.execute('''
|
||
SELECT COUNT(*) as count FROM messages
|
||
WHERE order_id = ? AND is_read = 0 AND sender_type != 'user'
|
||
''', (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)
|
||
|
||
# ---------------------- ОТЗЫВЫ ----------------------
|
||
@app.route('/reviews', methods=['GET', 'POST'])
|
||
def reviews():
|
||
if request.method == 'POST':
|
||
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()
|
||
if existing_review:
|
||
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')
|
||
''', (session['user_id'], order_id, session['username'], rating, comment))
|
||
conn.commit()
|
||
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('''
|
||
SELECT o.* FROM orders o
|
||
LEFT JOIN reviews r ON o.id = r.order_id AND r.user_id = ?
|
||
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':
|
||
query += ' AND rating = ?'
|
||
params.append(rating_filter)
|
||
if sort == 'newest':
|
||
query += ' ORDER BY created_at DESC'
|
||
elif sort == 'oldest':
|
||
query += ' ORDER BY created_at ASC'
|
||
elif sort == 'rating_high':
|
||
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,
|
||
completed_orders=completed_orders,
|
||
sort=sort, rating_filter=rating_filter)
|
||
|
||
# ---------------------- ЧАТ ----------------------
|
||
@app.route('/chat/<int:order_id>')
|
||
@login_required
|
||
def chat(order_id):
|
||
conn = get_db_connection()
|
||
order = conn.execute('SELECT * FROM orders WHERE id = ?', (order_id,)).fetchone()
|
||
if not order:
|
||
flash('Заказ не найден', 'danger')
|
||
return redirect(url_for('my_orders'))
|
||
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
|
||
WHEN m.sender_type = 'user' THEN u.username
|
||
WHEN m.sender_type = 'admin' THEN a.username
|
||
ELSE 'Система'
|
||
END as sender_name
|
||
FROM messages m
|
||
LEFT JOIN users u ON m.user_id = u.id
|
||
LEFT JOIN users a ON m.admin_id = a.id
|
||
WHERE m.order_id = ?
|
||
ORDER BY m.created_at ASC
|
||
''', (order_id,)).fetchall()
|
||
conn.close()
|
||
return render_template('chat.html', order=order, messages=messages)
|
||
|
||
@app.route('/send_message', methods=['POST'])
|
||
@login_required
|
||
def send_message():
|
||
order_id = request.form['order_id']
|
||
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:
|
||
conn.close()
|
||
return jsonify({'error': 'Заказ не найден'}), 404
|
||
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']
|
||
user_id = order['user_id']
|
||
else:
|
||
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 (?, ?, ?, ?, ?)
|
||
''', (order_id, user_id, admin_id, message.strip(), sender_type))
|
||
conn.commit()
|
||
conn.close()
|
||
return jsonify({'success': True, 'message': 'Сообщение отправлено'})
|
||
|
||
# ---------------------- АДМИНКА ----------------------
|
||
@app.route('/admin')
|
||
@admin_required
|
||
def admin_panel():
|
||
return render_template('admin/index.html')
|
||
|
||
@app.route('/admin/orders')
|
||
@admin_required
|
||
def admin_orders():
|
||
sort = request.args.get('sort', 'newest')
|
||
status = request.args.get('status', 'all')
|
||
conn = get_db_connection()
|
||
query = '''
|
||
SELECT o.*, u.username,
|
||
(SELECT COUNT(*) FROM messages m WHERE m.order_id = o.id AND m.is_read = 0 AND m.sender_type != 'admin') as unread_count
|
||
FROM orders o
|
||
LEFT JOIN users u ON o.user_id = u.id
|
||
WHERE 1=1
|
||
'''
|
||
params = []
|
||
if status != 'all':
|
||
query += ' AND o.status = ?'
|
||
params.append(status)
|
||
if sort == 'newest':
|
||
query += ' ORDER BY o.created_at DESC'
|
||
elif sort == 'oldest':
|
||
query += ' ORDER BY o.created_at ASC'
|
||
elif sort == 'price_high':
|
||
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)
|
||
|
||
@app.route('/admin/orders/<int:order_id>/update', methods=['POST'])
|
||
@admin_required
|
||
def update_order(order_id):
|
||
status = request.form['status']
|
||
sort = request.args.get('sort', 'newest')
|
||
status_filter = request.args.get('status', 'all')
|
||
conn = get_db_connection()
|
||
order = conn.execute('SELECT user_id FROM orders WHERE id = ?', (order_id,)).fetchone()
|
||
conn.execute('UPDATE orders SET status = ? WHERE id = ?', (status, order_id))
|
||
if order:
|
||
conn.execute('''
|
||
INSERT INTO messages (order_id, user_id, admin_id, message, sender_type)
|
||
VALUES (?, ?, ?, ?, 'system')
|
||
''', (order_id, order['user_id'], session['user_id'], f'Статус заказа изменен на: {status}'))
|
||
conn.commit()
|
||
conn.close()
|
||
flash('Статус заказа обновлен', 'success')
|
||
return redirect(url_for('admin_orders', sort=sort, status=status_filter))
|
||
|
||
@app.route('/admin/orders/<int:order_id>/delete', methods=['POST'])
|
||
@admin_required
|
||
def delete_order(order_id):
|
||
sort = request.args.get('sort', 'newest')
|
||
status_filter = request.args.get('status', 'all')
|
||
conn = get_db_connection()
|
||
conn.execute('DELETE FROM messages WHERE order_id = ?', (order_id,))
|
||
conn.execute('DELETE FROM orders WHERE id = ?', (order_id,))
|
||
conn.commit()
|
||
conn.close()
|
||
flash('Заказ удален', 'success')
|
||
return redirect(url_for('admin_orders', sort=sort, status=status_filter))
|
||
|
||
@app.route('/admin/reviews')
|
||
@admin_required
|
||
def admin_reviews():
|
||
sort = request.args.get('sort', 'newest')
|
||
status = request.args.get('status', 'all')
|
||
rating = request.args.get('rating', 'all')
|
||
conn = get_db_connection()
|
||
query = 'SELECT * FROM reviews WHERE 1=1'
|
||
params = []
|
||
if status != 'all':
|
||
query += ' AND status = ?'
|
||
params.append(status)
|
||
if rating != 'all':
|
||
query += ' AND rating = ?'
|
||
params.append(rating)
|
||
if sort == 'newest':
|
||
query += ' ORDER BY created_at DESC'
|
||
elif sort == 'oldest':
|
||
query += ' ORDER BY created_at ASC'
|
||
elif sort == 'rating_high':
|
||
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)
|
||
|
||
@app.route('/admin/reviews/<int:review_id>/moderate', methods=['POST'])
|
||
@admin_required
|
||
def moderate_review(review_id):
|
||
status = request.form['status']
|
||
sort = request.args.get('sort', 'newest')
|
||
status_filter = request.args.get('status', 'all')
|
||
rating_filter = request.args.get('rating', 'all')
|
||
conn = get_db_connection()
|
||
conn.execute('UPDATE reviews SET status = ? WHERE id = ?', (status, review_id))
|
||
conn.commit()
|
||
conn.close()
|
||
flash('Отзыв обработан', 'success')
|
||
return redirect(url_for('admin_reviews', sort=sort, status=status_filter, rating=rating_filter))
|
||
|
||
@app.route('/admin/reviews/<int:review_id>/delete', methods=['POST'])
|
||
@admin_required
|
||
def delete_review(review_id):
|
||
sort = request.args.get('sort', 'newest')
|
||
status_filter = request.args.get('status', 'all')
|
||
rating_filter = request.args.get('rating', 'all')
|
||
conn = get_db_connection()
|
||
conn.execute('DELETE FROM reviews WHERE id = ?', (review_id,))
|
||
conn.commit()
|
||
conn.close()
|
||
flash('Отзыв удален', 'success')
|
||
return redirect(url_for('admin_reviews', sort=sort, status=status_filter, rating=rating_filter))
|
||
|
||
@app.route('/admin/users')
|
||
@admin_required
|
||
def admin_users():
|
||
sort = request.args.get('sort', 'newest')
|
||
role = request.args.get('role', 'all')
|
||
conn = get_db_connection()
|
||
query = 'SELECT * FROM users WHERE 1=1'
|
||
params = []
|
||
if role != 'all':
|
||
query += ' AND role = ?'
|
||
params.append(role)
|
||
if sort == 'newest':
|
||
query += ' ORDER BY created_at DESC'
|
||
elif sort == 'oldest':
|
||
query += ' ORDER BY created_at ASC'
|
||
elif sort == 'name_asc':
|
||
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)
|
||
|
||
@app.route('/admin/users/<int:user_id>/delete', methods=['POST'])
|
||
@admin_required
|
||
def delete_user(user_id):
|
||
sort = request.args.get('sort', 'newest')
|
||
role_filter = request.args.get('role', 'all')
|
||
if user_id == session['user_id']:
|
||
flash('Нельзя удалить самого себя', 'danger')
|
||
return redirect(url_for('admin_users', sort=sort, role=role_filter))
|
||
conn = get_db_connection()
|
||
conn.execute('DELETE FROM messages WHERE user_id = ?', (user_id,))
|
||
conn.execute('DELETE FROM orders WHERE user_id = ?', (user_id,))
|
||
conn.execute('DELETE FROM reviews WHERE user_id = ?', (user_id,))
|
||
conn.execute('DELETE FROM users WHERE id = ?', (user_id,))
|
||
conn.commit()
|
||
conn.close()
|
||
flash('Пользователь удален', 'success')
|
||
return redirect(url_for('admin_users', sort=sort, role=role_filter))
|
||
|
||
@app.route('/admin/users/<int:user_id>/make-admin', methods=['POST'])
|
||
@admin_required
|
||
def make_admin(user_id):
|
||
sort = request.args.get('sort', 'newest')
|
||
role_filter = request.args.get('role', 'all')
|
||
conn = get_db_connection()
|
||
conn.execute('UPDATE users SET role = "admin" WHERE id = ?', (user_id,))
|
||
conn.commit()
|
||
conn.close()
|
||
flash('Пользователь назначен администратором', 'success')
|
||
return redirect(url_for('admin_users', sort=sort, role=role_filter))
|
||
|
||
@app.route('/admin/messages')
|
||
@admin_required
|
||
def admin_messages():
|
||
conn = get_db_connection()
|
||
messages = conn.execute('''
|
||
SELECT m.*, o.id as order_id, u.username as user_name
|
||
FROM messages m
|
||
JOIN orders o ON m.order_id = o.id
|
||
JOIN users u ON o.user_id = u.id
|
||
WHERE m.sender_type = 'user' AND m.is_read = 0
|
||
ORDER BY m.created_at DESC
|
||
''').fetchall()
|
||
conn.close()
|
||
return render_template('admin/messages.html', messages=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")
|
||
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('''
|
||
CREATE TABLE messages (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
order_id INTEGER,
|
||
user_id INTEGER,
|
||
admin_id INTEGER,
|
||
message TEXT NOT NULL,
|
||
sender_type TEXT NOT NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
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)
|