Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 34 additions & 38 deletions app/api/api_v1/endpoints/login.py
Original file line number Diff line number Diff line change
@@ -1,104 +1,100 @@
from typing import Annotated

import requests
from api import deps
from core.config import settings
from core.security import *
from crud import user
from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from requests import RequestException
from schemas import Message, Token
from sqlalchemy.orm import Session

from core.security import *
from core.config import settings
from api import deps
from crud import user
from schemas import Token, Message
from utils import gatekeeper_logout

router = APIRouter()



@router.post("/access-token/", response_model=Token)
def login_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
db: Session = Depends(deps.get_db)
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
db: Session = Depends(deps.get_db),
) -> Token:
"""
OAuth2 compatible token login, get an [access token, refresh token] pair for future requests
"""

if not settings.USING_GATEKEEPER:

user_db = user.authenticate(
db=db,
email=form_data.username,
password=form_data.password
db=db, email=form_data.username, password=form_data.password
)

if not user_db:
raise HTTPException(
status_code=400,
detail="Incorrect email or password"
)

raise HTTPException(status_code=400, detail="Incorrect email or password")
response_token = Token(
access_token=create_token(user_db.id, settings.ACCESS_TOKEN_EXPIRATION_TIME),
refresh_token=create_token(user_db.id, settings.REFRESH_TOKEN_EXPIRATION_TIME),
token_type="bearer"
access_token=create_token(
user_db.id, settings.ACCESS_TOKEN_EXPIRATION_TIME
),
refresh_token=create_token(
user_db.id, settings.REFRESH_TOKEN_EXPIRATION_TIME
),
token_type="bearer",
)
else:
try:
response = requests.post(
url=str(settings.GATEKEEPER_BASE_URL).rstrip("/") + "/api/login/",
headers={"Content-Type": "application/json"},
json={"username": "{}".format(form_data.username), "password": "{}".format(form_data.password)}
json={
"username": "{}".format(form_data.username),
"password": "{}".format(form_data.password),
},
)
except RequestException:
raise HTTPException(
status_code=400,
detail="Network error during communication with GateKeeper, please try again"
detail="Network error during communication with GateKeeper, please try again",
)

if response.status_code == 401:
raise HTTPException(
status_code=400,
detail="Error, no active account found with these credentials"
detail="Error, no active account found with these credentials",
)

if response.status_code == 400:
raise HTTPException(
status_code=400,
detail="Error, missing username/password values, please enter your username and/or password"
detail="Error, missing username/password values, please enter your username and/or password",
)

response_json = response.json()


if response_json["success"]:

response_token = Token(
access_token=response.json()["access"],
refresh_token=response.json()["refresh"],
token_type="bearer"
token_type="bearer",
)
else:
raise HTTPException(
status_code=400,
detail="Error, unsuccessful login attempt, GateKeeper returned 200 with success==False"
detail="Error, unsuccessful login attempt, GateKeeper returned 200 with success==False",
)

return response_token


@router.post("/logout/", response_model=Message, dependencies=[Depends(deps.is_using_gatekeeper), Depends(deps.get_jwt)])
def logout(
refresh_token: str = Depends(deps.get_refresh_token)
) -> Message:
@router.post(
"/logout/",
response_model=Message,
dependencies=[Depends(deps.is_using_gatekeeper), Depends(deps.get_jwt)],
)
def logout(refresh_token: str = Depends(deps.get_refresh_token)) -> Message:
"""
Logout
"""

gatekeeper_logout(refresh_token)

response_message = Message(
message="Successfully logged out!"
)
response_message = Message(message="Successfully logged out!")

return response_message
52 changes: 24 additions & 28 deletions app/api/api_v1/endpoints/user.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,76 @@
import requests
from fastapi import APIRouter, Depends, HTTPException
from requests import RequestException
from sqlalchemy.orm import Session
from typing import Any

import requests
from api import deps
from api.deps import is_not_using_gatekeeper
from core import settings
from crud import user
from fastapi import APIRouter, Depends, HTTPException
from models import User
from requests import RequestException
from schemas import Message, UserCreate, UserMe
from crud import user
from core import settings

from sqlalchemy.orm import Session

router = APIRouter()



@router.post("/register/", response_model=Message)
def register(
user_information: UserCreate,
db: Session = Depends(deps.get_db)
user_information: UserCreate, db: Session = Depends(deps.get_db)
) -> Message:
"""
Registration API for the service.
"""

pwd_check = settings.PASSWORD_SCHEMA_OBJ.validate(pwd=user_information.password)
if not pwd_check:
raise HTTPException(
status_code=400,
detail="Password needs to be at least 8 characters long,"
"contain at least one uppercase and one lowercase letter, one digit and have no spaces."
"contain at least one uppercase and one lowercase letter, one digit and have no spaces.",
)

if settings.USING_GATEKEEPER:
try:
response = requests.post(
url=str(settings.GATEKEEPER_BASE_URL).strip("/") + "/api/register/",
headers={"Content-Type": "application/json"},
json={"username": user_information.email,
"email": user_information.email, "password": user_information.password}
json={
"username": user_information.email,
"email": user_information.email,
"password": user_information.password,
},
)
except RequestException:
raise HTTPException(
status_code=400,
detail="Error, can't connect to gatekeeper instance."
status_code=400, detail="Error, can't connect to gatekeeper instance."
)

if response.status_code / 100 != 2:
raise HTTPException(
status_code=400,
detail="Error, gatekeeper raise issue with request."
status_code=400, detail="Error, gatekeeper raise issue with request."
)

else:
user_db = user.get_by_email(db=db, email=user_information.email)
if user_db:
raise HTTPException(
status_code=400,
detail="User with email:{} already exists.".format(user_information.email)
detail="User with email:{} already exists.".format(
user_information.email
),
)

user.create(db=db, obj_in=user_information)

response = Message(
message="You have successfully registered!"
)
response = Message(message="You have successfully registered!")

return response


@router.get("/me/", response_model=UserMe, dependencies=[Depends(is_not_using_gatekeeper)])
def get_me(
current_user: User = Depends(deps.get_current_user)
) -> Any:
@router.get(
"/me/", response_model=UserMe, dependencies=[Depends(is_not_using_gatekeeper)]
)
def get_me(current_user: User = Depends(deps.get_current_user)) -> Any:
"""
Returns user email
"""

return current_user
47 changes: 47 additions & 0 deletions app/logging_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import logging.config
import os

LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()


def configure_logging() -> None:
logging.config.dictConfig(
{
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"style": "{",
"datefmt": "%Y-%m-%d %H:%M:%S",
"format": "{asctime} {levelname:<8} [{name}:{lineno}] {message}",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "default",
"level": LOG_LEVEL,
},
},
"root": {
"handlers": ["console"],
"level": LOG_LEVEL,
},
"loggers": {
"api": {
"handlers": ["console"],
"level": LOG_LEVEL,
"propagate": False,
},
"uvicorn": {
"handlers": ["console"],
"level": "WARNING",
"propagate": False,
},
"python_multipart": {
"level": "WARNING",
"propagate": False,
},
},
}
)
34 changes: 26 additions & 8 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import logging
import time
from contextlib import asynccontextmanager

from api.api_v1.api import api_router
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

from api.api_v1.api import api_router

from core.config import settings
from fastapi import FastAPI
from init.init_gatekeeper import register_apis_to_gatekeeper
from init.init_soil_values import insert_soil_values_into_db
from init.init_kc import insert_crop_kc_into_db

from jobs.background_tasks import get_weather_data
from logging_config import configure_logging
from starlette.middleware.cors import CORSMiddleware
from starlette.requests import Request


@asynccontextmanager
async def lifespan(fa: FastAPI):
configure_logging()
insert_soil_values_into_db()
insert_crop_kc_into_db()
scheduler.add_job(get_weather_data, 'cron', day_of_week='*', hour=22, minute=0, second=0)
Expand All @@ -25,14 +29,13 @@ async def lifespan(fa: FastAPI):
yield
scheduler.shutdown()


app = FastAPI(
title="Irrigation Management", openapi_url="/api/v1/openapi.json", lifespan=lifespan
)


jobstores = {
'default': MemoryJobStore()
}
jobstores = {"default": MemoryJobStore()}

scheduler = AsyncIOScheduler(jobstores=jobstores)

Expand All @@ -46,4 +49,19 @@ async def lifespan(fa: FastAPI):
allow_headers=["*"],
)

logger = logging.getLogger(__name__)


@app.middleware("http")
async def log_requests(request: Request, call_next):
"""log request response"""
start_time = time.time()
response = await call_next(request)
duration = time.time() - start_time
logger.info(
f"{request.method} {request.url.path} - {response.status_code} - {duration:.4f}s"
)
return response


app.include_router(api_router, prefix="/api/v1")