257 lines
9.7 KiB
Python
257 lines
9.7 KiB
Python
import os
|
|
import uuid
|
|
from functools import wraps
|
|
from datetime import datetime
|
|
from flask import render_template, redirect, url_for, request, flash, session, current_app, send_file
|
|
from werkzeug.utils import secure_filename
|
|
from app.admin import bp
|
|
from app.models import Category, Item, ContactInfo
|
|
from app import db
|
|
import pdfkit
|
|
|
|
def admin_required(f):
|
|
@wraps(f)
|
|
def decorated_function(*args, **kwargs):
|
|
if 'admin_authenticated' not in session:
|
|
return redirect(url_for('admin.login'))
|
|
return f(*args, **kwargs)
|
|
return decorated_function
|
|
|
|
def allowed_file(filename):
|
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
|
|
return '.' in filename and \
|
|
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
|
|
|
@bp.route('/login', methods=['GET', 'POST'])
|
|
def login():
|
|
if request.method == 'POST':
|
|
password = request.form.get('password')
|
|
if password == current_app.config['ADMIN_PASSWORD']:
|
|
session['admin_authenticated'] = True
|
|
return redirect(url_for('admin.dashboard'))
|
|
else:
|
|
flash('Invalid password', 'danger')
|
|
return render_template('admin/login.html', title='Admin Login')
|
|
|
|
@bp.route('/')
|
|
@admin_required
|
|
def dashboard():
|
|
return render_template('admin/dashboard.html', title='Admin Dashboard')
|
|
|
|
@bp.route('/categories')
|
|
@admin_required
|
|
def categories():
|
|
categories = Category.query.all()
|
|
return render_template('admin/categories.html',
|
|
title='Manage Categories',
|
|
categories=categories)
|
|
|
|
@bp.route('/category/add', methods=['GET', 'POST'])
|
|
@admin_required
|
|
def add_category():
|
|
if request.method == 'POST':
|
|
name = request.form.get('name')
|
|
if name:
|
|
# Check if category already exists
|
|
existing = Category.query.filter_by(name=name).first()
|
|
if existing:
|
|
flash(f'Category "{name}" already exists', 'warning')
|
|
else:
|
|
category = Category(name=name)
|
|
db.session.add(category)
|
|
db.session.commit()
|
|
flash(f'Category "{name}" added successfully', 'success')
|
|
return redirect(url_for('admin.categories'))
|
|
else:
|
|
flash('Category name is required', 'danger')
|
|
return render_template('admin/add_category.html', title='Add Category')
|
|
|
|
@bp.route('/category/<int:id>/delete', methods=['POST'])
|
|
@admin_required
|
|
def delete_category(id):
|
|
category = Category.query.get_or_404(id)
|
|
if category:
|
|
db.session.delete(category)
|
|
db.session.commit()
|
|
flash(f'Category "{category.name}" deleted successfully', 'success')
|
|
return redirect(url_for('admin.categories'))
|
|
|
|
@bp.route('/items')
|
|
@admin_required
|
|
def items():
|
|
items = Item.query.order_by(Item.created_at.desc()).all()
|
|
return render_template('admin/items.html',
|
|
title='Manage Items',
|
|
items=items)
|
|
|
|
@bp.route('/item/add', methods=['GET', 'POST'])
|
|
@admin_required
|
|
def add_item():
|
|
categories = Category.query.all()
|
|
if request.method == 'POST':
|
|
title = request.form.get('title')
|
|
description = request.form.get('description')
|
|
price = request.form.get('price')
|
|
category_id = request.form.get('category_id')
|
|
status = request.form.get('status', 'For Sale')
|
|
|
|
if title and description and price and category_id:
|
|
try:
|
|
price = float(price)
|
|
item = Item(
|
|
title=title,
|
|
description=description,
|
|
price=price,
|
|
category_id=category_id,
|
|
status=status
|
|
)
|
|
|
|
# Handle image upload
|
|
if 'image' in request.files:
|
|
file = request.files['image']
|
|
if file and file.filename and allowed_file(file.filename):
|
|
# Create a unique filename
|
|
filename = secure_filename(file.filename)
|
|
unique_filename = f"{uuid.uuid4().hex}_{filename}"
|
|
file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], unique_filename))
|
|
item.image_filename = unique_filename
|
|
|
|
db.session.add(item)
|
|
db.session.commit()
|
|
flash(f'Item "{title}" added successfully', 'success')
|
|
return redirect(url_for('admin.items'))
|
|
except ValueError:
|
|
flash('Price must be a number', 'danger')
|
|
else:
|
|
flash('All fields are required', 'danger')
|
|
|
|
return render_template('admin/add_item.html',
|
|
title='Add Item',
|
|
categories=categories)
|
|
|
|
@bp.route('/item/<int:id>/edit', methods=['GET', 'POST'])
|
|
@admin_required
|
|
def edit_item(id):
|
|
item = Item.query.get_or_404(id)
|
|
categories = Category.query.all()
|
|
|
|
if request.method == 'POST':
|
|
title = request.form.get('title')
|
|
description = request.form.get('description')
|
|
price = request.form.get('price')
|
|
category_id = request.form.get('category_id')
|
|
status = request.form.get('status')
|
|
|
|
if title and description and price and category_id and status:
|
|
try:
|
|
price = float(price)
|
|
item.title = title
|
|
item.description = description
|
|
item.price = price
|
|
item.category_id = category_id
|
|
item.status = status
|
|
item.updated_at = datetime.utcnow()
|
|
|
|
# Handle image upload
|
|
if 'image' in request.files:
|
|
file = request.files['image']
|
|
if file and file.filename and allowed_file(file.filename):
|
|
# Delete old image if it exists
|
|
if item.image_filename:
|
|
old_image_path = os.path.join(current_app.config['UPLOAD_FOLDER'], item.image_filename)
|
|
if os.path.exists(old_image_path):
|
|
os.remove(old_image_path)
|
|
|
|
# Create a unique filename
|
|
filename = secure_filename(file.filename)
|
|
unique_filename = f"{uuid.uuid4().hex}_{filename}"
|
|
file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], unique_filename))
|
|
item.image_filename = unique_filename
|
|
|
|
db.session.commit()
|
|
flash(f'Item "{title}" updated successfully', 'success')
|
|
return redirect(url_for('admin.items'))
|
|
except ValueError:
|
|
flash('Price must be a number', 'danger')
|
|
else:
|
|
flash('All fields are required', 'danger')
|
|
|
|
return render_template('admin/edit_item.html',
|
|
title='Edit Item',
|
|
item=item,
|
|
categories=categories)
|
|
|
|
@bp.route('/item/<int:id>/delete', methods=['POST'])
|
|
@admin_required
|
|
def delete_item(id):
|
|
item = Item.query.get_or_404(id)
|
|
if item:
|
|
# Delete image file if exists
|
|
if item.image_filename:
|
|
image_path = os.path.join(current_app.config['UPLOAD_FOLDER'], item.image_filename)
|
|
if os.path.exists(image_path):
|
|
os.remove(image_path)
|
|
|
|
db.session.delete(item)
|
|
db.session.commit()
|
|
flash(f'Item "{item.title}" deleted successfully', 'success')
|
|
return redirect(url_for('admin.items'))
|
|
|
|
@bp.route('/information', methods=['GET', 'POST'])
|
|
@admin_required
|
|
def edit_contact():
|
|
contact_info = ContactInfo.query.first()
|
|
|
|
# Create default contact info if it doesn't exist
|
|
if not contact_info:
|
|
contact_info = ContactInfo(
|
|
information="Welcome to our Digital Garage Sale! This is a place to find unique pre-owned items at great prices.",
|
|
email="example@example.com",
|
|
signal="Signal Username or Number",
|
|
donation_link="https://example.com/donate"
|
|
)
|
|
db.session.add(contact_info)
|
|
db.session.commit()
|
|
|
|
if request.method == 'POST':
|
|
information = request.form.get('information')
|
|
email = request.form.get('email')
|
|
signal = request.form.get('signal')
|
|
donation_link = request.form.get('donation_link')
|
|
|
|
if email:
|
|
contact_info.information = information
|
|
contact_info.email = email
|
|
contact_info.signal = signal
|
|
contact_info.donation_link = donation_link
|
|
|
|
db.session.commit()
|
|
flash('Contact information updated successfully', 'success')
|
|
return redirect(url_for('admin.dashboard'))
|
|
else:
|
|
flash('Email is required', 'danger')
|
|
|
|
return render_template('admin/edit_contact.html',
|
|
title='Edit Contact Information',
|
|
contact_info=contact_info)
|
|
|
|
@bp.route('/catalog/generate')
|
|
@admin_required
|
|
def generate_catalog():
|
|
items = Item.query.order_by(Item.created_at.desc()).all()
|
|
categories = Category.query.all()
|
|
contact_info = ContactInfo.query.first()
|
|
|
|
# Create HTML to convert to PDF
|
|
html = render_template('admin/catalog_pdf.html',
|
|
items=items,
|
|
categories=categories,
|
|
contact_info=contact_info)
|
|
|
|
# Generate PDF
|
|
pdf_path = os.path.join(current_app.root_path, 'static', 'catalog.pdf')
|
|
pdfkit.from_string(html, pdf_path)
|
|
|
|
# Return PDF for download
|
|
return send_file(pdf_path, as_attachment=True, download_name='garage_sale_catalog.pdf')
|