- 🌟 Project Overview
- 🛠️ Setup and Installation
- ⚙️ Configuration
- 🚀 Running the Server
- 📡 API Endpoints
- 🗃️ Database Models
- 🔄 Background Tasks
- 🛑 Error Handling
- ⏱️ Rate Limiting
- 🔒 Security
- 📜 Database Migrations
- 📚 Dependencies
- 🔧 Utilities
- 📁 Project Structure
- ✍️ Contributing
- 📄 License
- 📫 Contact
GeoEgy is a robust backend system built with Flask, designed to manage and process geographical data related to places in Egypt. Leveraging modern technologies and best practices, it offers secure user authentication, efficient order management, administrative controls, and seamless integration with WhatsApp for real-time notifications.
- Scalability: Designed to handle a growing number of users and orders.
- Security: Implements JWT authentication, rate limiting, and security headers.
- Asynchronous Processing: Utilizes Celery for handling long-running tasks without blocking the main application.
- User-Friendly API: Provides clear and consistent API endpoints for frontend integration.
- Comprehensive Logging: Ensures all actions and errors are logged for monitoring and debugging.
- Python 3.7+
- Redis: Required for Celery broker and backend.
- Git: For version control (optional but recommended).
-
Clone the Repository
git clone https://github.com/kariemSeiam/GeoEgy/.git cd GeoEgy/Backend
-
Create a Virtual Environment
It's recommended to use a virtual environment to manage dependencies.
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install Dependencies
Install all required Python packages using
requirements.txt
.pip install -r requirements.txt
-
Set Up Environment Variables
Create a
.env
file in theBackend
directory and populate it with the necessary environment variables. Below is a sample:SECRET_KEY=your_secret_key JWT_SECRET_KEY=your_jwt_secret_key DATABASE_URI=sqlite:///database.db CELERY_BROKER_URL=redis://localhost:6379/0 CELERY_RESULT_BACKEND=redis://localhost:6379/0 TWILIO_ACCOUNT_SID=your_twilio_account_sid TWILIO_AUTH_TOKEN=your_twilio_auth_token TWILIO_WHATSAPP_NUMBER=whatsapp:+14155238886
Note: Replace the placeholder values with your actual credentials. For production environments, ensure that sensitive information is securely managed.
-
Initialize the Database
Perform database migrations to set up the initial schema.
flask db init flask db migrate -m "Initial migration." flask db upgrade
-
Start Redis Server
Ensure that Redis is running, as it's required for Celery.
redis-server
Note: Install Redis from https://redis.io/download if not already installed.
The application configuration is managed via the config.py
file, which loads environment variables and sets default values.
class Config:
SECRET_KEY = os.getenv('SECRET_KEY', 'your_jwt_secret_key')
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY', 'your_jwt_secret_key')
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI', 'sqlite:///database.db')
JWT_ACCESS_TOKEN_EXPIRES = False # Tokens never expire for simplicity
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Celery Configuration
CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL', 'redis://localhost:6379/0')
CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', 'redis://localhost:6379/0')
CELERY_INCLUDE = ['tasks'] # Provide directly as a list
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_ENABLE_UTC = True
# Rate Limiting
RATELIMIT_DEFAULT = '200 per day;50 per hour'
# Security Headers via Flask-Talisman
TALISMAN_CONTENT_SECURITY_POLICY = None # Customize as needed
Key Configuration Parameters:
- SECRET_KEY & JWT_SECRET_KEY: Used for session management and JWT token encryption.
- DATABASE_URI: Specifies the database location.
- Celery Settings: Configure the broker and backend URLs, task serialization, and more.
- Rate Limiting: Sets default rate limits for API requests.
- Security Headers: Managed via Flask-Talisman.
Ensure that your virtual environment is activated and all dependencies are installed.
python app.py
By default, the Flask server runs on http://127.0.0.1:5000/
.
In a separate terminal, activate the virtual environment and start the Celery worker.
python celery_worker.py worker --loglevel=info
Note: Ensure that Redis is running before starting the Celery worker.
The API is organized into three main categories: User, Order, and Admin. Each category has its own set of endpoints to handle various functionalities.
All protected endpoints require a valid JWT access token. Include the token in the Authorization
header as follows:
Authorization: Bearer <access_token>
-
URL:
/api/user/login
-
Method:
POST
-
Description: Authenticates a user using their phone number. If the user does not exist, it registers a new user.
-
Request Body:
{ "phone_number": "01012345678" }
-
Responses:
-
200 OK
{ "code": 200, "message": "تم تسجيل الدخول بنجاح", "data": { "access_token": "<jwt_token>", "user": { "id": 1, "phone_number": "01012345678", "whatsapp_number": null, "creation_date": "2024-11-23T02:50:35.367139", "is_admin": false, "is_blocked": false, "orders": [], "free_place_data_url": "https://example.com/api/free_place_data.json" } } }
-
400 Bad Request
{ "code": 400, "message": "رقم الهاتف مطلوب" }
-
403 Forbidden
{ "code": 403, "message": "حسابك محظور" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/user/profile
-
Method:
GET
-
Description: Retrieves the profile information of the authenticated user.
-
Headers:
Authorization: Bearer <access_token>
-
Responses:
-
200 OK
{ "code": 200, "data": { "id": 1, "phone_number": "01012345678", "whatsapp_number": "01087654321", "creation_date": "2024-11-23T02:50:35.367139", "is_admin": false, "is_blocked": false, "orders": [] } }
-
404 Not Found
{ "code": 404, "message": "المستخدم غير موجود" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/order/place
-
Method:
POST
-
Description: Allows authenticated users to place a new order.
-
Headers:
Authorization: Bearer <access_token>
-
Request Body:
{ "place_name": "Cairo Office", "business_details": "Details about the business.", "selected_govs": ["Cairo", "Giza"], "whatsapp_number": "01087654321" }
Parameters:
place_name
(string, required): Name of the place.business_details
(string, optional): Additional details about the business.selected_govs
(array of strings, required): List of selected governorates. Can include "كل محافظات مصر" for all governorates.whatsapp_number
(string, optional): User's WhatsApp number for notifications.
-
Responses:
-
201 Created
{ "code": 201, "message": "تم تقديم الطلب بنجاح", "data": { "order_id": 123 } }
-
400 Bad Request
{ "code": 400, "message": "يرجى إدخال جميع الحقول المطلوبة" }
Or for invalid governorates:
{ "code": 400, "message": "المحافظات المحددة غير صالحة" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/order/status
-
Method:
GET
-
Description: Retrieves the status of all orders placed by the authenticated user.
-
Headers:
Authorization: Bearer <access_token>
-
Responses:
-
200 OK
{ "code": 200, "data": [ { "order_id": 123, "user_id": 1, "place_name": "Cairo Office", "business_details": "Details about the business.", "selected_govs": ["Cairo", "Giza"], "total_price": 5000.0, "status": "Processing", "json_file_url": "https://example.com/api/orders/123/data.json", "order_date": "2024-11-23T03:00:00.000000", "queue_position": 1 }, // More orders... ] }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
Note: All admin endpoints require the authenticated user to have is_admin
set to true
.
-
URL:
/api/admin/orders
-
Method:
GET
-
Description: Retrieves all orders, ordered by their queue position.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Responses:
-
200 OK
{ "code": 200, "data": [ { "order_id": 123, "user_id": 1, "place_name": "Cairo Office", "business_details": "Details about the business.", "selected_govs": ["Cairo", "Giza"], "total_price": 5000.0, "status": "Processing", "json_file_url": "https://example.com/api/orders/123/data.json", "order_date": "2024-11-23T03:00:00.000000", "queue_position": 1 }, // More orders... ] }
-
403 Forbidden
{ "code": 403, "message": "غير مصرح" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/orders/<order_id>/accept
-
Method:
POST
-
Description: Accepts an order and changes its status to "Processing". Initiates background processing.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Parameters:
order_id
(integer, path parameter): ID of the order to accept.
-
Responses:
-
200 OK
{ "code": 200, "message": "تم قبول الطلب وسيتم معالجته قريباً" }
-
400 Bad Request
{ "code": 400, "message": "لا يمكن قبول هذا الطلب في الوقت الحالي" }
-
404 Not Found
{ "code": 404, "message": "الطلب غير موجود" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/orders/<order_id>/complete
-
Method:
POST
-
Description: Completes an order by updating its status to "Completed" and setting the JSON file URL.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Parameters:
order_id
(integer, path parameter): ID of the order to complete.
-
Request Body:
{ "json_file_url": "https://example.com/api/orders/123/data.json" }
-
Responses:
-
200 OK
{ "code": 200, "message": "تم إكمال الطلب بنجاح" }
-
400 Bad Request
{ "code": 400, "message": "يرجى توفير رابط ملف JSON" }
Or for invalid status:
{ "code": 400, "message": "لا يمكن إكمال هذا الطلب في الوقت الحالي" }
-
404 Not Found
{ "code": 404, "message": "الطلب غير موجود" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/governorates
-
Method:
GET
-
Description: Retrieves a list of all governorates.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Responses:
-
200 OK
{ "code": 200, "data": [ { "id": 1, "name": "Cairo", "price": 2000.0 }, // More governorates... ] }
-
403 Forbidden
{ "code": 403, "message": "غير مصرح" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/governorates
-
Method:
POST
-
Description: Adds a new governorate.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Request Body:
{ "name": "Alexandria", "price": 1500.0 }
-
Responses:
-
201 Created
{ "code": 201, "message": "تم إضافة المحافظة بنجاح" }
-
400 Bad Request
{ "code": 400, "message": "يرجى إدخال جميع الحقول المطلوبة" }
Or if the governorate already exists:
{ "code": 400, "message": "اسم المحافظة موجود مسبقاً" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/governorates/<gov_id>/edit
-
Method:
POST
-
Description: Edits an existing governorate.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Parameters:
gov_id
(integer, path parameter): ID of the governorate to edit.
-
Request Body:
{ "name": "New Alexandria", "price": 1800.0 }
-
Responses:
-
200 OK
{ "code": 200, "message": "تم تعديل المحافظة بنجاح" }
-
400 Bad Request
{ "code": 400, "message": "يرجى إدخال جميع الحقول المطلوبة" }
Or if there's a name conflict:
{ "code": 400, "message": "اسم المحافظة موجود مسبقاً" }
-
404 Not Found
{ "code": 404, "message": "المحافظة غير موجودة" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/governorates/<gov_id>/delete
-
Method:
POST
-
Description: Deletes an existing governorate.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Parameters:
gov_id
(integer, path parameter): ID of the governorate to delete.
-
Responses:
-
200 OK
{ "code": 200, "message": "تم حذف المحافظة بنجاح" }
-
404 Not Found
{ "code": 404, "message": "المحافظة غير موجودة" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/users/<user_id>/block
-
Method:
POST
-
Description: Blocks a user from accessing the system.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Parameters:
user_id
(integer, path parameter): ID of the user to block.
-
Responses:
-
200 OK
{ "code": 200, "message": "تم حظر المستخدم بنجاح" }
-
400 Bad Request
{ "code": 400, "message": "المستخدم محظور بالفعل" }
-
404 Not Found
{ "code": 404, "message": "المستخدم غير موجود" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/users/<user_id>/unblock
-
Method:
POST
-
Description: Unblocks a previously blocked user.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Parameters:
user_id
(integer, path parameter): ID of the user to unblock.
-
Responses:
-
200 OK
{ "code": 200, "message": "تم إلغاء حظر المستخدم بنجاح" }
-
400 Bad Request
{ "code": 400, "message": "المستخدم غير محظور" }
-
404 Not Found
{ "code": 404, "message": "المستخدم غير موجود" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
-
URL:
/api/admin/users
-
Method:
GET
-
Description: Retrieves a list of all users.
-
Headers:
Authorization: Bearer <admin_access_token>
-
Responses:
-
200 OK
{ "code": 200, "data": [ { "id": 1, "phone_number": "01012345678", "whatsapp_number": "01087654321", "creation_date": "2024-11-23T02:50:35.367139", "is_admin": false, "is_blocked": false, "orders": [] }, // More users... ] }
-
403 Forbidden
{ "code": 403, "message": "غير مصرح" }
-
500 Internal Server Error
{ "code": 500, "message": "Internal Server Error" }
-
The application uses SQLAlchemy for ORM (Object-Relational Mapping). Below are the primary models used in the application.
Represents a user of the application.
-
Table Name:
user
-
Fields:
Field Name Type Constraints id
Integer Primary Key phone_number
String(20) Unique, Not Nullable whatsapp_number
String(20) Nullable creation_date
DateTime Default: Current UTC Time, Not Nullable is_admin
Boolean Default: False
, Not Nullableis_blocked
Boolean Default: False
, Not Nullableorders
Relationship One-to-Many with Order
(cascade: all, delete-orphan) -
Methods:
-
to_dict()
: Returns a dictionary representation of the user.def to_dict(self): return { 'id': self.id, 'phone_number': self.phone_number, 'whatsapp_number': self.whatsapp_number, 'creation_date': self.creation_date.isoformat(), 'is_admin': self.is_admin, 'is_blocked': self.is_blocked, "orders": self.orders }
-
Represents an order placed by a user.
-
Table Name:
order
-
Fields:
Field Name Type Constraints id
Integer Primary Key user_id
Integer Foreign Key to user.id
, Not Nullable, Indexedplace_name
String(100) Not Nullable business_details
Text Nullable selected_govs
Text Not Nullable (Comma-separated governorates) total_price
Float Not Nullable status
String(50) Default: "Awaiting Payment Confirmation", Not Nullable json_file_url
String(200) Nullable order_date
DateTime Default: Current UTC Time, Not Nullable queue_position
Integer Default: 0, Not Nullable, Indexed -
Methods:
-
to_dict()
: Returns a dictionary representation of the order.def to_dict(self): return { 'order_id': self.id, 'user_id': self.user_id, 'place_name': self.place_name, 'business_details': self.business_details, 'selected_govs': self.selected_govs.split(','), 'total_price': self.total_price, 'status': self.status, 'json_file_url': self.json_file_url, 'order_date': self.order_date.isoformat(), 'queue_position': self.queue_position }
-
Represents a governorate (administrative division).
-
Table Name:
governorate
-
Fields:
Field Name Type Constraints id
Integer Primary Key name
String(50) Unique, Not Nullable, Indexed price
Float Not Nullable -
Methods:
-
to_dict()
: Returns a dictionary representation of the governorate.def to_dict(self): return { 'id': self.id, 'name': self.name, 'price': self.price }
-
Celery is used for handling asynchronous background tasks, such as processing orders and sending WhatsApp notifications.
-
Function:
process_order_task(order_id)
-
Description: Processes an order by generating a JSON file URL, updating the order status, and sending a WhatsApp notification to the user.
-
Workflow:
- Fetch Order: Retrieve the order from the database using
order_id
. - Simulate Processing: Perform data preparation (e.g., data analysis, report generation). Currently simulated with a delay.
- Generate JSON URL: Create a URL pointing to the generated JSON file.
- Update Status: Change the order status to "Completed".
- Commit Changes: Save the updates to the database.
- Send Notification: Notify the user via WhatsApp about the order completion.
- Fetch Order: Retrieve the order from the database using
-
Error Handling:
- Retries: Retries the task up to 3 times in case of failure with a delay of 60 seconds between attempts.
- Failure Handling: If all retries fail, updates the order status to "Failed" and notifies the user about the failure.
-
Code Snippet:
@celery.task(bind=True, max_retries=3, default_retry_delay=60) def process_order_task(self, order_id): try: with celery.flask_app.app_context(): order = Order.query.get(order_id) if not order: logger.error(f"Order not found: order_id={order_id}") return logger.info(f"Processing order_id={order_id}") # Simulate data preparation time.sleep(5) # Replace with actual processing logic # Generate JSON file URL order.json_file_url = f"https://example.com/api/orders/{order.id}/data.json" # Update order status to 'Completed' order.status = 'Completed' db.session.commit() logger.info(f"Order completed: order_id={order_id}") # Send WhatsApp notification to the user message = f"تم إكمال طلبك رقم {order.id}. يمكنك الآن الوصول إلى البيانات من خلال رابط JSON الخاص بك." send_whatsapp_message(order.user.whatsapp_number or order.user.phone_number, message) logger.info(f"WhatsApp notification sent to user_id={order.user_id} for order_id={order_id}") except Exception as exc: logger.error(f"Error processing order_id={order_id}: {exc}") try: self.retry(exc=exc) except self.MaxRetriesExceededError: with celery.flask_app.app_context(): order = Order.query.get(order_id) if order: order.status = 'Failed' db.session.commit() logger.error(f"Order failed after retries: order_id={order_id}") # Notify user about the failure user = User.query.get(order.user_id) if user: failure_message = f"عذراً، حدث خطأ أثناء معالجة طلبك رقم {order.id}. يرجى المحاولة مرة أخرى لاحقاً." send_whatsapp_message(user.whatsapp_number or user.phone_number, failure_message) logger.info(f"WhatsApp failure notification sent to user_id={user.id} for order_id={order.id}")
Custom error handlers are registered to provide consistent and informative error responses.
-
Trigger: When a requested resource is not found.
-
Response:
{ "code": 404, "message": "Not Found" }
-
Trigger: When an unexpected server error occurs.
-
Response:
{ "code": 500, "message": "Internal Server Error" }
Logging: All errors are logged with detailed information to aid in debugging and monitoring.
Flask-Limiter is implemented to prevent abuse by limiting the number of requests a user can make within a specified timeframe.
- Default Limits:
200 per day;50 per hour
- Key Function: Custom key function that uses the user's identity (if authenticated) or the remote IP address.
- User-Based Limiting: Authenticated users are limited based on their user ID.
- IP-Based Limiting: Unauthenticated requests are limited based on the client's IP address.
- Whitelisting: Local requests (e.g., from
127.0.0.1
) are exempt from rate limiting.
The rate limiter is initialized in utils/rate_limiter.py
:
limiter = Limiter(
key_func=get_user_identifier,
default_limits=["200 per day", "50 per hour"],
storage_uri="memory://", # Replace with Redis for production
)
Note: For production environments, it's recommended to use a persistent storage backend like Redis instead of in-memory storage.
Security best practices are implemented to protect the application from common vulnerabilities.
- Library: Flask-JWT-Extended
- Usage: Provides secure token-based authentication.
- Token Management: Tokens do not expire for simplicity (
JWT_ACCESS_TOKEN_EXPIRES = False
). Consider enabling expiration for enhanced security.
-
Purpose: Sets various HTTP security headers to protect against common web vulnerabilities.
-
Configuration: Managed in
utils/security.py
.csp = { 'default-src': [ "'self'", 'https://stackpath.bootstrapcdn.com', 'https://cdnjs.cloudflare.com', # Add other trusted sources ] } Talisman(app, content_security_policy=csp)
- Prevents brute-force attacks and API abuse.
- Ensures that all required fields are provided and valid in API requests.
- Certain IP addresses (e.g., localhost) can be exempted from rate limiting.
Flask-Migrate and Alembic are used to handle database migrations.
-
Initialize Migrations
flask db init
-
Create a Migration Script
flask db migrate -m "Initial migration."
-
Apply Migrations
flask db upgrade
- env.py: Configures the migration environment.
- versions/: Contains migration scripts with unique revision IDs.
Example Migration Script (migrations/versions/4fdcd22313de_.py
):
def upgrade():
# Commands to create tables
op.create_table('governorate', ...)
op.create_table('user', ...)
op.create_table('order', ...)
...
def downgrade():
# Commands to drop tables
op.drop_table('order')
op.drop_table('user')
op.drop_table('governorate')
...
Note: Always review and adjust auto-generated migration scripts as needed before applying them.
All project dependencies are listed in the requirements.txt
file.
- Flask==2.0.1: Web framework.
- Flask-JWT-Extended==4.3.1: JWT authentication.
- Flask-Limiter==2.1.0: Rate limiting.
- Flask-Migrate==3.1.0: Database migrations.
- Flask-SQLAlchemy==2.5.1: ORM.
- Flask-Cors==3.0.10: Cross-Origin Resource Sharing.
- Flask-Talisman==0.8.1: Security headers.
- Celery==5.2.3: Asynchronous task queue.
- redis==4.1.0: Redis client for Celery.
- python-dotenv==0.21.0: Environment variable management.
- Werkzeug==2.0.3: WSGI utility library.
- SQLAlchemy==1.4.46: SQL toolkit and ORM.
- setuptools==59.6.0, click==8.1.7, click-didyoumean==0.3.1, click-plugins==1.1.1: CLI utilities.
- Alembic: Database migrations (included with Flask-Migrate).
Located in utils/auth.py
, the admin_required
decorator ensures that only admin users can access certain endpoints.
def admin_required(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
try:
user_id = get_jwt_identity()
user = User.query.get(user_id)
if not user or not user.is_admin:
logger.warning(f"Unauthorized admin access attempt by user_id={user_id}")
return {'code': 403, 'message': 'غير مصرح'}, 403
except Exception as e:
logger.error(f"Error in admin_required decorator: {e}")
return {'code': 500, 'message': 'Internal Server Error'}, 500
return fn(*args, **kwargs)
return wrapper
Located in utils/export_data.py
, the export_to_csv
function allows exporting data as a CSV file.
def export_to_csv(data):
if not data:
logger.info("No data available to export.")
return Response("No data available to export.", mimetype='text/csv')
headers = data[0].keys()
def generate():
yield ','.join(headers) + '\n'
for row in data:
yield ','.join([str(row.get(header, "")) for header in headers]) + '\n'
response = Response(generate(), mimetype='text/csv')
response.headers.set("Content-Disposition", "attachment", filename="places_data.csv")
logger.info("CSV export initiated.")
return response
Located in utils/rate_limiter.py
, handles rate limiting based on user identity or IP address.
limiter = Limiter(
key_func=get_user_identifier,
default_limits=["200 per day", "50 per hour"],
storage_uri="memory://", # Replace with Redis for production
)
Located in utils/security.py
, initializes security headers using Flask-Talisman.
def init_security(app):
try:
csp = {
'default-src': [
"'self'",
'https://stackpath.bootstrapcdn.com',
'https://cdnjs.cloudflare.com',
# Add other trusted sources
]
}
Talisman(app, content_security_policy=csp)
logger.info("Security headers initialized with Flask-Talisman.")
except Exception as e:
logger.error(f"Error initializing security headers: {e}")
Located in utils/whatsapp.py
, handles sending WhatsApp messages via Twilio's API.
def send_whatsapp_message(phone_number, message):
pass
"""
Send a WhatsApp message using Twilio's WhatsApp API.
Args:
phone_number (str): Recipient's phone number in E.164 format (e.g., +2010XXXXXXX).
message (str): The message to send.
try:
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
whatsapp_from = os.getenv('TWILIO_WHATSAPP_NUMBER') # e.g., 'whatsapp:+14155238886'
if not all([account_sid, auth_token, whatsapp_from]):
logger.error("Twilio credentials are not properly set in environment variables.")
return
client = Client(account_sid, auth_token)
message = client.messages.create(
body=message,
from_=whatsapp_from,
to=f'whatsapp:{phone_number}'
)
logger.info(f"WhatsApp message sent to {phone_number}: SID={message.sid}")
except Exception as e:
logger.error(f"Failed to send WhatsApp message to {phone_number}: {e}")
"""
Note: The actual implementation is commented out. To enable WhatsApp messaging, uncomment and configure the Twilio client as shown in the commented section.
Here's a breakdown of the project's files and directories:
Backend/
├── app.py
├── celery_app.py
├── celery_worker.py
├── config.py
├── extensions.py
├── models.py
├── requirements.txt
├── tasks.py
├── controllers/
│ ├── admin_controller.py
│ ├── order_controller.py
│ └── user_controller.py
├── migrations/
│ ├── env.py
│ └── versions/
│ └── 4fdcd22313de_.py
└── utils/
├── auth.py
├── export_data.py
├── rate_limiter.py
├── security.py
└── whatsapp.py
-
app.py
- Initializes the Flask application.
- Configures extensions (SQLAlchemy, Migrate, JWT, CORS, Limiter, Security).
- Registers blueprints for user, order, and admin controllers.
- Defines routes for serving HTML pages and static files.
- Registers custom error handlers.
-
celery_app.py & celery_worker.py
- Configures and initializes Celery with the Flask application context.
celery_worker.py
serves as the entry point for running Celery workers.
-
config.py
- Manages application configuration, loading from environment variables.
-
extensions.py
- Initializes Flask extensions like SQLAlchemy.
-
models.py
- Defines the database models: User, Order, Governorate.
-
tasks.py
- Contains Celery tasks, such as processing orders asynchronously.
-
controllers/
- user_controller.py: Handles user-related endpoints (login, profile).
- order_controller.py: Manages order-related endpoints (place order, order status).
- admin_controller.py: Manages admin-related endpoints (view orders, manage governorates, manage users).
-
migrations/
- Manages database migrations using Alembic.
- env.py: Configures the migration environment.
- versions/: Contains migration scripts with unique revision IDs.
-
utils/
- auth.py: Authentication decorators.
- export_data.py: Utility for exporting data to CSV.
- rate_limiter.py: Configures rate limiting.
- security.py: Initializes security headers.
- whatsapp.py: Handles WhatsApp messaging via Twilio.
We appreciate your interest in contributing! Here's how you can help:
-
Fork the Repository
Click on Fork at the top right corner of this page.
-
Clone Your Fork
git clone https://github.com/kariemSeiam/GeoEgy/.git
-
Create a New Branch
git checkout -b feature/your-feature-name
-
Make Your Changes
Implement your feature or fix.
-
Commit and Push
git commit -m "Add new feature" git push origin feature/your-feature-name
-
Open a Pull Request
Submit your pull request against the main branch with a detailed description.
This project is released under the MIT License.
Have questions or suggestions? We'd love to hear from you!
- Email: [email protected]
- LinkedIn: Your Name
- GitHub: yourusername
A heartfelt thank you to all contributors and the following resources:
- Flask
- Celery
- Twilio
- Flask-JWT-Extended
- Flask-Limiter
- Flask-Migrate
- SQLAlchemy
- Alembic
- Community Contributors: For their valuable input and support.
The GeoEgy backend is a powerful and flexible system designed to handle complex geographical data processing with ease and security. By following this documentation, developers and contributors can effectively set up, understand, and enhance the application's functionality.