diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7bf21e4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,267 @@
+# Project level files
+0.0.2
+alembic.ini
+changes_note.txt
+migrations/
+.dbdata/
+app.dp
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# Backup files #
+*.bak
+
+# C extensions
+*.so
+
+# MacOS stuff
+**/.DS_Store
+
+# Docker ignore
+.dockerignore
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+flask_monitoringdashboard.db
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+*.pot
+*.pyc
+__pycache__
+db.sqlite3
+media
+
+#FastAPI stuff:
+.pytest_cache
+htmlcov
+dist
+site
+.coverage
+coverage.xml
+.netlify
+test.db
+log.txt
+docs_build
+docs.zip
+archive.zip
+
+
+# Flask stuff:
+# instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# Python #
+*.py[cod]
+*$py.class
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv/
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# If you are using PyCharm #
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# File-based project format
+*.iws
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Distribution / packaging
+.Python build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+.pytest_cache/
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+
+# .key
+SECRET.key
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2dbbbe6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,180 @@
+
+
+
+

+
+
Python Fast API boilerplate
+
+
+ Fast API boiler plate project
+
+
+
+
+
+
+
+ Table of Contents
+
+ -
+ About The Project
+
+
+ -
+ Getting Started
+
+
+ - License
+
+
+
+
+## About The Project
+
+FastAPI boilerplate provides a simple basic structure for project creation with mysql database.
+
+
+### Built With
+
+* [![Python][Python]][Python-url]
+* [![FastAPI][FastAPI]][FastAPI-url]
+
+
+## Getting Started
+
+Instructions for setting up project locally.
+To get a local copy up and running follow these simple steps.
+
+## Install + configure the project
+
+### 1. Linux
+### Prerequisites
+
+Requirement of Project
+* Install Python
+ ```sh
+ Python-Version : 3.11.0
+ ```
+* Create python virtual environment
+ ```sh
+ python3 -m venv venv
+ ```
+* Activate the python virtual environment
+ ```sh
+ source venv/bin/activate
+ ```
+
+### Installation
+
+1. Clone the repo
+ ```sh
+ git clone https://github.com/viitoradmin/python-fastapi-boilerplate/tree/feature/fastapi
+ ```
+2. Upgrade pip version
+ ```sh
+ python -m pip install --upgrade pip==22.1.2
+ ```
+3. Install the requirements for the project into the virtual environment
+ ```sh
+ pip install -r requirements.txt
+ ```
+4. Install the dependencies of Fast API
+ ```sh
+ pip install "fastapi[all]"
+ ```
+
+### 2. Windows
+
+1. Create python virtual environment
+ ```
+ conda create --name venv python=3.11
+ ```
+
+2. Activate the python virtual environment
+ ```
+ conda activate venv
+ ```
+
+3. Install the requirements for the project into the virtual environment in the following sequence:
+ ```
+ pip install -r requirements.txt
+ ```
+
+4. Install the dependencies of Fast API
+ ```
+ pip install "fastapi[all]"
+ ```
+
+5. Upgrade pip version
+ ```
+ python -m pip install --upgrade pip==22.1.2
+ ```
+
+### Use the alembic to Upgrade/Downgrade the database in the FastAPI
+ Note: Because by default Fastapi is provide only initial migrations.
+ It doesn't support the upgrade and downgrade the database.
+ so,to perform automatic migrations follow the following steps:
+
+
+1. To create Migration folder
+ ```
+ python -m alembic init migrations
+ ```
+2. Update the sqlalchemy.url into alembic.ini file
+ ```
+ sqlalchemy.url = mysql+pymysql://user:pass@host/db_name
+ ```
+
+3. update the Migrations>>env.py file o auto migrate the database.
+ ```
+ from models import Base
+ target_database = Base.metadata
+ ```
+
+4. Perform the initial migrations
+ ```
+ alembic revision --autogenerate -m 'initials'
+ ```
+
+5. Apply the changes into the database (upgrade the database)
+ ```
+ alembic upgrade head
+ ```
+
+6. To downgrade the database if required
+ ```
+ alembic downgrade -1
+ ```
+
+## Run the server in development mode
+
+Add environment variables (given in .env) by running following command in cmd/terminal:
+
+Run the server
+ ```
+ python asgi.py
+ ```
+Browse Swagger API Doc at: http://localhost:8000/docs
+
+Browse Redoc at: http://localhost:8000/redoc
+
+Browse Swagger API Doc for version v1 at: http://localhost:8000/v1/docs
+
+Browse Swagger API Doc for version v2 at: http://localhost:8000/v2/docs
+
+## Release History
+
+* 0.1
+ * Work in progress
+
+
+
+[Python]: https://img.shields.io/badge/Python-000000?style=for-the-badge&logo=python&logoColor=Blue
+[Python-url]: https://docs.python.org/3.10/
+[FastAPI]: https://img.shields.io/badge/FastAPI-20232A?style=for-the-badge&logo=fastapi&logoColor=009485
+[FastAPI-url]: https://fastapi.tiangolo.com/
\ No newline at end of file
diff --git a/apps/__init__.py b/apps/__init__.py
new file mode 100644
index 0000000..b003693
--- /dev/null
+++ b/apps/__init__.py
@@ -0,0 +1,36 @@
+"""This module is include API's route."""
+from fastapi import FastAPI
+
+from apps.api.auth.models import Base as authbase
+from apps.api.auth.view import router
+from apps.constant import constant
+from config import cors, database
+from apps.middleware.custom_middleware import CustomMiddleware
+from apps.middleware.request_size_middleware import LimitRequestSizeMiddleware
+from apps.middleware.logging_middleware import LogRequestsMiddleware
+from apps.middleware.authentication_middleware import AuthMiddleware
+from apps.middleware.session_middleware import SessionMiddleware
+from apps.middleware.rate_limiting_middleware import RateLimitMiddleware
+from apps.middleware.error_handling_middleware import ErrorHandlingMiddleware
+
+# Bind with the database, whenever new models find it's create it.
+authbase.metadata.create_all(bind=database.engine)
+
+# Create app object and add routes
+app = FastAPI(title="Python FastAPI boilerplate", middleware=cors.middleware)
+
+# add middlewares
+app.add_middleware(CustomMiddleware)
+app.add_middleware(LimitRequestSizeMiddleware, max_body_size=10 * 1024 * 1024)
+app.add_middleware(LogRequestsMiddleware)
+app.add_middleware(AuthMiddleware, secure_token="secure_token")
+app.add_middleware(SessionMiddleware, secure_token="new_session_token")
+app.add_middleware(RateLimitMiddleware, rate_limit=5, time_window=60)
+app.add_middleware(ErrorHandlingMiddleware)
+
+# define router for different version
+# router for API's
+app.include_router(
+ router,
+ prefix=constant.API_V1
+ )
diff --git a/apps/api/auth/method.py b/apps/api/auth/method.py
new file mode 100644
index 0000000..96c0fb8
--- /dev/null
+++ b/apps/api/auth/method.py
@@ -0,0 +1,23 @@
+"""This module contains database operations methods."""
+from sqlalchemy.orm import Session
+
+from apps.constant import constant
+
+
+class UserAuthMethod():
+ """This class defines methods to authenticate users."""
+
+ def __init__(self, model) -> constant.STATUS_NULL:
+ self.model = model
+
+ def find_by_email(self, db: Session, email: str):
+ """This funtion will return the email object"""
+ return db.query(self.model).filter(
+ self.model.email == email
+ ).first()
+
+ def find_by_username(self, db: Session, username: str):
+ """This function will return the username object"""
+ return db.query(self.model).filter(
+ self.model.username == username
+ ).first()
diff --git a/apps/api/auth/models.py b/apps/api/auth/models.py
new file mode 100644
index 0000000..8e2af2e
--- /dev/null
+++ b/apps/api/auth/models.py
@@ -0,0 +1,27 @@
+"""This module contains database model implementations."""
+from datetime import datetime
+
+from sqlalchemy import Column, DateTime, Integer, String
+
+from config.database import Base
+
+
+class Users(Base):
+ """
+ Table used for stored the users information
+ """
+ __tablename__ = "users"
+
+ id = Column(Integer, primary_key=True, nullable=False)
+ uuid = Column(String(100), nullable=False, unique=True, doc='unique_id')
+ first_name = Column(String(255), doc='First name of user', nullable=True)
+ last_name = Column(String(255), doc='Last name of user', nullable=True)
+ email = Column(String(100), doc='Email ID of the user', nullable=True)
+ username = Column(String(100), doc='Username of the user', nullable=True)
+ password = Column(String(100), doc='Password of the user', nullable=True)
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False,
+ doc='its generate automatically when data create')
+ updated_at = Column(DateTime, nullable=True,
+ onupdate=datetime.utcnow, doc='its generate automatically when data update')
+ deleted_at = Column(DateTime, nullable=True,
+ doc='its generate automatically when data deleted')
diff --git a/apps/api/auth/response.py b/apps/api/auth/response.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/api/auth/schema.py b/apps/api/auth/schema.py
new file mode 100644
index 0000000..8b2e550
--- /dev/null
+++ b/apps/api/auth/schema.py
@@ -0,0 +1,24 @@
+"""This module is for swager and request parameter schema"""
+from pydantic import BaseModel
+
+
+class UserAuth(BaseModel):
+ """This class is for user schema."""
+ first_name: str
+ last_name: str
+ email: str
+ password: str
+ username: str
+
+ class Config:
+ """This class is the schema for user configuration."""
+ from_attributes = True
+ json_schema_extra = {
+ "example": {
+ "first_name": "John",
+ "last_name": "Smith",
+ "email": "jhohnsmith@example.com",
+ "password": "Abc@123",
+ "username": "Jhon123"
+ }
+ }
diff --git a/apps/api/auth/service.py b/apps/api/auth/service.py
new file mode 100644
index 0000000..c9b2443
--- /dev/null
+++ b/apps/api/auth/service.py
@@ -0,0 +1,105 @@
+"""This module contains API's specific functionality."""
+import uuid
+
+from fastapi import status
+from fastapi.encoders import jsonable_encoder
+from sqlalchemy.orm import Session
+
+from apps.api.auth.method import UserAuthMethod
+from apps.api.auth.models import Users
+from apps.api.core import db_methods
+from apps.api.core.validation import ValidationMethods
+from apps.constant import constant
+from apps.utils.message import ErrorMessage, InfoMessage
+from apps.utils.standard_response import StandardResponse
+
+
+class UserAuthService:
+ """This class represents the user creation service"""
+ def create_user_service(self, db: Session, body: dict):
+ """This function is used to create user
+ Args:
+ db (Session): database connection
+ body (dict): dictionary to user information data
+
+ Returns:
+ response (dict): user object representing the user
+ """
+ username = body['username']
+ email = body['email'] or None
+ password = body['password']
+
+ # check Email exists in body or not
+ if "email" not in body or not email:
+ return StandardResponse(
+ False, status.HTTP_400_BAD_REQUEST, None, ErrorMessage.emailRequired
+ )
+
+ # check Email exists in Db or not
+ if (user_object := UserAuthMethod(Users).find_by_email(db, email)):
+ return StandardResponse(
+ False, status.HTTP_400_BAD_REQUEST,
+ constant.STATUS_NULL, ErrorMessage.emailAlreadyExist
+ ).make
+
+ # For password validation
+ if not ValidationMethods().password_validator(password):
+ return StandardResponse(
+ False, status.HTTP_400_BAD_REQUEST,
+ constant.STATUS_NULL, ErrorMessage.invalidPasswordFormat
+ ).make
+
+ user_object = Users(
+ uuid = uuid.uuid4(),
+ first_name=body['first_name'],
+ last_name=body['last_name'],
+ username=username,
+ email=email,
+ password=password
+ )
+
+ # Store user object in database
+ if not(db_methods.BaseMethods(Users).save(user_object, db)):
+ return StandardResponse(
+ False, status.HTTP_400_BAD_REQUEST,
+ constant.STATUS_NULL, ErrorMessage.userNotSaved
+ )
+
+ # check email exists or not
+ if user_object := UserAuthMethod(Users).find_by_email(db, email):
+ data = {
+ "username": user_object.username,
+ "first_name": user_object.first_name,
+ "last_name": user_object.last_name,
+ "email": user_object.email,
+ "password": user_object.password,
+ }
+
+ else:
+ return StandardResponse(
+ False, status.HTTP_400_BAD_REQUEST,
+ constant.STATUS_NULL, ErrorMessage.userInvalid
+ ).make
+
+ return StandardResponse(
+ True, status.HTTP_200_OK, data, InfoMessage.userCreated
+ ).make
+
+ def get_user_service(self, db):
+ """This function returns the user service list."""
+ if not (user_object := db_methods.BaseMethods(Users).find_by_uuid(db)):
+ return StandardResponse(
+ False,
+ status.HTTP_400_BAD_REQUEST,
+ None,
+ ErrorMessage.userNotFound
+ )
+ # convert the object data into json
+ user_data = jsonable_encoder(user_object)
+
+ return StandardResponse(
+ True,
+ status.HTTP_200_OK,
+ user_data,
+ InfoMessage.retriveInfoSuccessfully
+ )
diff --git a/apps/api/auth/test.py b/apps/api/auth/test.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/api/auth/view.py b/apps/api/auth/view.py
new file mode 100644
index 0000000..f89df38
--- /dev/null
+++ b/apps/api/auth/view.py
@@ -0,0 +1,44 @@
+"""This module is responsible to contain API's endpoint"""
+from fastapi import APIRouter, Depends, status
+from sqlalchemy.orm import Session
+
+from apps.api.auth import schema
+from apps.api.auth.service import UserAuthService
+from apps.constant import constant
+from apps.utils.standard_response import StandardResponse
+from config import database
+
+## Load API's
+router = APIRouter()
+getdb = database.get_db
+
+## Define verison 1 API's here
+class UserCrudApi():
+ """This class is for user's CRUD operation with version 1 API's"""
+ @router.get('/users')
+ async def list_user(db: Session = Depends(getdb)):
+ """This API is for list user.
+ Args: None
+ Returns:
+ response: will return users list."""
+ try:
+ response = UserAuthService().get_user_service(db)
+ return response
+ except Exception:
+ return StandardResponse(False, status.HTTP_400_BAD_REQUEST, None, constant.ERROR_MSG)
+
+ @router.post('/create/user')
+ async def create_user(body: schema.UserAuth,
+ db: Session = Depends(getdb)):
+ """This API is for create user.
+ Args:
+ body(dict) : user's data
+ Returns:
+ response: will return the user's data"""
+ try:
+ # as per pydantic version 2.
+ body = body.model_dump()
+ response = UserAuthService().create_user_service(db, body)
+ return response
+ except Exception:
+ return StandardResponse(False, status.HTTP_400_BAD_REQUEST, None, constant.ERROR_MSG)
\ No newline at end of file
diff --git a/apps/api/core/db_methods.py b/apps/api/core/db_methods.py
new file mode 100644
index 0000000..c11388d
--- /dev/null
+++ b/apps/api/core/db_methods.py
@@ -0,0 +1,39 @@
+"""This module contains databse methods."""
+from fastapi import Depends
+from sqlalchemy.orm import Session
+
+from apps.constant import constant
+from config import database
+
+getdb = database.get_db
+
+class BaseMethods():
+ """This class provides basic DB methods"""
+
+ def __init__(self, model):
+ self.model = model
+
+ def save(self, validate_data, db: Session = Depends(getdb)):
+ """this function saves the object to the database for the given model
+ Args:
+ validate_data (dict): validate data
+ db (Session): database session.
+ Returns:
+ returns the created object
+ """
+ try:
+ db.add(validate_data)
+ db.commit()
+ db.refresh(validate_data)
+ return constant.STATUS_TRUE
+ except Exception as err:
+ print(err)
+ db.rollback()
+ db.close()
+ return constant.STATUS_FALSE
+
+ def find_by_uuid(self, db: Session = Depends(getdb)):
+ """This function is used to find users."""
+ return db.query(self.model).filter(self.model.deleted_at == constant.STATUS_NULL).all()
+
+
\ No newline at end of file
diff --git a/apps/api/core/validation.py b/apps/api/core/validation.py
new file mode 100644
index 0000000..e005097
--- /dev/null
+++ b/apps/api/core/validation.py
@@ -0,0 +1,35 @@
+"""This module contains validations functionality."""
+import re
+
+class ValidationMethods:
+ """This class contains not null validation"""
+ def not_null_validator(self, v, field):
+ """This function is used to check whether a field is not null"""
+ if v == '':
+ raise ValueError(f'{field} must be required')
+ return v
+
+ def password_validator(self, v):
+ """This function is used to validate the password."""
+ reg = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!#%*?&]{6,20}$"
+ pattern = re.compile(reg)
+
+ if not(re.search(pattern, v)):
+ # Length should be at least 6
+ # Length should be not be greater than 20
+ # Password should have at least one numeral
+ # Password should have at least one uppercase letter
+ # Password should have at least one lowercase letter
+ # Password should have at least one of the symbols $@#
+ return False
+ return True
+
+ def email_validator(self, v):
+ """This function checks if the email address is valid or not."""
+ # regular expression for email validation
+ reg = "([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\"([]!#-[^-~ \t]|(\\[\t -~]))+\")@([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*])"
+
+ pattern = re.compile(reg)
+ if not(re.search(pattern, v)):
+ raise ValueError("Invalid Email format")
+ return v
diff --git a/apps/constant/constant.py b/apps/constant/constant.py
new file mode 100644
index 0000000..87fe12d
--- /dev/null
+++ b/apps/constant/constant.py
@@ -0,0 +1,21 @@
+"""This module contains constant messages."""
+
+STATUS_SUCCESS = "success"
+STATUS_FAIL = "fail"
+STATUS_ERROR = "error"
+STATUS_NULL = None
+STATUS_TRUE = True
+STATUS_FALSE = False
+API_V1 = "/v1"
+API_V2 = "/v2"
+
+## success message ##
+USER_CREATED = "User created successfully!"
+USER_UPDATED = "User updated sucessfully!"
+USER_DELETED = "User deleted successfully!"
+USER_LIST = "List of all users!"
+
+
+## error message ##
+ERROR_MSG = "Error while creating user!"
+LIST_ERROR_MSG = "There is no list to retrieve!!"
diff --git a/apps/middleware/__init__.py b/apps/middleware/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/apps/middleware/__init__.py
@@ -0,0 +1 @@
+
diff --git a/apps/middleware/authentication_middleware.py b/apps/middleware/authentication_middleware.py
new file mode 100644
index 0000000..7067ec5
--- /dev/null
+++ b/apps/middleware/authentication_middleware.py
@@ -0,0 +1,21 @@
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+from starlette.responses import Response, JSONResponse
+
+
+
+class AuthMiddleware(BaseHTTPMiddleware):
+ def __init__(self, app, secure_token: str):
+ super().__init__(app)
+ self.secure_token = secure_token
+
+ async def dispatch(self, request: Request, call_next):
+ token = request.headers.get("Authorization")
+
+ # Validate the Authorization token
+ if not token or token != f"Bearer {self.secure_token}":
+ return JSONResponse(content={"error": "Unauthorized"}, status_code=401)
+
+ # Proceed to the next middleware or endpoint
+ response = await call_next(request)
+ return response
diff --git a/apps/middleware/cors_middleware.py b/apps/middleware/cors_middleware.py
new file mode 100644
index 0000000..c194b0d
--- /dev/null
+++ b/apps/middleware/cors_middleware.py
@@ -0,0 +1,10 @@
+from starlette.middleware.cors import CORSMiddleware
+
+
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"], # Replace "*" with specific origins for better security
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
diff --git a/apps/middleware/custom_middleware.py b/apps/middleware/custom_middleware.py
new file mode 100644
index 0000000..e092b0b
--- /dev/null
+++ b/apps/middleware/custom_middleware.py
@@ -0,0 +1,17 @@
+from fastapi import FastAPI, Request
+from starlette.middleware.base import BaseHTTPMiddleware
+
+app = FastAPI()
+
+class CustomMiddleware(BaseHTTPMiddleware):
+ async def dispatch(self, request: Request, call_next):
+ # Logic before processing the request
+ print("Before Request")
+
+ response = await call_next(request)
+
+ # Logic after processing the request
+ print("After Request")
+ return response
+
+
diff --git a/apps/middleware/error_handling_middleware.py b/apps/middleware/error_handling_middleware.py
new file mode 100644
index 0000000..a3c7050
--- /dev/null
+++ b/apps/middleware/error_handling_middleware.py
@@ -0,0 +1,13 @@
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+from starlette.responses import JSONResponse
+
+class ErrorHandlingMiddleware(BaseHTTPMiddleware):
+ async def dispatch(self, request: Request, call_next):
+ try:
+ # Proceed to the next middleware or endpoint
+ response = await call_next(request)
+ return response
+ except Exception as exc:
+ # Catch any exception and return a JSON response with status 500
+ return JSONResponse(content={"error": str(exc)}, status_code=500)
diff --git a/apps/middleware/logging_middleware.py b/apps/middleware/logging_middleware.py
new file mode 100644
index 0000000..487f8c0
--- /dev/null
+++ b/apps/middleware/logging_middleware.py
@@ -0,0 +1,15 @@
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+from starlette.responses import Response
+
+class LogRequestsMiddleware(BaseHTTPMiddleware):
+ async def dispatch(self, request: Request, call_next):
+ # Log incoming request details
+ print(f"Incoming request: {request.method} {request.url}")
+
+ # Call the next middleware or endpoint
+ response = await call_next(request)
+
+ # Log response details
+ print(f"Response status: {response.status_code}")
+ return response
diff --git a/apps/middleware/rate_limiting_middleware.py b/apps/middleware/rate_limiting_middleware.py
new file mode 100644
index 0000000..4885130
--- /dev/null
+++ b/apps/middleware/rate_limiting_middleware.py
@@ -0,0 +1,38 @@
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+from starlette.responses import Response, JSONResponse
+from time import time
+
+
+class RateLimitMiddleware(BaseHTTPMiddleware):
+ def __init__(self, app, rate_limit: int, time_window: int = 60):
+ """
+ :param app: The FastAPI app
+ :param rate_limit: Maximum number of requests allowed
+ :param time_window: Time window in seconds for rate limiting (default is 60 seconds)
+ """
+ super().__init__(app)
+ self.rate_limit = rate_limit
+ self.time_window = time_window
+ self.request_count = 0
+ self.window_start_time = time()
+
+ async def dispatch(self, request: Request, call_next):
+ current_time = time()
+
+ # Check if the time window has expired
+ if current_time - self.window_start_time > self.time_window:
+ # Reset the window
+ self.window_start_time = current_time
+ self.request_count = 0
+
+ # Enforce rate limiting
+ if self.request_count >= self.rate_limit:
+ return JSONResponse(content={"error": "Rate limit exceeded"}, status_code=429)
+
+ # Increment request count
+ self.request_count += 1
+
+ # Process the request
+ response = await call_next(request)
+ return response
diff --git a/apps/middleware/request_size_middleware.py b/apps/middleware/request_size_middleware.py
new file mode 100644
index 0000000..9ead88e
--- /dev/null
+++ b/apps/middleware/request_size_middleware.py
@@ -0,0 +1,17 @@
+from fastapi import status, HTTPException
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+
+
+class LimitRequestSizeMiddleware(BaseHTTPMiddleware):
+ def __init__(self, app, max_body_size: int):
+ super().__init__(app)
+ self.max_body_size = max_body_size
+
+ async def dispatch(self, request: Request, call_next):
+ if int(request.headers.get("content-length", 0)) > self.max_body_size:
+ raise HTTPException(
+ detail="Request entity too large",
+ status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
+ )
+ return await call_next(request)
diff --git a/apps/middleware/response_time_middleware.py b/apps/middleware/response_time_middleware.py
new file mode 100644
index 0000000..58ebe5f
--- /dev/null
+++ b/apps/middleware/response_time_middleware.py
@@ -0,0 +1,13 @@
+import time
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+from starlette.responses import Response
+
+
+class TimingMiddleware(BaseHTTPMiddleware):
+ async def dispatch(self, request: Request, call_next):
+ start_time = time.time()
+ response: Response = await call_next(request)
+ process_time = time.time() - start_time
+ print(f"Request processing time: {process_time:.2f} seconds")
+ return response
diff --git a/apps/middleware/session_middleware.py b/apps/middleware/session_middleware.py
new file mode 100644
index 0000000..c5f990c
--- /dev/null
+++ b/apps/middleware/session_middleware.py
@@ -0,0 +1,22 @@
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+from starlette.responses import Response, JSONResponse
+
+class SessionMiddleware(BaseHTTPMiddleware):
+ def __init__(self, app, new_session_token: str):
+ super().__init__(app)
+ self.new_session_token = new_session_token
+
+ async def dispatch(self, request: Request, call_next):
+ # Check for the session token in cookies
+ session_token = request.cookies.get("session_token")
+ if not session_token:
+ # Return a response indicating the session has expired
+ response = JSONResponse(content={"error": "Session expired"}, status_code=403)
+ # Set a new session token in the cookies
+ response.set_cookie(key="session_token", value=self.new_session_token)
+ return response
+
+ # Proceed to the next middleware or endpoint
+ response = await call_next(request)
+ return response
diff --git a/apps/utils/helper.py b/apps/utils/helper.py
new file mode 100644
index 0000000..f9507ae
--- /dev/null
+++ b/apps/utils/helper.py
@@ -0,0 +1,19 @@
+"""This moduel is used to convert password into hash string."""
+from passlib.context import CryptContext
+
+class PasswordUtils():
+ """This class is used to manage password management"""
+
+ def __init__(self):
+ self.pwd_context = CryptContext(schemes=['bcrypt'], deprecated="auto")
+
+ def hash_password(self, password: str):
+ """
+ This function is used to hash password
+ Arguments:
+ password(str) : password argument of string format.
+
+ Returns:
+ Hash of the password
+ """
+ return self.pwd_context.hash(password)
diff --git a/apps/utils/message.py b/apps/utils/message.py
new file mode 100644
index 0000000..180d3b6
--- /dev/null
+++ b/apps/utils/message.py
@@ -0,0 +1,18 @@
+"""This module contains predefined messages."""
+
+class InfoMessage:
+ """This class contains information messages."""
+ userCreated = "User created successfully"
+ retriveInfoSuccessfully = "Retrive user info successfully"
+
+class ErrorMessage:
+ """This class contains error messages."""
+ emailRequired = "Email is required"
+ usernameRequired = "Username is required"
+ usernameAlreadyExists = "Username already exists"
+ userNotSaved = "User is not saved"
+ emailRequired = "E-Mail is required"
+ emailAlreadyExist = "E-Mail already exists."
+ invalidPasswordFormat = "Password format is invalid"
+ userNotFound = "User is not found"
+ userInvalid = "user is invalid"
diff --git a/apps/utils/standard_response.py b/apps/utils/standard_response.py
new file mode 100644
index 0000000..af044f2
--- /dev/null
+++ b/apps/utils/standard_response.py
@@ -0,0 +1,37 @@
+"""This module contains standard response class."""
+from fastapi.responses import JSONResponse
+
+from apps.constant import constant
+
+
+class StandardResponse:
+ """This class is universal to return standard API responses
+
+ Attributes:
+ status (int): The http status response from API
+ data (dict/list): The Data from API
+ message (str): The message from the API
+ """
+
+ def __init__(self, status, status_code: int, data: dict, message: str) -> None:
+ """This function defines arguments that are used in the class
+
+ Arguments:
+ status (str): The success/failure status.
+ status_code (int): The http status response from API
+ data (dict/list): The Data from API
+ message (str): The message from the API
+
+ Returns:
+ Returns the API standard response
+ """
+ self.status = status
+ self.status_code = status_code
+ self.data = data
+ self.message = message
+
+ @property
+ def make(self) -> dict:
+ self.status = constant.STATUS_SUCCESS if self.status_code in [201, 200] else constant.STATUS_FAIL
+ response = {'status': self.status, 'data': self.data, 'message': self.message}
+ return JSONResponse(content=response, status_code=self.status_code)
\ No newline at end of file
diff --git a/asgi.py b/asgi.py
index e69de29..0b0d6b4 100644
--- a/asgi.py
+++ b/asgi.py
@@ -0,0 +1,15 @@
+"""This module contains entry point for an application."""
+import os
+
+import uvicorn
+
+from apps.__init__ import app
+from config.env_config import load_dotenv
+
+load_dotenv()
+
+if __name__ == "__main__":
+ uvicorn.run("asgi:app",
+ host=os.environ.get('SERVER_HOST'),
+ port=int(os.environ.get('SERVER_PORT')),
+ reload=True)
diff --git a/config/cors.py b/config/cors.py
new file mode 100644
index 0000000..866126e
--- /dev/null
+++ b/config/cors.py
@@ -0,0 +1,21 @@
+"""This module contains CORS headers."""
+from starlette.middleware import Middleware
+from fastapi.middleware.cors import CORSMiddleware
+
+
+##### CORS configuration #####
+CORS_ALLOW_ORIGINS = ["*"]
+
+CORS_ALLOW_METHODS = ["*"]
+
+CORS_ALLOW_HEADERS = ["*"]
+
+middleware = [
+ Middleware(
+ CORSMiddleware,
+ allow_origins=CORS_ALLOW_ORIGINS,
+ allow_credentials=True,
+ allow_methods=CORS_ALLOW_METHODS,
+ allow_headers=CORS_ALLOW_HEADERS
+ )
+]
diff --git a/config/database.py b/config/database.py
new file mode 100644
index 0000000..2f2b6bc
--- /dev/null
+++ b/config/database.py
@@ -0,0 +1,23 @@
+"""This config file will define Database Url and configurations"""
+from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+
+from config import env_config
+
+# ##### DATABASE CONFIGURATION ############################
+SQLALCHEMY_DATABASE_URL = f'mysql+pymysql://{env_config.DATABASE_USER}:{env_config.DATABASE_PASSWORD}@{env_config.DATABASE_HOST}:{env_config.DATABASE_PORT}/{env_config.DATABASE_NAME}'
+
+# Create engine by sqlalchemy db url
+engine = create_engine(SQLALCHEMY_DATABASE_URL, pool_recycle=3600, pool_pre_ping=True)
+SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
+
+Base = declarative_base()
+
+def get_db():
+ """This function returns the database object."""
+ db = SessionLocal()
+ try:
+ yield db
+ finally:
+ db.close()
diff --git a/config/env_config.py b/config/env_config.py
new file mode 100644
index 0000000..579adf0
--- /dev/null
+++ b/config/env_config.py
@@ -0,0 +1,19 @@
+"""This module contains configuration information."""
+import os
+from os.path import join
+
+from dotenv import load_dotenv
+
+from .project_path import BASE_DIR
+
+##### ENV configuration #####
+dotenv_path = join(BASE_DIR, ".env")
+load_dotenv(dotenv_path)
+
+
+## Database deatils ##
+DATABASE_NAME = os.environ.get("DATABASE_NAME")
+DATABASE_USER = os.environ.get("DATABASE_USER")
+DATABASE_PASSWORD = os.environ.get("DATABASE_PASSWORD")
+DATABASE_HOST = os.environ.get("DATABASE_HOST")
+DATABASE_PORT = os.environ.get("DATABASE_PORT")
diff --git a/config/project_path.py b/config/project_path.py
new file mode 100644
index 0000000..6820465
--- /dev/null
+++ b/config/project_path.py
@@ -0,0 +1,19 @@
+"""This module contains project path information."""
+from os.path import abspath, basename, dirname, join
+
+
+# ##### PATH CONFIGURATION ################################
+
+# fetch FastAPI's project directory
+BASE_DIR = dirname(dirname(abspath(__file__)))
+
+# the name of the whole site
+SITE_NAME = basename(BASE_DIR)
+
+# look for templates here
+# This is an internal setting, used in the TEMPLATES directive
+PROJECT_TEMPLATES_ROOT = join(BASE_DIR, 'assets', 'templates')
+
+PROJECT_STATIC_ROOT = join(BASE_DIR, 'assets', 'static')
+
+MEDIA_ROOT = join(BASE_DIR, 'media')
diff --git a/images/python_logo.png b/images/python_logo.png
new file mode 100644
index 0000000..5a7c311
Binary files /dev/null and b/images/python_logo.png differ
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..24df1dc
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+fastapi==0.111.0
+PyMySQL==1.1.1
+SQLAlchemy==2.0.30
+uvicorn==0.30.0