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