| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>Temu Clone - Backend Setup</title> |
| <style> |
| body { |
| font-family: Arial, sans-serif; |
| line-height: 1.6; |
| margin: 0; |
| padding: 20px; |
| background-color: #f5f5f5; |
| } |
| .container { |
| max-width: 1200px; |
| margin: 0 auto; |
| background: white; |
| padding: 20px; |
| border-radius: 5px; |
| box-shadow: 0 0 10px rgba(0,0,0,0.1); |
| } |
| h1, h2, h3 { |
| color: #333; |
| } |
| code { |
| background: #f4f4f4; |
| padding: 2px 5px; |
| border-radius: 3px; |
| font-family: monospace; |
| } |
| pre { |
| background: #333; |
| color: #fff; |
| padding: 10px; |
| border-radius: 5px; |
| overflow-x: auto; |
| } |
| .section { |
| margin-bottom: 30px; |
| padding-bottom: 20px; |
| border-bottom: 1px solid #eee; |
| } |
| .note { |
| background: #fffde7; |
| padding: 10px; |
| border-left: 4px solid #ffd600; |
| margin: 10px 0; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Temu Clone - Backend Setup</h1> |
| |
| <div class="section"> |
| <h2>1. Backend Setup</h2> |
| |
| <h3>Python Requirements</h3> |
| <p>Create a <code>requirements.txt</code> file:</p> |
| <pre> |
| 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 |
| </pre> |
|
|
| <h3>Configuration</h3> |
| <p>Create <code>config.py</code>:</p> |
| <pre> |
| 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' |
| </pre> |
|
|
| <h3>Database Models</h3> |
| <p>Create <code>models.py</code>:</p> |
| <pre> |
| 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) |
| </pre> |
| </div> |
|
|
| <div class="section"> |
| <h2>2. Flask Application</h2> |
| |
| <p>Create <code>app.py</code>:</p> |
| <pre> |
| 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/<filename>') |
| 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) |
| </pre> |
| </div> |
|
|
| <div class="section"> |
| <h2>3. Admin Panel Templates</h2> |
| |
| <h3>Login Template (templates/auth/login.html)</h3> |
| <pre> |
| <!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> |
| </pre> |
|
|
| <h3>Admin Dashboard (templates/admin/index.html)</h3> |
| <pre> |
| {% 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 %} |
| </pre> |
| </div> |
|
|
| <div class="section"> |
| <h2>4. Integration with Frontend</h2> |
| |
| <h3>Update your frontend JavaScript to use the API</h3> |
| <p>Modify your existing JavaScript to fetch products from the backend:</p> |
| <pre> |
| // 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')); |
| } |
| }); |
| }); |
| </pre> |
| </div> |
|
|
| <div class="section"> |
| <h2>5. Deployment Instructions</h2> |
| |
| <div class="note"> |
| <p><strong>Note:</strong> For production, you should use a proper web server like Gunicorn with Nginx, and a production database like PostgreSQL.</p> |
| </div> |
| |
| <h3>Local Development</h3> |
| <ol> |
| <li>Install Python 3.8+</li> |
| <li>Create and activate a virtual environment: |
| <pre>python -m venv venv |
| source venv/bin/activate # On Windows: venv\Scripts\activate</pre> |
| </li> |
| <li>Install dependencies: |
| <pre>pip install -r requirements.txt</pre> |
| </li> |
| <li>Run the application: |
| <pre>python app.py</pre> |
| </li> |
| <li>Access the admin panel at <code>http://localhost:5000/admin</code></li> |
| <li>Login with the admin credentials from <code>config.py</code></li> |
| </ol> |
| |
| <h3>Production Deployment</h3> |
| <p>For production, you'll need to:</p> |
| <ol> |
| <li>Set proper environment variables (SECRET_KEY, DATABASE_URL, etc.)</li> |
| <li>Use a production WSGI server like Gunicorn: |
| <pre>gunicorn -w 4 -b 0.0.0.0:5000 app:app</pre> |
| </li> |
| <li>Configure a reverse proxy like Nginx</li> |
| <li>Set up a production database (PostgreSQL recommended)</li> |
| <li>Configure HTTPS with Let's Encrypt</li> |
| </ol> |
| </div> |
| </div> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Marv12/sma" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |