Create a requirements.txt file:
Flask==2.3.2 Flask-SQLAlchemy==3.0.3 Flask-Login==0.6.2 Flask-Admin==1.6.1 Flask-WTF==1.0.1 python-dotenv==1.0.0 Pillow==9.5.0
Create config.py:
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
SECRET_KEY = os.getenv('SECRET_KEY') or 'your-secret-key-here'
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL') or 'sqlite:///site.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
# Admin credentials
ADMIN_EMAIL = os.getenv('ADMIN_EMAIL') or 'admin@example.com'
ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD') or 'admin123'
Create models.py:
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
db = SQLAlchemy()
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
is_admin = db.Column(db.Boolean, default=False)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
orders = db.relationship('Order', backref='customer', lazy=True)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
price = db.Column(db.Float, nullable=False)
original_price = db.Column(db.Float)
image = db.Column(db.String(100))
category = db.Column(db.String(50))
flash_sale = db.Column(db.Boolean, default=False)
rating = db.Column(db.Float, default=0)
reviews = db.Column(db.Integer, default=0)
date_added = db.Column(db.DateTime, default=datetime.utcnow)
order_items = db.relationship('OrderItem', backref='product', lazy=True)
class Order(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
total = db.Column(db.Float, nullable=False)
status = db.Column(db.String(20), default='Pending')
payment_method = db.Column(db.String(50))
shipping_address = db.Column(db.Text)
billing_address = db.Column(db.Text)
date_ordered = db.Column(db.DateTime, default=datetime.utcnow)
items = db.relationship('OrderItem', backref='order', lazy=True)
class OrderItem(db.Model):
id = db.Column(db.Integer, primary_key=True)
order_id = db.Column(db.Integer, db.ForeignKey('order.id'), nullable=False)
product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
quantity = db.Column(db.Integer, nullable=False)
price = db.Column(db.Float, nullable=False)
Create app.py:
import os
from flask import Flask, render_template, request, redirect, url_for, flash, send_from_directory
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_user, login_required, logout_user, current_user
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from werkzeug.utils import secure_filename
from models import db, User, Product, Order, OrderItem
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
# Initialize extensions
db.init_app(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
# Create upload folder if not exists
if not os.path.exists(app.config['UPLOAD_FOLDER']):
os.makedirs(app.config['UPLOAD_FOLDER'])
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# Admin setup
class AdminModelView(ModelView):
def is_accessible(self):
return current_user.is_authenticated and current_user.is_admin
def inaccessible_callback(self, name, **kwargs):
return redirect(url_for('login'))
admin = Admin(app, name='Admin Panel', template_mode='bootstrap3')
admin.add_view(AdminModelView(User, db.session))
admin.add_view(AdminModelView(Product, db.session))
admin.add_view(AdminModelView(Order, db.session))
admin.add_view(AdminModelView(OrderItem, db.session))
# Routes
@app.route('/')
def home():
return render_template('admin/index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
email = request.form.get('email')
password = request.form.get('password')
user = User.query.filter_by(email=email).first()
if user and user.check_password(password):
login_user(user)
next_page = request.args.get('next')
return redirect(next_page or url_for('admin.index'))
else:
flash('Invalid email or password', 'danger')
return render_template('auth/login.html')
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
# API Endpoints
@app.route('/api/products', methods=['GET'])
def get_products():
products = Product.query.all()
return {'products': [{
'id': p.id,
'name': p.name,
'price': p.price,
'original_price': p.original_price,
'image': url_for('uploaded_file', filename=p.image) if p.image else None,
'category': p.category,
'flash_sale': ,
'rating': p.rating,
'reviews': p.reviews
} for p in products]}
@app.route('/api/orders', methods=['POST'])
def create_order():
data = request.get_json()
# In a real app, you would validate the data and process payment
order = Order(
user_id=data.get('user_id'),
total=data['total'],
payment_method=data.get('payment_method', 'Credit Card'),
shipping_address=data.get('shipping_address', ''),
billing_address=data.get('billing_address', '')
)
db.session.add(order)
for item in data['items']:
order_item = OrderItem(
order_id=order.id,
product_id=item['product_id'],
quantity=item['quantity'],
price=item['price']
)
db.session.add(order_item)
db.session.commit()
return {'success': True, 'order_id': order.id}
@app.route('/uploads/')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
# Create admin user on first run
def create_admin_user():
admin_email = app.config['ADMIN_EMAIL']
admin_password = app.config['ADMIN_PASSWORD']
if not User.query.filter_by(email=admin_email).first():
admin_user = User(
username='admin',
email=admin_email,
is_admin=True
)
admin_user.set_password(admin_password)
db.session.add(admin_user)
db.session.commit()
print("Admin user created successfully!")
if __name__ == '__main__':
with app.app_context():
db.create_all()
create_admin_user()
app.run(debug=True)
<!DOCTYPE html>
<html>
<head>
<title>Admin Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-4">
<div class="card shadow">
<div class="card-body">
<h3 class="card-title text-center mb-4">Admin Login</h3>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('login') }}">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary w-100">Login</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
{% extends 'admin/master.html' %}
{% block body %}
<div class="container">
<h1>Admin Dashboard</h1>
<div class="row mt-4">
<div class="col-md-4">
<div class="card text-white bg-primary mb-3">
<div class="card-body">
<h5 class="card-title">Total Products</h5>
<p class="card-text" style="font-size: 2rem;">{{ Product.query.count() }}</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-white bg-success mb-3">
<div class="card-body">
<h5 class="card-title">Total Orders</h5>
<p class="card-text" style="font-size: 2rem;">{{ Order.query.count() }}</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-white bg-info mb-3">
<div class="card-body">
<h5 class="card-title">Total Users</h5>
<p class="card-text" style="font-size: 2rem;">{{ User.query.count() }}</p>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
Recent Orders
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Date</th>
<th>Total</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for order in Order.query.order_by(Order.date_ordered.desc()).limit(5).all() %}
<tr>
<td><a href="{{ url_for('order.edit_view', id=order.id) }}">{{ order.id }}</a></td>
<td>{{ order.date_ordered.strftime('%Y-%m-%d') }}</td>
<td>${{ "%.2f"|format(order.total) }}</td>
<td>{{ order.status }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
Recent Products
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{% for product in Product.query.order_by(Product.date_added.desc()).limit(5).all() %}
<tr>
<td><a href="{{ url_for('product.edit_view', id=product.id) }}">{{ product.name }}</a></td>
<td>${{ "%.2f"|format(product.price) }}</td>
<td>{{ product.category }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Modify your existing JavaScript to fetch products from the backend:
// Fetch products from backend
async function fetchProducts() {
try {
const response = await fetch('/api/products');
const data = await response.json();
return data.products;
} catch (error) {
console.error('Error fetching products:', error);
return [];
}
}
// Example of creating an order
async function createOrder(orderData) {
try {
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(orderData)
});
return await response.json();
} catch (error) {
console.error('Error creating order:', error);
return { success: false, error: 'Failed to create order' };
}
}
// Update your existing code to use these functions
document.addEventListener('DOMContentLoaded', async function() {
const products = await fetchProducts();
// Render products to the page
renderProducts(products);
// When user checks out
checkoutButton.addEventListener('click', async function() {
const orderData = {
user_id: currentUser?.id || null,
total: calculateCartTotal(),
items: cart.map(item => ({
product_id: item.id,
quantity: item.quantity,
price: item.price
})),
payment_method: 'Credit Card',
shipping_address: '123 Main St, Anytown, USA'
};
const result = await createOrder(orderData);
if (result.success) {
alert(`Order #${result.order_id} created successfully!`);
// Clear cart
cart = [];
localStorage.setItem('cart', JSON.stringify(cart));
updateCartCount();
renderCartItems();
} else {
alert('Failed to create order: ' + (result.error || 'Unknown error'));
}
});
});
Note: For production, you should use a proper web server like Gunicorn with Nginx, and a production database like PostgreSQL.
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
python app.py
http://localhost:5000/adminconfig.pyFor production, you'll need to:
gunicorn -w 4 -b 0.0.0.0:5000 app:app