diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b44912e --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +# Copy this file to .env and update with your values + +# Security +SECRET_KEY=change-this-to-a-random-secret-key +FLASK_ENV=development +FLASK_DEBUG=True + +# Database +DB_HOST=localhost +DB_USER=wearwell_user +DB_PASSWORD=your_password_here +DB_NAME=wearwell_db +DB_PORT=3306 + +# Application +FLASK_PORT=5001 +FLASK_HOST=0.0.0.0 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..a45b148 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203, W503 +exclude = .git,__pycache__,venv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a7a60e --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Environment variables +.env + +# Virtual environment +venv/ +env/ + +# Python cache +__pycache__/ +*.pyc + +# IDE +.vscode/ +.idea/ diff --git a/README.md b/README.md index 9aee135..d3cb19b 100644 --- a/README.md +++ b/README.md @@ -1 +1,10 @@ -Shop Project \ No newline at end of file +# WearWell Flask Application + +## Setup +1. Copy `.env.example` to `.env` +2. Update `.env` with your configuration +3. Install dependencies: `pip install -r requirements.txt` +4. Run: `python run.py` + +## Development +- Use `requirements-dev.txt` for development tools diff --git a/app.py b/app.py new file mode 100644 index 0000000..dedd34d --- /dev/null +++ b/app.py @@ -0,0 +1,552 @@ +# app.py - COMPLETE VERSION +import os +from dotenv import load_dotenv +from flask import Flask, jsonify, request +import pymysql +import pymysql.cursors +from config import config + +# Load environment variables at the start +load_dotenv() + +app = Flask(__name__) + +# Configure the app +env = os.getenv('FLASK_ENV', 'development') +app.config.from_object(config[env]) + +print(f"🔧 App configured for: {env}") +print(f"📡 Will run on port: {app.config['FLASK_PORT']}") + +# Database configuration +def get_db_connection(): + db_config = { + 'host': app.config['DB_HOST'], + 'user': app.config['DB_USER'], + 'password': app.config['DB_PASSWORD'], + 'database': app.config['DB_NAME'], + 'port': app.config['DB_PORT'], + 'charset': 'utf8mb4', + 'cursorclass': pymysql.cursors.DictCursor + } + try: + return pymysql.connect(**db_config) + except pymysql.Error as e: + print(f"Database connection error: {e}") + return None + +def init_users_table(): + """Create users table if it doesn't exist""" + conn = get_db_connection() + if conn: + try: + with conn.cursor() as cursor: + cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(80) UNIQUE NOT NULL, + email VARCHAR(120) UNIQUE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + conn.commit() + print("✅ Users table is ready") + except Exception as e: + print(f"Error creating users table: {e}") + finally: + conn.close() + +# Initialize the users table when app starts +init_users_table() + +# Routes +@app.route('/') +def hello(): + return jsonify({ + "message": "WearWell API is running!", + "port": app.config['FLASK_PORT'], + "endpoints": { + "health_check": "/health", + "all_users": "/users", + "get_user": "/users/", + "add_user": "/users/add (POST)", + "delete_user": "/users/delete/ (DELETE)", + "web_interface": "/users/manage" + } + }) + +@app.route('/health') +def health_check(): + conn = get_db_connection() + if conn: + conn.close() + return jsonify({"status": "healthy", "database": "connected"}) + else: + return jsonify({"status": "unhealthy", "database": "disconnected"}), 500 + +# User Management Routes +@app.route('/users') +def get_all_users(): + """Get all users""" + conn = get_db_connection() + if not conn: + return jsonify({"error": "Database connection failed"}), 500 + + try: + with conn.cursor() as cursor: + cursor.execute("SELECT * FROM users ORDER BY created_at DESC") + users = cursor.fetchall() + return jsonify({"users": users, "count": len(users)}) + except Exception as e: + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + +@app.route('/users/') +def get_user(user_id): + """Get a specific user by ID""" + conn = get_db_connection() + if not conn: + return jsonify({"error": "Database connection failed"}), 500 + + try: + with conn.cursor() as cursor: + cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) + user = cursor.fetchone() + + if user: + return jsonify({"user": user}) + else: + return jsonify({"error": "User not found"}), 404 + except Exception as e: + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + +@app.route('/users/add', methods=['POST']) +def add_user(): + """Add a new user""" + conn = get_db_connection() + if not conn: + return jsonify({"error": "Database connection failed"}), 500 + + try: + # Check if request is JSON + if not request.is_json: + return jsonify({"error": "Content-Type must be application/json"}), 400 + + data = request.get_json() + + if not data or not data.get('username') or not data.get('email'): + return jsonify({"error": "Username and email are required"}), 400 + + username = data['username'].strip() + email = data['email'].strip() + + # Basic validation + if len(username) < 3: + return jsonify({"error": "Username must be at least 3 characters"}), 400 + + if '@' not in email: + return jsonify({"error": "Invalid email format"}), 400 + + with conn.cursor() as cursor: + cursor.execute( + "INSERT INTO users (username, email) VALUES (%s, %s)", + (username, email) + ) + conn.commit() + user_id = cursor.lastrowid + + return jsonify({ + "message": "User added successfully", + "user_id": user_id, + "username": username, + "email": email + }), 201 + + except pymysql.IntegrityError as e: + if "username" in str(e): + return jsonify({"error": "Username already exists"}), 400 + elif "email" in str(e): + return jsonify({"error": "Email already exists"}), 400 + else: + return jsonify({"error": "Database integrity error"}), 400 + except Exception as e: + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + +@app.route('/users/delete/', methods=['DELETE']) +def delete_user(user_id): + """Delete a user by ID""" + conn = get_db_connection() + if not conn: + return jsonify({"error": "Database connection failed"}), 500 + + try: + with conn.cursor() as cursor: + # First check if user exists + cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) + user = cursor.fetchone() + + if not user: + return jsonify({"error": "User not found"}), 404 + + # Delete the user + cursor.execute("DELETE FROM users WHERE id = %s", (user_id,)) + conn.commit() + + return jsonify({ + "message": "User deleted successfully", + "deleted_user": { + "id": user_id, + "username": user['username'], + "email": user['email'] + } + }) + + except Exception as e: + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + +@app.route('/users/update/', methods=['PUT']) +def update_user(user_id): + """Update a user's information""" + conn = get_db_connection() + if not conn: + return jsonify({"error": "Database connection failed"}), 500 + + try: + if not request.is_json: + return jsonify({"error": "Content-Type must be application/json"}), 400 + + data = request.get_json() + + if not data or (not data.get('username') and not data.get('email')): + return jsonify({"error": "At least username or email is required to update"}), 400 + + # Build update query dynamically based on provided fields + update_fields = [] + values = [] + + if 'username' in data and data['username']: + update_fields.append("username = %s") + values.append(data['username'].strip()) + + if 'email' in data and data['email']: + update_fields.append("email = %s") + values.append(data['email'].strip()) + + values.append(user_id) + + with conn.cursor() as cursor: + cursor.execute( + f"UPDATE users SET {', '.join(update_fields)} WHERE id = %s", + values + ) + conn.commit() + + if cursor.rowcount == 0: + return jsonify({"error": "User not found"}), 404 + + return jsonify({"message": "User updated successfully"}) + + except pymysql.IntegrityError as e: + if "username" in str(e): + return jsonify({"error": "Username already exists"}), 400 + elif "email" in str(e): + return jsonify({"error": "Email already exists"}), 400 + else: + return jsonify({"error": "Database integrity error"}), 400 + except Exception as e: + return jsonify({"error": str(e)}), 500 + finally: + conn.close() + +# Web Interface +@app.route('/users/manage') +def manage_users(): + """Simple web interface to manage users""" + return ''' + + + + User Management - WearWell + + + +
+

👥 User Management - WearWell

+ +
+

➕ Add New User

+ + + +
+
+ +
+

📋 Users List

+ +
Loading user count...
+
+
+ +
+

🔧 API Testing

+

Test the API endpoints directly:

+ + + +
+
+ + + + + ''' diff --git a/config.py b/config.py new file mode 100644 index 0000000..5f716f1 --- /dev/null +++ b/config.py @@ -0,0 +1,33 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +class Config: + SECRET_KEY = os.getenv('SECRET_KEY', 'dev-fallback-key') + FLASK_ENV = os.getenv('FLASK_ENV', 'development') + DEBUG = os.getenv('FLASK_DEBUG', 'False').lower() == 'true' + + # Database + DB_HOST = os.getenv('DB_HOST', 'localhost') + DB_USER = os.getenv('DB_USER', 'wearwell_user') + DB_PASSWORD = os.getenv('DB_PASSWORD', '') + DB_NAME = os.getenv('DB_NAME', 'wearwell_db') + DB_PORT = int(os.getenv('DB_PORT', 3306)) + + # Application + FLASK_PORT = int(os.getenv('FLASK_PORT', 5001)) + FLASK_HOST = os.getenv('FLASK_HOST', '0.0.0.0') + +class DevelopmentConfig(Config): + DEBUG = True + +class ProductionConfig(Config): + DEBUG = False + FLASK_ENV = 'production' + +config = { + 'development': DevelopmentConfig, + 'production': ProductionConfig, + 'default': DevelopmentConfig +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e6d74ab --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +blinker==1.9.0 +cffi==2.0.0 +click==8.3.1 +cryptography==46.0.3 +Flask==3.1.2 +Flask-MySQLdb==2.0.0 +itsdangerous==2.2.0 +Jinja2==3.1.6 +MarkupSafe==3.0.3 +mysqlclient==2.2.7 +pycparser==2.23 +PyMySQL==1.1.2 +python-dotenv==1.2.1 +Werkzeug==3.1.3 diff --git a/run.py b/run.py new file mode 100644 index 0000000..dcd087a --- /dev/null +++ b/run.py @@ -0,0 +1,21 @@ +# run.py - FIXED VERSION +import os +from dotenv import load_dotenv + +# Load environment variables FIRST +load_dotenv() + +from app import app + +if __name__ == '__main__': + env = os.getenv('FLASK_ENV', 'development') + print(f"🚀 Starting WearWell in {env} mode...") + print(f"📡 Host: {app.config.get('FLASK_HOST', '0.0.0.0')}") + print(f"🔌 Port: {app.config.get('FLASK_PORT', 5000)}") + print(f"🐛 Debug: {app.config.get('DEBUG', False)}") + + app.run( + host=app.config.get('FLASK_HOST', '0.0.0.0'), + port=app.config.get('FLASK_PORT', 5001), + debug=app.config.get('DEBUG', False) + ) diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..85dc36a --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,7 @@ +#!/bin/bash +echo "Setting up WearWell..." +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +cp .env.example .env +echo "Please edit .env with your configuration" diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..6026b0f --- /dev/null +++ b/wsgi.py @@ -0,0 +1,4 @@ +from app import app + +if __name__ == "__main__": + app.run()