Compare commits

...

14 Commits
1.5.8 ... 1.6.5

Author SHA1 Message Date
1dd2fed95f Merge pull request 'sort by date desc' (#61) from feature/add-sort-date into master
Reviewed-on: #61
2025-06-20 22:02:23 +00:00
b7ae697ea6 sort by date desc 2025-06-20 23:58:14 +02:00
03036b2d3b Merge pull request 'fix skip' (#60) from feature/pagination into master
Reviewed-on: #60
2025-03-16 19:52:03 +00:00
e07a74384f fix skip 2025-03-14 23:05:55 +01:00
32b6fdacb6 Merge pull request 'fix form' (#59) from feature/persist-token into master
Reviewed-on: #59
2025-03-06 22:32:19 +00:00
2baaf3c126 fix form 2025-03-06 23:31:37 +01:00
86a3d05f7e Merge pull request 'fix form remember_me' (#58) from feature/persist-token into master
Reviewed-on: #58
2025-03-06 22:23:42 +00:00
9fac430654 fix form remember_me 2025-03-06 23:22:38 +01:00
f56eb9db92 Merge pull request 'fix if' (#57) from feature/persist-token into master
Reviewed-on: #57
2025-03-06 22:13:04 +00:00
15062c029f fix if 2025-03-06 23:12:28 +01:00
6c51c7469b Merge pull request 'add persist token' (#56) from feature/persist-token into master
Reviewed-on: #56
2025-03-06 21:46:00 +00:00
952b0211ba add persist token 2025-03-06 22:42:26 +01:00
ece35338da Merge pull request 'rollback' (#55) from feature/oauth into master
Reviewed-on: #55
2025-03-06 21:01:56 +00:00
221bd1e244 rollback 2025-03-06 22:00:52 +01:00
4 changed files with 12 additions and 86 deletions

View File

@@ -10,34 +10,12 @@ from passlib.context import CryptContext
from ..models import users, token
from ..dependencies import database, cookie
from authlib.integrations.starlette_client import OAuth
import httpx, os
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth = OAuth()
oauth.register(
name="google",
client_id=os.environ["GOOGLE_CLIENT_ID"],
client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
authorize_url="https://accounts.google.com/o/oauth2/auth",
access_token_url="https://oauth2.googleapis.com/token",
client_kwargs={"scope": "openid email profile"},
)
oauth.register(
name="facebook",
client_id=os.environ["FACEBOOK_CLIENT_ID"],
client_secret=os.environ["FACEBOOK_CLIENT_SECRET"],
authorize_url="https://www.facebook.com/v12.0/dialog/oauth",
access_token_url="https://graph.facebook.com/v12.0/oauth/access_token",
client_kwargs={"scope": "email public_profile"},
)
oauth2_scheme = cookie.OAuth2PasswordBearerWithCookie(tokenUrl="token")
def create_access_token(data: dict, expires_delta: timedelta | None = None):
@@ -47,38 +25,6 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None):
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def authenticate_oauth(provider: str, token: str):
"""Validate OAuth token and get user info."""
if provider == "google":
url = f"https://www.googleapis.com/oauth2/v3/userinfo?access_token={token}"
elif provider == "facebook":
url = f"https://graph.facebook.com/me?fields=id,name,email,picture&access_token={token}"
else:
raise HTTPException(status_code=400, detail="Unsupported provider")
async with httpx.AsyncClient() as client:
response = await client.get(url)
if response.status_code != 200:
raise HTTPException(status_code=400, detail="Invalid OAuth token")
user_info = response.json()
email = user_info.get("email")
if not email:
raise HTTPException(status_code=400, detail="Email not provided by provider")
user_repository = users.UserRepository(database=database.database)
user = user_repository.find_one_by({'email': email})
if not user:
user = users.User(
username=email,
email=email,
profile_picture=user_info.get("picture", {}).get("data", {}).get("url", ""),
status=1,
)
user_repository.save(user)
return user
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)

View File

@@ -84,8 +84,7 @@ async def read_events(
status_code=status.HTTP_400_BAD_REQUEST,
detail="`skip` should be >= 0 and `limit` should be > 0 and greater than `skip`.",
)
limit = limit + skip
skip = limit * skip
# Initialize filters
filters = []
@@ -127,7 +126,7 @@ async def read_events(
# Fetch and return results
event_repository = events.EventRepository(database=database.database)
list_events = []
for event_index in event_repository.find_by(object_search, limit=limit, skip=skip):
for event_index in event_repository.find_by(object_search, limit=limit, skip=skip, sort=[("start_date", 1)]):
event = events.EventOut(
id=event_index.id,
tags=event_index.tags,
@@ -166,7 +165,7 @@ async def search_events(
status_code=status.HTTP_400_BAD_REQUEST,
detail="`skip` should be >= 0 and `limit` should be > 0 and greater than `skip`.",
)
limit = limit + skip
skip = limit * skip
# Initialize filters
filters = [{"status": {"$eq": status}}]
@@ -201,7 +200,7 @@ async def search_events(
# Fetch and return results
event_repository = events.EventRepository(database=database.database)
list_events = []
for event_index in event_repository.find_by(object_search, limit=limit, skip=skip):
for event_index in event_repository.find_by(object_search, limit=limit, skip=skip, sort=[("start_date", 1)]):
event = events.EventOut(
id=event_index.id,
tags=event_index.tags,

View File

@@ -1,7 +1,7 @@
from datetime import datetime, timedelta
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status, APIRouter
from fastapi import Depends, FastAPI, HTTPException, status, APIRouter, Form
from fastapi.responses import JSONResponse
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from ..dependencies import users_token, permissions_checker
@@ -11,38 +11,21 @@ from ..models import token, users
router = APIRouter()
ACCESS_TOKEN_EXPIRE_MINUTES = 30
@router.post("/oauth/{provider}", tags=["token"])
async def oauth_login(provider: str, token: str):
"""Handles OAuth login via Google/Facebook."""
user = await users_token.authenticate_oauth(provider, token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication"
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = users_token.create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
content = {"roles": user.roles, "message": "OAuth login successful"}
response = JSONResponse(content=content)
response.set_cookie(key="access_token", value=f"Bearer {access_token}", httponly=True)
return response
@router.post("/token", tags=["token"])
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
remember_me: bool = Form(False)):
user = users_token.authenticate_user(form_data.username, form_data.password)
expires_access_token_time = ACCESS_TOKEN_EXPIRE_MINUTES
if remember_me:
expires_access_token_time=120
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token_expires = timedelta(minutes=expires_access_token_time)
access_token = users_token.create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)

View File

@@ -5,6 +5,4 @@ python-jose[cryptography]
passlib[bcrypt]
python-multipart
fastapi-mail
redis
authlib
httpx
redis