Temu Clone - Backend Setup

1. Backend Setup

Python Requirements

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

Configuration

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'

Database Models

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)

2. Flask Application

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)

3. Admin Panel Templates

Login Template (templates/auth/login.html)

<!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>

Admin Dashboard (templates/admin/index.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 %}

4. Integration with Frontend

Update your frontend JavaScript to use the API

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'));
        }
    });
});

5. Deployment Instructions

Note: For production, you should use a proper web server like Gunicorn with Nginx, and a production database like PostgreSQL.

Local Development

  1. Install Python 3.8+
  2. Create and activate a virtual environment:
    python -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
  3. Install dependencies:
    pip install -r requirements.txt
  4. Run the application:
    python app.py
  5. Access the admin panel at http://localhost:5000/admin
  6. Login with the admin credentials from config.py

Production Deployment

For production, you'll need to:

  1. Set proper environment variables (SECRET_KEY, DATABASE_URL, etc.)
  2. Use a production WSGI server like Gunicorn:
    gunicorn -w 4 -b 0.0.0.0:5000 app:app
  3. Configure a reverse proxy like Nginx
  4. Set up a production database (PostgreSQL recommended)
  5. Configure HTTPS with Let's Encrypt

Made with DeepSite LogoDeepSite - 🧬 Remix