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//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//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//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')