|
|
import os |
|
|
import io |
|
|
import json |
|
|
import hashlib |
|
|
from datetime import datetime, timedelta |
|
|
from PIL import Image |
|
|
import pytz |
|
|
from flask import Flask, request, jsonify, send_file |
|
|
from flask_cors import CORS |
|
|
import google.generativeai as genai |
|
|
import firebase_admin |
|
|
from firebase_admin import credentials, db, storage, auth |
|
|
import pandas as pd |
|
|
import requests |
|
|
from urllib.parse import urlparse, unquote |
|
|
|
|
|
app = Flask(__name__) |
|
|
CORS(app) |
|
|
|
|
|
|
|
|
cred = credentials.Certificate('datingi-firebase-adminsdk-obe4r-d63a25b54e.json') |
|
|
firebase_admin.initialize_app(cred, { |
|
|
'databaseURL': 'https://datingi.firebaseio.com', |
|
|
'storageBucket': 'datingi.firebasestorage.app' |
|
|
}) |
|
|
|
|
|
bucket = storage.bucket() |
|
|
api_key = os.environ['Gemini'] |
|
|
FIREBASE_API_KEY = os.environ.get('FIREBASE_API_KEY') |
|
|
|
|
|
|
|
|
def create_test_accounts(): |
|
|
try: |
|
|
|
|
|
try: |
|
|
admin_user = auth.create_user( |
|
|
email='rurumukamuri@gmail.com', |
|
|
password='ruvimbo1', |
|
|
name='Ruru', |
|
|
phone='0776076957' |
|
|
) |
|
|
except auth.EmailAlreadyExistsError: |
|
|
admin_user = auth.get_user_by_email('rurumukamuri@gmail.com') |
|
|
|
|
|
db.reference(f'users/{admin_user.uid}').set({ |
|
|
'is_admin': True |
|
|
}) |
|
|
|
|
|
print("Test accounts created/updated successfully") |
|
|
except Exception as e: |
|
|
print(f"Error creating test accounts: {str(e)}") |
|
|
|
|
|
|
|
|
create_test_accounts() |
|
|
|
|
|
|
|
|
def configure_gemini(): |
|
|
genai.configure(api_key=api_key) |
|
|
return genai.GenerativeModel('gemini-2.0-flash-thinking-exp') |
|
|
|
|
|
def verify_token(token): |
|
|
try: |
|
|
decoded_token = auth.verify_id_token(token) |
|
|
return decoded_token['uid'] |
|
|
except Exception: |
|
|
return None |
|
|
|
|
|
def verify_admin(auth_header): |
|
|
if not auth_header or not auth_header.startswith('Bearer '): |
|
|
raise ValueError('Invalid token') |
|
|
|
|
|
token = auth_header.split(' ')[1] |
|
|
uid = verify_token(token) |
|
|
if not uid: |
|
|
raise PermissionError('Invalid user') |
|
|
|
|
|
user_ref = db.reference(f'users/{uid}') |
|
|
user_data = user_ref.get() |
|
|
if not user_data or not user_data.get('is_admin', False): |
|
|
raise PermissionError('Admin access required') |
|
|
|
|
|
return uid |
|
|
|
|
|
|
|
|
|
|
|
CATEGORIES = [ |
|
|
'Masks', |
|
|
'Gloves', |
|
|
'Face Shields', |
|
|
'Protective Suits', |
|
|
'Sanitizers', |
|
|
'Disinfectants' |
|
|
] |
|
|
|
|
|
|
|
|
@app.route('/api/auth/register', methods=['POST']) |
|
|
def register(): |
|
|
try: |
|
|
data = request.get_json() |
|
|
email = data.get('email') |
|
|
password = data.get('password') |
|
|
name = data.get('name') |
|
|
|
|
|
if not email or not password: |
|
|
return jsonify({'error': 'Email and password required'}), 400 |
|
|
|
|
|
|
|
|
user = auth.create_user( |
|
|
email=email, |
|
|
password=password, |
|
|
display_name=name |
|
|
) |
|
|
|
|
|
|
|
|
user_ref = db.reference(f'users/{user.uid}') |
|
|
user_data = { |
|
|
'email': email, |
|
|
'name': name, |
|
|
'created_at': datetime.now(pytz.UTC).isoformat(), |
|
|
'is_admin': False, |
|
|
'shipping_addresses': [], |
|
|
'phone': data.get('phone', '') |
|
|
} |
|
|
user_ref.set(user_data) |
|
|
|
|
|
|
|
|
session_id = data.get('session_id') |
|
|
if session_id: |
|
|
guest_cart_ref = db.reference(f'carts/session_{session_id}') |
|
|
guest_cart = guest_cart_ref.get() |
|
|
if guest_cart: |
|
|
user_cart_ref = db.reference(f'carts/{user.uid}') |
|
|
user_cart_ref.set(guest_cart) |
|
|
guest_cart_ref.delete() |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'uid': user.uid, |
|
|
'email': user.email, |
|
|
'name': user.display_name |
|
|
}), 201 |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 400 |
|
|
|
|
|
@app.route('/api/auth/login', methods=['POST']) |
|
|
def login(): |
|
|
try: |
|
|
data = request.get_json() |
|
|
email = data.get('email') |
|
|
password = data.get('password') |
|
|
|
|
|
if not email or not password: |
|
|
return jsonify({'error': 'Email and password required'}), 400 |
|
|
|
|
|
|
|
|
user = auth.get_user_by_email(email) |
|
|
|
|
|
|
|
|
custom_token = auth.create_custom_token(user.uid) |
|
|
|
|
|
|
|
|
user_ref = db.reference(f'users/{user.uid}') |
|
|
user_data = user_ref.get() |
|
|
|
|
|
|
|
|
session_id = data.get('session_id') |
|
|
if session_id: |
|
|
guest_cart_ref = db.reference(f'carts/session_{session_id}') |
|
|
guest_cart = guest_cart_ref.get() |
|
|
if guest_cart: |
|
|
user_cart_ref = db.reference(f'carts/{user.uid}') |
|
|
user_cart_ref.set(guest_cart) |
|
|
guest_cart_ref.delete() |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'token': custom_token, |
|
|
'uid': user.uid, |
|
|
'email': user.email, |
|
|
'name': user.display_name, |
|
|
'is_admin': user_data.get('is_admin', False) |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': 'Invalid email or password'}), 401 |
|
|
|
|
|
@app.route('/api/auth/password-reset', methods=['POST']) |
|
|
def request_password_reset(): |
|
|
try: |
|
|
data = request.get_json() |
|
|
email = data.get('email') |
|
|
|
|
|
if not email: |
|
|
return jsonify({'error': 'Email required'}), 400 |
|
|
|
|
|
|
|
|
link = auth.generate_password_reset_link(email) |
|
|
|
|
|
|
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'reset_link': link |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 400 |
|
|
|
|
|
@app.route('/api/user/profile', methods=['GET', 'PUT']) |
|
|
def user_profile(): |
|
|
try: |
|
|
auth_header = request.headers.get('Authorization') |
|
|
if not auth_header: |
|
|
return jsonify({'error': 'Authorization required'}), 401 |
|
|
|
|
|
uid = verify_token(auth_header.split(' ')[1]) |
|
|
if not uid: |
|
|
return jsonify({'error': 'Invalid token'}), 401 |
|
|
|
|
|
user_ref = db.reference(f'users/{uid}') |
|
|
|
|
|
if request.method == 'GET': |
|
|
user_data = user_ref.get() |
|
|
return jsonify(user_data) |
|
|
|
|
|
elif request.method == 'PUT': |
|
|
data = request.get_json() |
|
|
updates = {} |
|
|
|
|
|
|
|
|
if 'name' in data: |
|
|
updates['name'] = data['name'] |
|
|
auth.update_user(uid, display_name=data['name']) |
|
|
|
|
|
if 'phone' in data: |
|
|
updates['phone'] = data['phone'] |
|
|
|
|
|
if 'shipping_addresses' in data: |
|
|
updates['shipping_addresses'] = data['shipping_addresses'] |
|
|
|
|
|
if updates: |
|
|
user_ref.update(updates) |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'updated_fields': list(updates.keys()) |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), |
|
|
|
|
|
|
|
|
@app.route('/api/admin/products', methods=['POST']) |
|
|
def create_product(): |
|
|
try: |
|
|
verify_admin(request.headers.get('Authorization', '')) |
|
|
|
|
|
if 'image' not in request.files: |
|
|
return jsonify({'error': 'No image file uploaded'}), 400 |
|
|
|
|
|
file = request.files['image'] |
|
|
name = request.form.get('name') |
|
|
category = request.form.get('category') |
|
|
price = float(request.form.get('price')) |
|
|
description = request.form.get('description') |
|
|
stock = int(request.form.get('stock', 0)) |
|
|
|
|
|
if category not in CATEGORIES: |
|
|
return jsonify({'error': 'Invalid category'}), 400 |
|
|
|
|
|
|
|
|
image_bytes = file.read() |
|
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') |
|
|
file_hash = hashlib.md5(image_bytes).hexdigest() |
|
|
blob = bucket.blob(f'products/{timestamp}_{file_hash}.jpg') |
|
|
blob.upload_from_string(image_bytes, content_type='image/jpeg') |
|
|
image_url = blob.public_url |
|
|
|
|
|
|
|
|
product_ref = db.reference('products').push({ |
|
|
'name': name, |
|
|
'category': category, |
|
|
'price': price, |
|
|
'description': description, |
|
|
'stock': stock, |
|
|
'image_url': image_url, |
|
|
'created_at': datetime.now(pytz.UTC).isoformat(), |
|
|
'active': True |
|
|
}) |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'product_id': product_ref.key |
|
|
}), 201 |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
@app.route('/api/admin/products/<string:product_id>', methods=['PUT']) |
|
|
def update_product(product_id): |
|
|
try: |
|
|
verify_admin(request.headers.get('Authorization', '')) |
|
|
|
|
|
updates = {} |
|
|
if 'name' in request.form: |
|
|
updates['name'] = request.form['name'] |
|
|
if 'category' in request.form and request.form['category'] in CATEGORIES: |
|
|
updates['category'] = request.form['category'] |
|
|
if 'price' in request.form: |
|
|
updates['price'] = float(request.form['price']) |
|
|
if 'description' in request.form: |
|
|
updates['description'] = request.form['description'] |
|
|
if 'stock' in request.form: |
|
|
updates['stock'] = int(request.form['stock']) |
|
|
if 'active' in request.form: |
|
|
updates['active'] = request.form['active'].lower() == 'true' |
|
|
|
|
|
if 'image' in request.files: |
|
|
file = request.files['image'] |
|
|
image_bytes = file.read() |
|
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') |
|
|
file_hash = hashlib.md5(image_bytes).hexdigest() |
|
|
blob = bucket.blob(f'products/{timestamp}_{file_hash}.jpg') |
|
|
blob.upload_from_string(image_bytes, content_type='image/jpeg') |
|
|
updates['image_url'] = blob.public_url |
|
|
|
|
|
db.reference(f'products/{product_id}').update(updates) |
|
|
|
|
|
return jsonify({'success': True}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
|
|
|
@app.route('/api/cart/add', methods=['POST']) |
|
|
def add_to_cart(): |
|
|
try: |
|
|
data = request.get_json() |
|
|
product_id = data['product_id'] |
|
|
quantity = int(data['quantity']) |
|
|
|
|
|
|
|
|
auth_header = request.headers.get('Authorization') |
|
|
uid = verify_token(auth_header.split(' ')[1]) if auth_header else None |
|
|
|
|
|
|
|
|
session_id = data.get('session_id') |
|
|
if not uid and not session_id: |
|
|
return jsonify({'error': 'Either login or provide session_id'}), 400 |
|
|
|
|
|
cart_id = uid if uid else f'session_{session_id}' |
|
|
cart_ref = db.reference(f'carts/{cart_id}') |
|
|
|
|
|
|
|
|
product = db.reference(f'products/{product_id}').get() |
|
|
if not product: |
|
|
return jsonify({'error': 'Product not found'}), 404 |
|
|
if not product.get('active', False): |
|
|
return jsonify({'error': 'Product not available'}), 400 |
|
|
if product.get('stock', 0) < quantity: |
|
|
return jsonify({'error': 'Insufficient stock'}), 400 |
|
|
|
|
|
|
|
|
cart_item = cart_ref.child(product_id).get() or {'quantity': 0} |
|
|
new_quantity = cart_item['quantity'] + quantity |
|
|
|
|
|
cart_ref.child(product_id).set({ |
|
|
'quantity': new_quantity, |
|
|
'price': product['price'], |
|
|
'name': product['name'] |
|
|
}) |
|
|
|
|
|
return jsonify({'success': True}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
@app.route('/api/orders', methods=['POST']) |
|
|
def create_order(): |
|
|
try: |
|
|
data = request.get_json() |
|
|
auth_header = request.headers.get('Authorization') |
|
|
uid = verify_token(auth_header.split(' ')[1]) if auth_header else None |
|
|
|
|
|
cart_id = uid if uid else f'session_{data["session_id"]}' |
|
|
cart_ref = db.reference(f'carts/{cart_id}') |
|
|
cart = cart_ref.get() |
|
|
|
|
|
if not cart: |
|
|
return jsonify({'error': 'Cart is empty'}), 400 |
|
|
|
|
|
|
|
|
total = 0 |
|
|
items = [] |
|
|
for product_id, item in cart.items(): |
|
|
product = db.reference(f'products/{product_id}').get() |
|
|
if not product or not product.get('active', False): |
|
|
return jsonify({'error': f'Product {product_id} not available'}), 400 |
|
|
if product['stock'] < item['quantity']: |
|
|
return jsonify({'error': f'Insufficient stock for {product["name"]}'}), 400 |
|
|
|
|
|
total += item['quantity'] * product['price'] |
|
|
items.append({ |
|
|
'product_id': product_id, |
|
|
'quantity': item['quantity'], |
|
|
'price': product['price'], |
|
|
'name': product['name'] |
|
|
}) |
|
|
|
|
|
|
|
|
db.reference(f'products/{product_id}').update({ |
|
|
'stock': product['stock'] - item['quantity'] |
|
|
}) |
|
|
|
|
|
|
|
|
order_ref = db.reference('orders').push({ |
|
|
'user_id': uid, |
|
|
'session_id': None if uid else data.get('session_id'), |
|
|
'items': items, |
|
|
'total': total, |
|
|
'status': 'pending', |
|
|
'shipping_address': data.get('shipping_address'), |
|
|
'contact_email': data.get('contact_email'), |
|
|
'created_at': datetime.now(pytz.UTC).isoformat() |
|
|
}) |
|
|
|
|
|
|
|
|
cart_ref.delete() |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'order_id': order_ref.key |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
|
|
|
@app.route('/api/admin/reports', methods=['POST']) |
|
|
def generate_report(): |
|
|
try: |
|
|
verify_admin(request.headers.get('Authorization', '')) |
|
|
data = request.get_json() |
|
|
start_date = datetime.fromisoformat(data['start_date']) |
|
|
end_date = datetime.fromisoformat(data['end_date']) |
|
|
|
|
|
|
|
|
orders_ref = db.reference('orders') |
|
|
orders = orders_ref.get() or {} |
|
|
|
|
|
filtered_orders = { |
|
|
k: v for k, v in orders.items() |
|
|
if start_date <= datetime.fromisoformat(v['created_at']) <= end_date |
|
|
} |
|
|
|
|
|
prompt = f"""Analyze this e-commerce data for medical PPE products and generate a comprehensive business report. |
|
|
Focus on: |
|
|
1. Total sales and revenue |
|
|
2. Popular products and categories |
|
|
3. Customer behavior patterns |
|
|
4. Stock management recommendations |
|
|
5. Market trends and insights |
|
|
|
|
|
Data period: {start_date.date()} to {end_date.date()}""" |
|
|
|
|
|
model = configure_gemini() |
|
|
response = model.generate_content([prompt, json.dumps(filtered_orders)]) |
|
|
|
|
|
return jsonify({ |
|
|
'report': response.text, |
|
|
'data': { |
|
|
'total_orders': len(filtered_orders), |
|
|
'total_revenue': sum(order['total'] for order in filtered_orders.values()) |
|
|
} |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
|
|
|
@app.route('/api/products', methods=['GET']) |
|
|
def get_products(): |
|
|
try: |
|
|
category = request.args.get('category') |
|
|
search = request.args.get('search', '').lower() |
|
|
|
|
|
products_ref = db.reference('products') |
|
|
products = products_ref.get() or {} |
|
|
|
|
|
filtered_products = [] |
|
|
for product_id, product in products.items(): |
|
|
if not product.get('active', False): |
|
|
continue |
|
|
if category and product['category'] != category: |
|
|
continue |
|
|
if search and search not in product['name'].lower(): |
|
|
continue |
|
|
|
|
|
filtered_products.append({ |
|
|
'id': product_id, |
|
|
**product |
|
|
}) |
|
|
|
|
|
return jsonify(filtered_products) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
|
|
|
@app.route('/api/user/orders', methods=['GET']) |
|
|
def get_user_orders(): |
|
|
try: |
|
|
auth_header = request.headers.get('Authorization') |
|
|
if not auth_header: |
|
|
return jsonify({'error': 'Authorization required'}), 401 |
|
|
|
|
|
uid = verify_token(auth_header.split(' ')[1]) |
|
|
if not uid: |
|
|
return jsonify({'error': 'Invalid token'}), 401 |
|
|
|
|
|
orders_ref = db.reference('orders') |
|
|
orders = orders_ref.order_by_child('user_id').equal_to(uid).get() or {} |
|
|
|
|
|
return jsonify([{ |
|
|
'id': order_id, |
|
|
**order_data |
|
|
} for order_id, order_data in orders.items()]) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
|
|
|
@app.route('/api/admin/dashboard', methods=['GET']) |
|
|
def get_admin_dashboard(): |
|
|
try: |
|
|
verify_admin(request.headers.get('Authorization', '')) |
|
|
|
|
|
|
|
|
orders_ref = db.reference('orders') |
|
|
recent_orders = orders_ref.order_by_child('created_at').limit_to_last(10).get() or {} |
|
|
|
|
|
|
|
|
products_ref = db.reference('products') |
|
|
products = products_ref.get() or {} |
|
|
low_stock = [ |
|
|
{'id': pid, **p} |
|
|
for pid, p in products.items() |
|
|
if p.get('active', False) and p.get('stock', 0) < 10 |
|
|
] |
|
|
|
|
|
|
|
|
total_revenue = sum(order['total'] for order in recent_orders.values()) |
|
|
total_orders = len(recent_orders) |
|
|
|
|
|
return jsonify({ |
|
|
'recent_orders': recent_orders, |
|
|
'low_stock_products': low_stock, |
|
|
'statistics': { |
|
|
'total_revenue': total_revenue, |
|
|
'total_orders': total_orders, |
|
|
'total_products': len(products), |
|
|
'low_stock_count': len(low_stock) |
|
|
} |
|
|
}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'error': str(e)}), 500 |
|
|
|
|
|
if __name__ == '__main__': |
|
|
app.run(debug=True, host="0.0.0.0", port=7860) |