Skip to content

Commit

Permalink
UPDATE: readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mazzasaverio committed Apr 2, 2024
1 parent 39e2527 commit d269d0b
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 127 deletions.
10 changes: 1 addition & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
Sviluppo in locale senza docker

install i pacchetti con poetry andando nella cartella backend

runna

uvicorn app.main:app --reload

Runn con docker invece:
This project serves as a template for building a scalable question answering system using FastAPI, Langchain, and PostgreSQL with the pgvector extension.
13 changes: 10 additions & 3 deletions backend/app/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
from sqlmodel import Session, create_engine, select


engine = create_engine(str(settings.SYNC_DATABASE_URI))

reusable_oauth2 = OAuth2PasswordBearer(
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
)

engine = create_engine(str(settings.SYNC_DATABASE_URI))


def get_db() -> Generator[Session, None, None]:
with Session(engine) as session:
Expand Down Expand Up @@ -53,9 +53,16 @@ def get_current_user(session: SessionDep, token: TokenDep) -> User:


def get_current_active_superuser(current_user: CurrentUser) -> User:
logger.debug(f"################### current_user: {current_user}")
if not current_user.is_superuser:
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
return current_user


def verify_password_reset_token(token: str) -> str | None:
try:
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
return str(decoded_token["sub"])
except JWTError:
return None
104 changes: 12 additions & 92 deletions backend/app/api/routes/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
from fastapi.responses import HTMLResponse
from fastapi.security import OAuth2PasswordRequestForm

from app import crud
from app.api.deps import CurrentUser, SessionDep, get_current_active_superuser
from app.crud import user_crud
from app.api.deps import (
CurrentUser,
SessionDep,
get_current_active_superuser,
verify_password_reset_token,
)
from app.core import security
from app.core.config import settings
from app.core.security import get_password_hash
from app.models import Message, NewPassword, Token, UserOut
from app.utils import (
generate_password_reset_token,
generate_reset_password_email,
send_email,
verify_password_reset_token,
)
from app.models.user_model import Message, NewPassword, Token, UserOut
from app.core.config import logger

router = APIRouter()

Expand All @@ -28,97 +28,17 @@ def login_access_token(
"""
OAuth2 compatible token login, get an access token for future requests
"""
user = crud.authenticate(
user = user_crud.authenticate(
session=session, email=form_data.username, password=form_data.password
)
logger.info(f"User {user} logged in")
if not user:
raise HTTPException(status_code=400, detail="Incorrect email or password")
elif not user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token_expires = timedelta(minutes=100000)
return Token(
access_token=security.create_access_token(
user.id, expires_delta=access_token_expires
)
)


@router.post("/login/test-token", response_model=UserOut)
def test_token(current_user: CurrentUser) -> Any:
"""
Test access token
"""
return current_user


@router.post("/password-recovery/{email}")
def recover_password(email: str, session: SessionDep) -> Message:
"""
Password Recovery
"""
user = crud.get_user_by_email(session=session, email=email)

if not user:
raise HTTPException(
status_code=404,
detail="The user with this email does not exist in the system.",
)
password_reset_token = generate_password_reset_token(email=email)
email_data = generate_reset_password_email(
email_to=user.email, email=email, token=password_reset_token
)
send_email(
email_to=user.email,
subject=email_data.subject,
html_content=email_data.html_content,
)
return Message(message="Password recovery email sent")


@router.post("/reset-password/")
def reset_password(session: SessionDep, body: NewPassword) -> Message:
"""
Reset password
"""
email = verify_password_reset_token(token=body.token)
if not email:
raise HTTPException(status_code=400, detail="Invalid token")
user = crud.get_user_by_email(session=session, email=email)
if not user:
raise HTTPException(
status_code=404,
detail="The user with this email does not exist in the system.",
)
elif not user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
hashed_password = get_password_hash(password=body.new_password)
user.hashed_password = hashed_password
session.add(user)
session.commit()
return Message(message="Password updated successfully")


@router.post(
"/password-recovery-html-content/{email}",
dependencies=[Depends(get_current_active_superuser)],
response_class=HTMLResponse,
)
def recover_password_html_content(email: str, session: SessionDep) -> Any:
"""
HTML Content for Password Recovery
"""
user = crud.get_user_by_email(session=session, email=email)

if not user:
raise HTTPException(
status_code=404,
detail="The user with this username does not exist in the system.",
)
password_reset_token = generate_password_reset_token(email=email)
email_data = generate_reset_password_email(
email_to=user.email, email=email, token=password_reset_token
)

return HTMLResponse(
content=email_data.html_content, headers={"subject:": email_data.subject}
)
7 changes: 3 additions & 4 deletions backend/app/api/routes/qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
from langchain.prompts.prompt import PromptTemplate
from app.schemas.chat_schema import ChatBody
from fastapi import APIRouter, Depends
from app.api.deps import get_current_active_superuser
from app.api.deps import get_current_user
from app.models.user_model import User

router = APIRouter()

Expand All @@ -35,10 +36,8 @@

@router.post("/chat")
async def chat_action(
request: ChatBody,
jwt: Annotated[dict, Depends(get_current_active_superuser)],
request: ChatBody, current_user: Annotated[User, Depends(get_current_user)]
):
logger.info(f"User JWT from request: {jwt}")

embeddings = OpenAIEmbeddings()

Expand Down
22 changes: 13 additions & 9 deletions backend/app/api/routes/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import col, delete, func, select

from app import crud
from app.crud import user_crud
from app.api.deps import (
CurrentUser,
SessionDep,
get_current_active_superuser,
)
from app.core.config import settings
from app.core.security import get_password_hash, verify_password
from app.models import (
from app.models.user_model import (
Item,
Message,
UpdatePassword,
Expand Down Expand Up @@ -52,14 +52,14 @@ def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
"""
Create new user.
"""
user = crud.get_user_by_email(session=session, email=user_in.email)
user = user_crud.get_user_by_email(session=session, email=user_in.email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this email already exists in the system.",
)

user = crud.create_user(session=session, user_create=user_in)
user = user_crud.create_user(session=session, user_create=user_in)
if settings.emails_enabled and user_in.email:
email_data = generate_new_account_email(
email_to=user_in.email, username=user_in.email, password=user_in.password
Expand All @@ -81,7 +81,9 @@ def update_user_me(
"""

if user_in.email:
existing_user = crud.get_user_by_email(session=session, email=user_in.email)
existing_user = user_crud.get_user_by_email(
session=session, email=user_in.email
)
if existing_user and existing_user.id != current_user.id:
raise HTTPException(
status_code=409, detail="User with this email already exists"
Expand Down Expand Up @@ -132,14 +134,14 @@ def create_user_open(session: SessionDep, user_in: UserCreateOpen) -> Any:
status_code=403,
detail="Open user registration is forbidden on this server",
)
user = crud.get_user_by_email(session=session, email=user_in.email)
user = user_crud.get_user_by_email(session=session, email=user_in.email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this email already exists in the system",
)
user_create = UserCreate.from_orm(user_in)
user = crud.create_user(session=session, user_create=user_create)
user = user_crud.create_user(session=session, user_create=user_create)
return user


Expand Down Expand Up @@ -183,13 +185,15 @@ def update_user(
detail="The user with this id does not exist in the system",
)
if user_in.email:
existing_user = crud.get_user_by_email(session=session, email=user_in.email)
existing_user = user_crud.get_user_by_email(
session=session, email=user_in.email
)
if existing_user and existing_user.id != user_id:
raise HTTPException(
status_code=409, detail="User with this email already exists"
)

db_user = crud.update_user(session=session, db_user=db_user, user_in=user_in)
db_user = user_crud.update_user(session=session, db_user=db_user, user_in=user_in)
return db_user


Expand Down
8 changes: 8 additions & 0 deletions backend/ingestion/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
from typing import List
from loguru import logger
import sys
from typing_extensions import Self
import secrets
import warnings
from pydantic import (
model_validator,
)


class Settings(BaseSettings):
Expand All @@ -20,6 +26,8 @@ class Settings(BaseSettings):
FIRST_SUPERUSER: str
FIRST_SUPERUSER_PASSWORD: str

SECRET_KEY: str = secrets.token_urlsafe(32)

@property
def ASYNC_DATABASE_URI(self) -> str:
return f"postgresql+asyncpg://{self.DB_USER}:{self.DB_PASS}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
Expand Down
12 changes: 3 additions & 9 deletions backend/ingestion/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,25 +95,19 @@ async def init_db() -> None:

# Your existing database initialization logic here
# For example, creating extensions or setting up initial data
await create_extension()
logger.info("Database initialized and all tables created if they didn't exist.")
# await create_extension()
# logger.info("Database initialized and all tables created if they didn't exist.")


from sqlmodel import Session, create_engine, select

from ingestion.crud import user_crud
from sqlmodel import Session

engine = create_engine(str(settings.SYNC_DATABASE_URI))


# make sure all SQLModel models are imported (app.models) before initializing DB
# otherwise, SQLModel might fail to initialize relationships properly
# for more details: https://github.com/tiangolo/full-stack-fastapi-template/issues/28


def create_super_user() -> None:

engine = create_engine(str(settings.SYNC_DATABASE_URI))
with Session(engine) as session:

user = session.exec(
Expand Down
2 changes: 1 addition & 1 deletion backend/ingestion/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def _load_docs(
# Example usage
if __name__ == "__main__":

# asyncio.run(init_db())
asyncio.run(init_db())

create_super_user()

Expand Down

0 comments on commit d269d0b

Please sign in to comment.