diff --git a/.env.sample b/.env.sample index b62a25b..e017af1 100644 --- a/.env.sample +++ b/.env.sample @@ -1 +1,2 @@ -MONGO_DETAILS=mongodb://mongourl:port +MONGO_DETAILS=mongodb://mongodb:27017 +secret=guiyfgc837tgf3iw87-012389764 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 3549ecb..a223e29 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,12 @@ FROM python:3.8 +WORKDIR /app -ADD requirements.txt /requirements.txt +ADD requirements.txt /app/requirements.txt -RUN pip install -r requirements.txt +RUN pip install --upgrade -r requirements.txt EXPOSE 80 -COPY ./app /app +COPY ./ /app -CMD ["uvicorn", "app.server.app:app", "--host", "0.0.0.0", "--port", "80"] +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"] \ No newline at end of file diff --git a/README.md b/README.md index 36d12a6..0a987c2 100644 --- a/README.md +++ b/README.md @@ -33,71 +33,19 @@ python app/main.py You also need to start your mongodb instance. -The starter listens on port 8000 on address [0.0.0.0](0.0.0.0). +The starter listens on port 8000 on address [0.0.0.0](0.0.0.0:8080). ![FastAPI-MongoDB starter](https://res.cloudinary.com/adeshina/image/upload/v1600180509/fopab9idhrjqeqds4izk.png) -## Deploying to Vercel +## Deployment -> Currently, the vercel build fails when running requests to MongoDB through the async driver. The next section shows how to deploy to Heroku. +[![Develop on Okteto](https://okteto.com/develop-okteto.svg)](https://cloud.okteto.com/deploy?repository=https://github.com/Youngestdev/fastapi-mongo) -To deploy to [vercel](https://vercel.com), make sure the `vercel` CLI tool is installed and run the command in the base directory: - -```console -vercel -``` - -The above deploys to development, to deploy it into production, run: - -```console -vercel --prod -``` - -Ensure you add the environment variable `MONGO_DETAILS` in vercel. - -## Deploying to Heroku - -To deploy to Heroku, connect your repository to the Heroku application and deploy the branch master. This template has been deployed to Heroku and you can view it here: [FastAPI Mongo](https://fastapi-mongo.herokuapp.com/) - -Ensure you add the environment variable `MONGO_DETAILS` in your application's settings. - -## Dockerising - -To build a docker image for this boilerplate, create a duplicate `.env` file but with name `env`. Next, build an image: - -```console -docker build -t fastapi-mongo . -``` - -The command above builds an image that can be deployed. To run the image in a container: - -```console -docker run --env-file env -d --name fastapi-mongo -p 80:80 fastapi-mongo:latest -``` ## Contributing ? Fork the repo, make changes and send a PR. We'll review it together! -## TODOS - -- [ ] Add a simple bash script file that runs the installation process. - -- [x] Fix the `UPDATE` part of the CRUD operation - -- [x] Add Authentication - -- [x] Add Dockerfile - -- [x] Vercel configuration file - -- [x] Deploying to Heroku - -- [ ] Write a concise README - -- [x] Format code. I'm new to FastAPI so I'll be working towards best practices. - - ## License This project is licensed under the terms of MIT license. diff --git a/app/__init__.py b/__init__.py similarity index 100% rename from app/__init__.py rename to __init__.py diff --git a/app/server/app.py b/app.py similarity index 61% rename from app/server/app.py rename to app.py index e0b68d2..218f8e5 100644 --- a/app/server/app.py +++ b/app.py @@ -1,8 +1,8 @@ from fastapi import FastAPI, Depends -from .auth.jwt_bearer import JWTBearer -from .routes.student import router as StudentRouter -from .routes.admin import router as AdminRouter +from auth.jwt_bearer import JWTBearer +from routes.student import router as StudentRouter +from routes.admin import router as AdminRouter app = FastAPI() @@ -10,7 +10,7 @@ @app.get("/", tags=["Root"]) async def read_root(): - return {"message": "Welcome to this fantastic app, sighs."} + return {"message": "Welcome to this fantastic app."} app.include_router(AdminRouter, tags=["Administrator"], prefix="/admin") diff --git a/app/main.py b/app/main.py deleted file mode 100644 index 7eee0e1..0000000 --- a/app/main.py +++ /dev/null @@ -1,4 +0,0 @@ -import uvicorn - -if __name__ == '__main__': - uvicorn.run('server.app:app', host="0.0.0.0", port=8000, reload=True) \ No newline at end of file diff --git a/app/server/auth/admin.py b/auth/admin.py similarity index 92% rename from app/server/auth/admin.py rename to auth/admin.py index 90bf12b..ac3f5e8 100644 --- a/app/server/auth/admin.py +++ b/auth/admin.py @@ -2,7 +2,7 @@ from fastapi.security import HTTPBasicCredentials, HTTPBasic from passlib.context import CryptContext -from app.server.database.database import admin_collection +from database.database import admin_collection security = HTTPBasic() hash_helper = CryptContext(schemes=["bcrypt"]) diff --git a/app/server/auth/jwt_bearer.py b/auth/jwt_bearer.py similarity index 90% rename from app/server/auth/jwt_bearer.py rename to auth/jwt_bearer.py index f0cc248..be1551e 100644 --- a/app/server/auth/jwt_bearer.py +++ b/auth/jwt_bearer.py @@ -1,17 +1,13 @@ -# Code copied from github.com/overrideveloper/HowAreYouApi* from fastapi import Request, HTTPException from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from .jwt_handler import decodeJWT -# from models.Database import Database class JWTBearer(HTTPBearer): - # db: Database = None def __init__(self, auto_error: bool = True): super(JWTBearer, self).__init__(auto_error=auto_error) - # self.db = db async def __call__(self, request: Request): credentials: HTTPAuthorizationCredentials = await super(JWTBearer, self).__call__(request) diff --git a/app/server/auth/jwt_handler.py b/auth/jwt_handler.py similarity index 97% rename from app/server/auth/jwt_handler.py rename to auth/jwt_handler.py index b61b1e0..0d71462 100644 --- a/app/server/auth/jwt_handler.py +++ b/auth/jwt_handler.py @@ -27,4 +27,4 @@ def decodeJWT(token: str) -> dict: decoded_token = jwt.decode(token.encode(), JWT_SECRET, algorithms=["HS256"]) return decoded_token if decoded_token['expires'] >= time.time() else None except: - return None + return {} diff --git a/app/server/database/database.py b/database/database.py similarity index 95% rename from app/server/database/database.py rename to database/database.py index c5a0009..32feab6 100644 --- a/app/server/database/database.py +++ b/database/database.py @@ -2,7 +2,7 @@ from bson import ObjectId from decouple import config -from app.server.database.database_helper import student_helper, admin_helper +from .database_helper import student_helper, admin_helper MONGO_DETAILS = config('MONGO_DETAILS') diff --git a/app/server/database/database_helper.py b/database/database_helper.py similarity index 100% rename from app/server/database/database_helper.py rename to database/database_helper.py diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d5df18e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3" + +services: + web: + build: . + ports: + - 8080:8080 + env_file: + - .env + + mongodb: + image: bitnami/mongodb:latest + ports: + - 27017 + volumes: + - data:/bitnami/mongodb + + +volumes: + data: \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..46e02bc --- /dev/null +++ b/main.py @@ -0,0 +1,4 @@ +import uvicorn + +if __name__ == '__main__': + uvicorn.run('app:app', host="0.0.0.0", port=8000, reload=True) \ No newline at end of file diff --git a/app/server/models/admin.py b/models/admin.py similarity index 100% rename from app/server/models/admin.py rename to models/admin.py diff --git a/app/server/models/student.py b/models/student.py similarity index 100% rename from app/server/models/student.py rename to models/student.py diff --git a/requirements.txt b/requirements.txt index 9cbcb01..304286e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,25 @@ +bcrypt==3.2.0 +cffi==1.14.2 +click==7.1.2 +dnspython==2.0.0 +email-validator==1.1.1 fastapi==0.61.1 -pydantic==1.6.1 -uvicorn==0.11.8 +h11==0.9.0 +httptools==0.1.1 +idna==2.10 motor==2.2.0 -pydantic[email] -python-decouple -email-validator +nanodb==0.4.3 +nanodb-driver==0.0.3 +passlib==1.7.2 +pycparser==2.20 +pydantic==1.6.1 PyJWT==1.7.1 -passlib -bcrypt +pymongo==3.11.0 +python-decouple==3.3 +PyYAML==3.11 +pyzmq==22.0.3 +six==1.15.0 +starlette==0.13.6 +uvicorn==0.11.8 +uvloop==0.14.0 +websockets==8.1 diff --git a/app/server/routes/admin.py b/routes/admin.py similarity index 71% rename from app/server/routes/admin.py rename to routes/admin.py index 349f77f..e1aa120 100644 --- a/app/server/routes/admin.py +++ b/routes/admin.py @@ -3,11 +3,10 @@ from fastapi.security import HTTPBasicCredentials from passlib.context import CryptContext -from server.database.database import admin_collection -#from app.server.auth.admin import validate_login -from server.auth.jwt_handler import signJWT -from server.database.database import add_admin -from server.models.admin import AdminModel +from database.database import admin_collection +from auth.jwt_handler import signJWT +from database.database import add_admin +from models.admin import AdminModel router = APIRouter() @@ -27,14 +26,6 @@ async def admin_login(admin_credentials: HTTPBasicCredentials = Body(...)): return "Incorrect email or password" - # OLD CODE - # if validate_login(admin): - # return { - # "email": admin.username, - # "access_token": signJWT(admin.username) - # } - # return "Invalid Login Details!" - @router.post("/") async def admin_signup(admin: AdminModel = Body(...)): admin_exists = await admin_collection.find_one({"email": admin.email}, {"_id": 0}) diff --git a/app/server/routes/student.py b/routes/student.py similarity index 94% rename from app/server/routes/student.py rename to routes/student.py index 7624b70..35c7ec3 100644 --- a/app/server/routes/student.py +++ b/routes/student.py @@ -1,8 +1,8 @@ from fastapi import APIRouter, Body from fastapi.encoders import jsonable_encoder -from app.server.database.database import * -from app.server.models.student import * +from database.database import * +from models.student import * router = APIRouter() @@ -21,7 +21,7 @@ async def get_student_data(id): student = await retrieve_student(id) return ResponseModel(student, "Student data retrieved successfully") \ if student \ - else ErrorResponseModel("An error occured.", 404, "Student doesn'exist.") + else ErrorResponseModel("An error occured.", 404, "Student doesn't exist.") @router.post("/", response_description="Student data added into the database") diff --git a/vercel.json b/vercel.json deleted file mode 100644 index 7766548..0000000 --- a/vercel.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "builds": [ - {"src": "/app/server/app.py", "use": "@now/python"} - ], - "routes": [ - {"src": "/(.*)", "dest": "app/server/app.py"} - ] -} \ No newline at end of file