Compare commits

..

95 Commits

Author SHA1 Message Date
f880b1e6a4 Merge pull request 'fix list' (#34) from feature/tags-search into master
Reviewed-on: #34
2024-12-18 23:53:28 +01:00
32f571103d fix list 2024-12-18 23:52:42 +01:00
94d6db0866 Merge pull request 'fix list' (#33) from feature/tags-search into master
Reviewed-on: #33
2024-12-18 23:48:18 +01:00
be17ec9202 fix list 2024-12-18 23:47:40 +01:00
9cf1c64ef9 Merge pull request 'feature/tags-search' (#32) from feature/tags-search into master
Reviewed-on: #32
2024-12-18 22:11:35 +01:00
1d7ec61982 add in 2024-12-16 23:53:46 +01:00
3189b08b09 refactor code 2024-12-16 23:51:26 +01:00
81be2a08d1 Merge pull request 'add options i' (#31) from feature/options-case into master
Reviewed-on: #31
2024-11-28 23:19:15 +01:00
7f34e835e3 add options i 2024-11-28 23:17:19 +01:00
721d7a3a5d Merge pull request 'fix end_date' (#30) from feature/searchByDate into master
Reviewed-on: #30
2024-11-23 21:06:36 +01:00
f08bdd5f0a fix end_date 2024-11-23 21:03:44 +01:00
beff787801 Merge pull request 'add selected date' (#29) from feature/searchByDate into master
Reviewed-on: #29
2024-11-23 20:18:57 +01:00
c8bc3cc68a add selected date 2024-11-23 20:13:03 +01:00
f290ea9bd0 Merge pull request 'add parameter date event' (#28) from feature/searchByDate into master
Reviewed-on: #28
2024-11-23 16:25:26 +01:00
ea05db0d91 add parameter date event 2024-11-23 16:12:40 +01:00
36ae3504b8 Merge pull request 'add current_time in list all events' (#27) from feature/listEventDate into master
Reviewed-on: #27
2024-11-10 18:45:39 +01:00
b4e37a71d0 add current_time in list all events 2024-11-10 18:42:13 +01:00
53dc28a8e9 Merge pull request 'feature/listEventDate' (#26) from feature/listEventDate into master
Reviewed-on: #26
2024-11-09 18:20:25 +01:00
49a22bc567 add current_datetime in events 2024-11-09 16:19:52 +01:00
4a484bfb42 add current_time in search 2024-11-09 16:04:47 +01:00
f8805aa60b Merge pull request 'rollback city' (#25) from feature/search into master
Reviewed-on: #25
2024-11-07 18:05:03 +01:00
ef19ef00d5 rollback city 2024-11-07 17:54:12 +01:00
9574ce5aeb Merge pull request 'feature/search' (#24) from feature/search into master
Reviewed-on: #24
2024-11-04 23:06:10 +01:00
115a5acb6f add another object_search 2024-11-04 22:58:51 +01:00
4c2d4646ce add lat and long as parameter 2024-11-04 22:57:53 +01:00
4f0cda831f Merge pull request 'add event set status to 1' (#23) from feature/search into master
Reviewed-on: #23
2024-10-23 23:37:50 +02:00
360ac8771a add event set status to 1 2024-10-23 23:36:09 +02:00
df4a2bead6 Merge pull request 'fix object_search' (#22) from feature/search into master
Reviewed-on: #22
2024-10-23 23:22:59 +02:00
a1e135a03e fix object_search 2024-10-23 23:22:05 +02:00
8397d84ec5 Merge pull request 'fix endpoint search' (#21) from feature/search into master
Reviewed-on: #21
2024-10-23 23:08:46 +02:00
9108e15555 fix endpoint search 2024-10-23 23:07:28 +02:00
fcb11dd0d8 Merge pull request 'list events only enabled' (#20) from feature/search into master
Reviewed-on: #20
2024-10-23 23:02:24 +02:00
3f4173863c list events only enabled 2024-10-23 22:58:17 +02:00
6781789012 Merge pull request 'add search' (#19) from feature/search into master
Reviewed-on: #19
2024-10-23 22:04:08 +02:00
436e6d68dd add search 2024-10-23 21:44:33 +02:00
2bbb3a0a95 Merge pull request 'add search' (#18) from feature/category into master
Reviewed-on: #18
2024-10-21 20:18:01 +02:00
d91e561561 add search 2024-10-20 20:56:24 +02:00
84d8f7fd12 Merge pull request 'add tags for event models' (#17) from feature/category into master
Reviewed-on: #17
2024-10-14 18:05:16 +02:00
0df6291428 add tags for event models 2024-10-14 18:03:51 +02:00
8209dd5be7 Merge pull request 'add img path' (#16) from feature/add-imgUrl into master
Reviewed-on: #16
2024-09-17 22:36:15 +02:00
8362602bde add img path 2024-09-15 23:54:47 +02:00
5733677bee Merge pull request 'add description' (#15) from feature/add-description into master
Reviewed-on: #15
2024-09-03 15:45:14 +02:00
0a33551fa7 add description 2024-09-03 15:41:37 +02:00
a39e4596c6 Merge pull request 'fix put error' (#14) from feature/more-detail-place into master
Reviewed-on: #14
2024-09-03 11:25:09 +02:00
f63de8e306 fix put error 2024-09-03 11:20:57 +02:00
af7df69731 Merge pull request 'feature/more-detail-place' (#13) from feature/more-detail-place into master
Reviewed-on: #13
2024-09-02 23:55:30 +02:00
080abdd9fe remove useless file 2024-09-02 23:54:35 +02:00
fbe187b5ff fix error 2024-09-02 23:54:19 +02:00
3477ed90a5 Merge pull request 'fix build error' (#12) from feature/more-detail-place into master
Reviewed-on: #12
2024-09-02 23:08:48 +02:00
0a7b862222 fix build error 2024-09-02 22:56:08 +02:00
2e3404cfcf Merge pull request 'add backoffice' (#11) from hotfix/add-origins into master
Reviewed-on: #11
2024-05-29 23:37:56 +02:00
3f87083451 add backoffice 2024-05-29 23:36:11 +02:00
a4e263f4cd Merge pull request 'add origins' (#10) from hotfix/add-origins into master
Reviewed-on: #10
2024-04-29 23:00:14 +02:00
8777a690e0 add origins 2024-04-29 22:58:36 +02:00
195406d1bb Merge pull request 'replace backend address' (#9) from hotfix/address into master
Reviewed-on: #9
2023-12-17 13:20:30 +01:00
fc2c6fdbd8 replace backend address 2023-12-17 13:20:29 +01:00
5f9c3c32c6 Merge pull request 'add backoffice origins' (#8) from events into master
Reviewed-on: #8
2023-12-17 12:33:34 +01:00
8f31746c68 add backoffice origins 2023-12-17 12:28:41 +01:00
ff3cb878c4 Merge pull request 'events' (#7) from events into master
Reviewed-on: #7
2023-11-20 23:43:15 +01:00
fb302ca63e add latitude and longitude 2023-11-20 22:40:42 +01:00
6edc87fb1d remove file 2023-11-19 21:29:42 +01:00
afd2dec01d test toto 2023-11-19 21:17:41 +01:00
d621977955 add organizers 2023-11-16 21:40:49 +01:00
e7e4326a31 remove participants and interested 2023-11-14 00:21:15 +01:00
ecc19cf09a fix events delete 2023-11-12 21:18:49 +01:00
e7d13bda96 fix events 2023-11-12 18:42:49 +01:00
322c00e18d remove list 2023-11-09 21:03:13 +01:00
c7b209030e put and patch events 2023-11-08 23:10:45 +01:00
a85582d200 change delete method events 2023-11-08 22:43:53 +01:00
6cc43005f0 add name 2023-11-07 21:08:58 +01:00
51634184af add router events 2023-11-06 23:29:12 +01:00
ff72cf3c05 add routers 2023-11-06 23:27:47 +01:00
75a6802c15 add event 2023-11-06 23:15:52 +01:00
d206a9f8a3 update put method 2023-11-04 23:36:19 +01:00
dd04cedf0e change response return 2023-11-04 20:45:07 +01:00
82750bbd0c Merge pull request 'integ-backoffice' (#6) from integ-backoffice into master
Reviewed-on: #6
2023-11-03 20:42:24 +00:00
632f61a376 add route users 2023-11-03 21:36:08 +01:00
4f6a057eca add or 2023-11-03 21:00:47 +01:00
ce0ba3e55b status code 2023-11-03 15:22:59 +01:00
753f4ee087 add enable and remove groups 2023-11-02 23:54:40 +01:00
a279439e72 move method 2023-11-02 17:30:15 +01:00
2546763b03 add method patch enable 2023-10-31 18:02:24 +01:00
1286f0a3f2 remove current user for groups 2023-10-31 17:49:57 +01:00
475c8e0ffc disable message 2023-10-31 17:47:44 +01:00
6cc12e0535 add method group method 2023-10-31 17:44:24 +01:00
af4af31e26 search user done 2023-10-30 23:37:16 +01:00
4059411828 add routers usersé 2023-10-27 23:50:35 +02:00
4e2cf12c3b add enable user 2023-10-26 00:08:14 +02:00
94bdf0e27a update user 2023-10-25 23:42:34 +02:00
c120e60911 add datetime 2023-10-23 23:45:13 +02:00
e12c1c91b3 change status 2023-10-22 18:58:35 +02:00
76a4680622 add count get 2023-10-21 10:10:02 +02:00
68c578c449 check roles user 2023-10-20 23:38:12 +02:00
6e7970d5e3 add method delete 2023-10-20 22:43:59 +02:00
0d00df8194 auth with cookie 2023-10-20 22:26:48 +02:00
9 changed files with 748 additions and 65 deletions

View File

@@ -0,0 +1,37 @@
from fastapi.security import OAuth2
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
from fastapi import Request
from fastapi.security.utils import get_authorization_scheme_param
from fastapi import HTTPException
from fastapi import status
from typing import Optional
from typing import Dict
class OAuth2PasswordBearerWithCookie(OAuth2):
def __init__(
self,
tokenUrl: str,
scheme_name: Optional[str] = None,
scopes: Optional[Dict[str, str]] = None,
auto_error: bool = True,
):
if not scopes:
scopes = {}
flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes})
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
async def __call__(self, request: Request) -> Optional[str]:
authorization: str = request.cookies.get("access_token") #changed to accept access token from httpOnly Cookie
scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer":
if self.auto_error:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
else:
return None
return param

View File

@@ -4,18 +4,17 @@ from passlib.context import CryptContext
from pydantic import EmailStr from pydantic import EmailStr
def add(username="", password="", roles="User", disabled=False, confirmed=True, email="test@toto.com"): def add(username="", password="", roles="User", status=1, email="test@toto.com"):
user_repository = users.UserRepository(database=database.database) user_repository = users.UserRepository(database=database.database)
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
result = user_repository.find_one_by({'username': username}) result = user_repository.find_one_by({'username': username})
change = "added" change = "added"
user = users.User(username=username, password=pwd_context.hash(password), roles=roles, disabled=disabled, confirmed=confirmed, email=email) user = users.User(username=username, password=pwd_context.hash(password), roles=roles, status=status, email=email)
if result is not None: if result is not None:
result.password=pwd_context.hash(password) result.password=pwd_context.hash(password)
result.roles=roles result.roles=roles
result.disabled=disabled result.status=status
result.confirmed=confirmed
result.email=email result.email=email
user = result user = result
change = "updated" change = "updated"

View File

@@ -8,14 +8,14 @@ from jose import JWTError, jwt
from passlib.context import CryptContext from passlib.context import CryptContext
from ..models import users, token from ..models import users, token
from ..dependencies import database from ..dependencies import database, cookie
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256" ALGORITHM = "HS256"
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = cookie.OAuth2PasswordBearerWithCookie(tokenUrl="token")
def verify_password(plain_password, hashed_password): def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password) return pwd_context.verify(plain_password, hashed_password)
@@ -35,6 +35,9 @@ def authenticate_user(username: str, password: str):
return False return False
if not verify_password(password, user.password): if not verify_password(password, user.password):
return False return False
user.connected_at = datetime.today()
user_repository = users.UserRepository(database=database.database)
user_repository.save(user)
return user return user
def create_access_token(data: dict, expires_delta: timedelta | None = None): def create_access_token(data: dict, expires_delta: timedelta | None = None):
@@ -70,6 +73,6 @@ async def get_current_user(token_str: Annotated[str, Depends(oauth2_scheme)]):
async def get_current_active_user( async def get_current_active_user(
current_user: Annotated[users.User, Depends(get_current_user)] current_user: Annotated[users.User, Depends(get_current_user)]
): ):
if current_user.disabled: if current_user.status == 0:
raise HTTPException(status_code=400, detail="Inactive user") raise HTTPException(status_code=400, detail="Inactive user")
return current_user return current_user

View File

@@ -1,7 +1,7 @@
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from .routers import users, token, mail from .routers import users, token, mail, events
from .dependencies import user_add from .dependencies import user_add
import os import os
@@ -10,6 +10,10 @@ app = FastAPI()
origins = [ origins = [
"http://localhost:8084", "http://localhost:8084",
"https://backend.valczeryba.ovh",
"https://facebook.com",
"https://fetlife.com",
"https://backoffice.valczeryba.ovh"
] ]
app.add_middleware( app.add_middleware(
@@ -24,6 +28,7 @@ app.add_middleware(
app.include_router(users.router) app.include_router(users.router)
app.include_router(token.router) app.include_router(token.router)
app.include_router(mail.router) app.include_router(mail.router)
app.include_router(events.router)
@app.on_event("startup") @app.on_event("startup")

53
app/models/events.py Normal file
View File

@@ -0,0 +1,53 @@
from pydantic import BaseModel, EmailStr
from pydantic_mongo import AbstractRepository, ObjectIdField
from datetime import datetime, date
class Event(BaseModel):
id: ObjectIdField = None
name: str
place: str
description: str
imgUrl: str | None = None
status: int = 0
latitude: float = 0.0
longitude: float = 0.0
organizers: list[str] = []
tags: list[str] = []
start_date: datetime | None = None
end_date: datetime | None = None
created_at: datetime = datetime.today()
updated_at: datetime | None = None
deleted_at: datetime | None = None
disabled_at: datetime | None = None
class EventOut(BaseModel):
id: ObjectIdField = None
name: str
place: str
description: str
imgUrl: str | None = None
status: int = 0
start_date: datetime | None = None
end_date: datetime | None = None
tags: list[str] = []
class EventIn(BaseModel):
name: str
place: str
description: str
imgUrl: str | None = None
status: int = 0
organizers: list[str] = []
tags: list[str] = []
start_date: datetime | None = None
end_date: datetime | None = None
latitude: float = 0.0
longitude: float = 0.0
class EventIDS(BaseModel):
ids: list[str]
class EventRepository(AbstractRepository[Event]):
class Meta:
collection_name = "events"

View File

@@ -1,30 +1,40 @@
from pydantic import BaseModel, EmailStr from pydantic import BaseModel, EmailStr
from pydantic_mongo import AbstractRepository, ObjectIdField from pydantic_mongo import AbstractRepository, ObjectIdField
from datetime import datetime, date
class User(BaseModel): class User(BaseModel):
id: ObjectIdField = None id: ObjectIdField = None
username: str username: str
password: str password: str
firstName: str = ""
name: str = ""
roles: str = "User" roles: str = "User"
disabled: bool = False status: int = 0
removed: bool = False
confirmed: bool = False
email: EmailStr email: EmailStr
birth: str | None = None
created_at: datetime = datetime.today()
connected_at: datetime | None = None
updated_at: datetime | None = None
deleted_at: datetime | None = None
disabled_at: datetime | None = None
class UserOut(BaseModel): class UserOut(BaseModel):
id: ObjectIdField = None id: ObjectIdField = None
username: str username: str
roles: str roles: str
disabled: bool firstName: str
removed: bool name: str
confirmed: bool status: int = 0
email: EmailStr email: EmailStr
class UserIn(BaseModel): class UserIn(BaseModel):
username: str username: str
name: str
firstName: str
roles: str roles: str
password: str password: str
birth: str
email: EmailStr email: EmailStr
@@ -37,6 +47,9 @@ class UserCreate(BaseModel):
class UserInDB(User): class UserInDB(User):
password: str password: str
class UserIDS(BaseModel):
ids: list[str]
class UserRepository(AbstractRepository[User]): class UserRepository(AbstractRepository[User]):
class Meta: class Meta:
collection_name = "users" collection_name = "users"

View File

@@ -0,0 +1,422 @@
from fastapi import APIRouter, Depends, HTTPException, status, Response
from fastapi.responses import JSONResponse
from datetime import datetime
from ..dependencies import users_token, permissions_checker, database
from ..models import events, users
from pydantic import EmailStr
from typing import Annotated, Union
from bson import ObjectId
from datetime import datetime
router = APIRouter()
def build_location_filter(min_lat, max_lat, min_lon, max_lon):
"""Build location-based query filters."""
if min_lat is not None and max_lat is not None and min_lon is not None and max_lon is not None:
return [
{"latitude": {"$gte": min_lat}},
{"latitude": {"$lte": max_lat}},
{"longitude": {"$gte": min_lon}},
{"longitude": {"$lte": max_lon}},
]
return []
def build_datetime_filter(current_datetime):
"""Build filters for current datetime."""
if current_datetime:
return {
"$or": [
{"start_date": {"$gte": current_datetime}}, # Upcoming events
{
"$and": [
{"start_date": {"$lte": current_datetime}}, # Already started
{"$or": [
{"end_date": {"$gte": current_datetime}}, # Ongoing
{"end_date": None}, # No end date
]},
],
},
],
}
return None
def build_date_filter(start_date, end_date):
"""Build date range filters."""
if start_date and end_date:
return [
{"start_date": {"$gte": datetime.combine(start_date, datetime.min.time())}},
{"start_date": {"$lte": datetime.combine(end_date, datetime.max.time())}},
]
return []
def build_text_filter(item):
"""Build text-based search filters."""
if item:
return {
"$or": [
{"name": {"$regex": item, "$options": "i"}},
{"tags": {"$regex": item, "$options": "i"}},
{"organizers": {"$regex": item, "$options": "i"}},
]
}
return None
@router.get("/events", tags=["events"], response_model=list[events.EventOut])
async def read_events(
authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))],
skip: int = 0,
limit: int = 20,
id_event: str | None = None,
name: str | None = None,
status: int = 1,
tags: str | None = None,
organizers: str | None = None,
current_datetime: datetime | None = None,
date_event: datetime | None = None,
start_date: datetime | None = None,
end_date: datetime | None = None,
):
# Validate `skip` and `limit`
if limit < 1 or skip < 0 or limit < skip:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="`skip` should be >= 0 and `limit` should be > 0 and greater than `skip`.",
)
limit = limit + skip
# Initialize filters
filters = []
# Add status filter
filters.append({"status": {"$eq": status}})
# Add date filters
if date_event:
start_of_day = datetime.combine(date_event, datetime.min.time())
end_of_day = datetime.combine(date_event, datetime.max.time())
filters.extend(build_date_filter(start_of_day, end_of_day))
elif start_date and end_date:
filters.extend(build_date_filter(start_date, end_date))
# Add current datetime filter
datetime_filter = build_datetime_filter(current_datetime)
if datetime_filter:
filters.append(datetime_filter)
# Add text-based filters
if name:
filters.append(build_text_filter(name))
if tags:
filters.append({"tags": {"$eq": tags}})
if organizers:
filters.append({"organizers": {"$eq": organizers}})
# Add ID filter
if id_event:
try:
event_id = ObjectId(id_event)
filters.append({"_id": {"$eq": event_id}})
except Exception:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid event ID format.")
# Combine all filters
object_search = {"$and": filters} if filters else {}
# 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):
event = events.EventOut(
id=event_index.id,
tags=event_index.tags,
imgUrl=event_index.imgUrl,
name=event_index.name,
description=event_index.description,
place=event_index.place,
status=event_index.status,
start_date=event_index.start_date,
end_date=event_index.end_date,
)
list_events.append(event)
return list_events
@router.get("/events/search", tags=["events"], response_model=list[events.EventOut])
async def search_events(
authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))],
skip: int = 0,
limit: int = 20,
item: Union[str, None] = None,
status: int = 1,
min_lat: Union[float, None] = None,
max_lat: Union[float, None] = None,
min_lon: Union[float, None] = None,
max_lon: Union[float, None] = None,
current_datetime: Union[datetime, None] = None,
date_event: Union[datetime, None] = None,
start_date: Union[datetime, None] = None,
end_date: Union[datetime, None] = None,
tags: Union[list[str], None] = None,
):
if limit < 1 or skip < 0 or limit < skip:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="`skip` should be >= 0 and `limit` should be > 0 and greater than `skip`.",
)
limit = limit + skip
# Initialize filters
filters = [{"status": {"$eq": status}}]
# Date filters
if date_event:
start_of_day = datetime.combine(date_event, datetime.min.time())
end_of_day = datetime.combine(date_event, datetime.max.time())
filters.extend(build_date_filter(start_of_day, end_of_day))
else:
filters.extend(build_date_filter(start_date, end_date))
# Add location filter
filters.extend(build_location_filter(min_lat, max_lat, min_lon, max_lon))
# Add datetime filter
datetime_filter = build_datetime_filter(current_datetime)
if datetime_filter:
filters.append(datetime_filter)
# Add text filter
text_filter = build_text_filter(item)
if text_filter:
filters.append(text_filter)
if tags is not None:
filters.append({"tags": {"$in": tags}})
# Combine filters
object_search = {"$and": filters} if filters else {}
# 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):
event = events.EventOut(
id=event_index.id,
tags=event_index.tags,
imgUrl=event_index.imgUrl,
name=event_index.name,
description=event_index.description,
place=event_index.place,
status=event_index.status,
start_date=event_index.start_date,
end_date=event_index.end_date,
)
list_events.append(event)
return list_events
@router.get("/events/me",tags=["events"])
async def read_users_me(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]):
event_repository = events.EventRepository(database=database.database)
listOrganizers = []
for event_index in event_repository.find_by({"organizers":{"$eq": current_user.username}}, limit=limit, skip=skip):
event = events.EventOut(id=event_index.id, name=event_index.name, tags=event_index.tags, imgUrl=event_index.imgUrl, description=event_index.description, place=event_index.place, status=event_index.status, start_date=event_index.start_date, end_date=event_index.end_date)
listOrganizers.append(event)
content = {"organizers":listOrganizers}
response = JSONResponse(content=content)
return response
@router.get("/events/count", tags=["events"])
async def read_events_count(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]):
count = database.database.get_collection("events").estimated_document_count()
content = {"count":count}
response = JSONResponse(content=content)
return response
@router.get("/events/{item_id}", tags=["events"], response_model=events.Event)
async def read_events_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]):
event_repository = events.EventRepository(database=database.database)
event = event_repository.find_one_by_id(ObjectId(item_id))
return event
@router.delete("/events/me/{item_id}", tags=["events"])
async def delete_event_me(item_id: str, current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], remove: bool = False):
event_repository = events.EventRepository(database=database.database)
event = event_repository.find_one_by_id(ObjectId(item_id))
if remove is True:
event.deleted_at = datetime.today()
event.status = -1
content = {"message": "event is deleted"}
else:
event.status = 0
event.deleted_at = datetime.today()
content = {"message": "event is disabled"}
event_repository.save(event)
response = JSONResponse(content=content)
return response
@router.delete("/events/groups",tags=["events"])
async def delete_events_groups(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], remove: bool = False, eventids: events.EventIDS | None = None):
if len(eventids.ids) == 0:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="eventids should be greater than 0"
)
event_repository = event.EventRepository(database=database.database)
for i in eventids.ids:
event = event_repository.find_one_by_id(ObjectId(i))
if remove is True:
event.status = -1
event.deleted_at = datetime.today()
content = {"message": "events are deleted "}
else:
event.status = 0
event.disabled_at = datetime.today()
content = {"message": "events are disabled"}
event_repository.save(event)
response = JSONResponse(content=content)
return response
@router.delete("/events/{item_id}", tags=["events"])
async def delete_events_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], remove : bool = False):
event_repository = events.EventRepository(database=database.database)
event = event_repository.find_one_by_id(ObjectId(item_id))
if remove is True:
event.status = -1
event.deleted_at = datetime.today()
content = {"message": "events are deleted"}
else:
event.status = 0
event.disabled_at = datetime.today()
content = {"message": "events are disabled"}
event_repository.save(event)
response = JSONResponse(content=content)
return response
@router.put("/events/me/{item_id}",tags=["events"])
async def update_events_me(item_id: str, current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], eventSingle: events.EventIn | None = None):
event_repository = event.EventRepository(database=database.database)
event = event_repository.find_one_by_id(ObjectId(item_id))
event.name = eventSingle.name
event.description = eventSingle.description
event.place = eventSingle.place
event.start_date = eventSingle.start_date
event.tags = eventSingle.tags
event.end_date = eventSingle.end_date
event.latitude = eventSingle.latitude
event.longitude = eventSingle.longitude
event.updated_at = datetime.today()
event.imgUrl = eventSingle.imgUrl
event_repository.save(event)
content = {"message": "event is updated"}
response = JSONResponse(content=content)
return response
@router.put("/events", tags=["events"], status_code=status.HTTP_201_CREATED)
async def update_events(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], eventSingle: events.EventIn | None = None):
if eventSingle is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Body request is empty"
)
event_repository = events.EventRepository(database=database.database)
event = event_repository.find_one_by({"name": {'$eq': eventSingle.name}})
if event is not None:
raise HTTPException(
status_code=status.HTTP_204_NO_CONTENT,
detail="name"
)
event = events.Event(name=eventSingle.name, description=eventSingle.description, place=eventSingle.place)
event.start_date = eventSingle.start_date
event.end_date = eventSingle.end_date
event.organizers = eventSingle.organizers
event.latitude = eventSingle.latitude
event.longitude = eventSingle.longitude
event.imgUrl = eventSingle.imgUrl
event.tags = eventSingle.tags
event.status = 1
event.created_at = datetime.today()
event_repository.save(event)
content = {"message": "event is created"}
response = JSONResponse(content=content, status_code=status.HTTP_201_CREATED)
return response
@router.put("/events/{item_id}", tags=["events"], status_code=status.HTTP_200_OK)
async def update_events_id(item_id: str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], eventSingle: events.EventIn | None = None, response: Response = Response):
if eventSingle is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Body request is empty"
)
event_repository = events.EventRepository(database=database.database)
event = event_repository.find_one_by({"id": {'$eq': ObjectId(item_id)}})
if event is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Event not found"
)
event.name = eventSingle.name
event.place = eventSingle.place
event.description = eventSingle.description
event.start_date = eventSingle.start_date
event.end_date = eventSingle.end_date
event.organizers = eventSingle.organizers
event.tags = eventSingle.tags
event.latitude = eventSingle.latitude
event.longitude = eventSingle.longitude
event.updated_at = datetime.today()
event.imgUrl = eventSingle.imgUrl
event_repository.save(event)
content = {"message": "event is updated"}
response = JSONResponse(content=content)
return response
@router.patch("/events/groups",tags=["events"])
async def patch_events_groups(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], eventids: events.EventIDS | None = None):
if len(eventids.ids) == 0:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="eventids should be greater than 0"
)
event_repository = events.EventRepository(database=database.database)
content = {"message": "events are enabled"}
for i in eventids.ids:
event = event_repository.find_one_by_id(ObjectId(i))
event.status = 1
event.disabled_at = None
event.deleted_at = None
event_repository.save(event)
response = JSONResponse(content=content)
return response
@router.patch("/events/{item_id}", tags=["events"])
async def patch_events_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]):
event_repository = events.EventRepository(database=database.database)
event = event_repository.find_one_by_id(ObjectId(item_id))
event.status = 1
event.disabled_at = None
event.deleted_at = None
event_repository.save(event)
content = {"message": "event is enabled"}
response = JSONResponse(content=content)
return response

View File

@@ -7,13 +7,13 @@ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from ..dependencies import users_token, permissions_checker from ..dependencies import users_token, permissions_checker
from ..models import token, users from ..models import token, users
router = APIRouter() router = APIRouter()
ACCESS_TOKEN_EXPIRE_MINUTES = 30 ACCESS_TOKEN_EXPIRE_MINUTES = 30
@router.post("/token", tags=["token"]) @router.post("/token", tags=["token"])
async def login_for_access_token( async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()] form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
):
user = users_token.authenticate_user(form_data.username, form_data.password) user = users_token.authenticate_user(form_data.username, form_data.password)
if not user: if not user:
raise HTTPException( raise HTTPException(
@@ -25,9 +25,9 @@ async def login_for_access_token(
access_token = users_token.create_access_token( access_token = users_token.create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires data={"sub": user.username}, expires_delta=access_token_expires
) )
content = {"message": "Access token generated"} content = {"roles":user.roles,"message": "Access token generated"}
response = JSONResponse(content=content) response = JSONResponse(content=content)
response.set_cookie(key="jwt", value=access_token) response.set_cookie(key="access_token", value="Bearer {0}".format(access_token), httponly=True)
return response return response
@router.get("/token",tags=["token"]) @router.get("/token",tags=["token"])
@@ -35,3 +35,10 @@ async def check_token(current_user: Annotated[users.User, Depends(users_token.ge
content = {"message": "Check token"} content = {"message": "Check token"}
response = JSONResponse(content=content) response = JSONResponse(content=content)
return response return response
@router.delete("/token",tags=["token"])
async def check_token(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]):
content = {"message": "Token deleted"}
response = JSONResponse(content=content)
response.set_cookie(key="access_token", value="", httponly=True)
return response

View File

@@ -1,6 +1,9 @@
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status, Response
from fastapi.responses import JSONResponse
from datetime import datetime
from ..dependencies import users_token, permissions_checker, database from ..dependencies import users_token, permissions_checker, database
from ..models import users from ..models import users
from pydantic import EmailStr
from typing import Annotated from typing import Annotated
from bson import ObjectId from bson import ObjectId
router = APIRouter() router = APIRouter()
@@ -8,7 +11,7 @@ router = APIRouter()
@router.get("/users", tags=["users"], response_model=list[users.UserOut]) @router.get("/users", tags=["users"], response_model=list[users.UserOut])
async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], skip: int = 0, limit: int = 20): async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], skip: int = 0, limit: int = 20, id_user: str | None = None, roles: str | None = None, status: int | None = None, email: EmailStr | None = None, name: str | None = None):
if limit < 1 or skip < 0 or limit < skip: if limit < 1 or skip < 0 or limit < skip:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
@@ -17,36 +20,62 @@ async def read_users(authorize: Annotated[bool, Depends(permissions_checker.Perm
limit = limit + skip limit = limit + skip
listUsers = [] listUsers = []
user_repository = users.UserRepository(database=database.database) user_repository = users.UserRepository(database=database.database)
for user_index in user_repository.find_by({}, limit=limit, skip=skip): object_search = {}
user = users.UserOut(id=user_index.id, username=user_index.username, email=user_index.email, disabled=user_index.disabled, roles=user_index.roles, removed=user_index.removed, confirmed=user_index.confirmed) if status is not None and roles is not None:
object_search = {"$and":[{"roles":{"$eq": roles}}, {"status":{"$eq":status}}]}
else:
if status is not None:
object_search = {"status":{"$eq": status}}
if roles is not None:
object_search = {"roles":{"$eq":roles}}
if id_user is not None:
userid = ObjectId(id_user)
object_search = {"id": {"$regex": userid}}
if status is not None and roles is not None:
object_search = {"$and":[{"id":{"$regex": userid}}, {"roles":{"$eq": roles}}, {"status":{"$eq":status}}]}
else:
if status is not None:
object_search = {"$and":[{"id":{"$regex": userid}}, {"status":{"$eq":status}}]}
if roles is not None:
object_search = {"$and":[{"id":{"$regex": userid}}, {"roles":{"$eq":roles}}]}
if email is not None:
object_search = {"email": {"$eq": email}}
if status is not None and roles is not None:
object_search = {"$and":[{"email":{"$eq": email}}, {"roles":{"$eq": roles}}, {"status":{"$eq":status}}]}
else:
if status is not None:
object_search = {"$and":[{"email":{"$eq": email}}, {"status":{"$eq":status}}]}
if roles is not None:
object_search = {"$and":[{"email":{"$eq": email}}, {"roles":{"$eq":roles}}]}
if name is not None:
object_search = {"username": {"$regex": name}}
if status is not None and roles is not None:
object_search = {"$and":[{"username":{"$regex": name}}, {"roles":{"$eq": roles}}, {"status":{"$eq":status}}]}
else:
if status is not None:
object_search = {"$and":[{"username":{"$regex": name}}, {"status":{"$eq":status}}]}
if roles is not None:
object_search = {"$and":[{"username":{"$regex": name}}, {"roles":{"$eq":roles}}]}
for user_index in user_repository.find_by(object_search, limit=limit, skip=skip):
user = users.UserOut(id=user_index.id, username=user_index.username, email=user_index.email, status=user_index.status, roles=user_index.roles, firstName=user_index.firstName, name=user_index.name)
listUsers.append(user) listUsers.append(user)
return listUsers return listUsers
@router.get("/users/search", tags=["users"], response_model=list[users.UserOut]) @router.get("/users/me",tags=["users"], response_model=users.User, response_model_exclude=["id", "password", "roles", "status"])
async def read_users_id(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], skip: int = 0, limit: int = 20, key: str | None = None, value: str | None= None):
if limit < 1 or skip < 0 or limit < skip:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="skip should be greater than 0 and limit should be greater than 1. Limit should be greater than skip"
)
if key is None or value is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Key or/and value parameter is empty"
)
limit = limit + skip
listUsers = []
user_repository = users.UserRepository(database=database.database)
for user_index in user_repository.find_by({key: {'$regex': value}}, limit=limit, skip=skip):
user = users.UserOut(id=user_index.id, username=user_index.username, disabled=user_index.disabled, roles=user_index.roles, email=user_index.email, removed=user_index.removed, confirmed=user_index.confirmed)
listUsers.append(user)
return listUsers
@router.get("/users/me",tags=["users"], response_model=users.User, response_model_exclude=["id", "password", "roles", "disabled"])
async def read_users_me(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]): async def read_users_me(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]):
return current_user return current_user
@router.get("/users/count", tags=["users"])
async def read_users_count(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]):
count = database.database.get_collection("users").estimated_document_count()
content = {"count":count}
response = JSONResponse(content=content)
return response
@router.get("/users/{item_id}", tags=["users"], response_model=users.User) @router.get("/users/{item_id}", tags=["users"], response_model=users.User)
async def read_users_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): async def read_users_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]):
user_repository = users.UserRepository(database=database.database) user_repository = users.UserRepository(database=database.database)
@@ -54,38 +83,76 @@ async def read_users_id(item_id : str, authorize: Annotated[bool, Depends(permis
return user return user
@router.delete("/users/me",tags=["users"])
@router.delete("/users/me",tags=["users"], response_model=users.User, response_model_exclude=["id", "password", "roles", "disabled"]) async def delete_users_me(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], remove: bool = False):
async def read_users_me(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], remove: bool = False):
user_repository = users.UserRepository(database=database.database) user_repository = users.UserRepository(database=database.database)
current_user.disabled = True
if remove is True:
current_user.removed = True
user_repository.save(current_user)
return current_user
@router.delete("/users/{item_id}", tags=["users"], response_model=users.User) if remove is True:
async def read_users_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], remove : bool = False): current_user.deleted_at = datetime.today()
current_user.status = -1
content = {"message": "users are deleted"}
else:
current_user.status = 0
current_user.deleted_at = datetime.today()
content = {"message": "users are disabled"}
user_repository.save(current_user)
response = JSONResponse(content=content)
return response
@router.delete("/users/groups",tags=["users"])
async def delete_users_groups(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], remove: bool = False, userids: users.UserIDS | None = None):
if len(userids.ids) == 0:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="userids should be greater than 0"
)
user_repository = users.UserRepository(database=database.database)
for i in userids.ids:
user = user_repository.find_one_by_id(ObjectId(i))
if remove is True:
user.status = -1
user.deleted_at = datetime.today()
content = {"message": "users are deleted "}
else:
user.status = 0
user.disabled_at = datetime.today()
content = {"message": "users are disabled"}
user_repository.save(user)
response = JSONResponse(content=content)
return response
@router.delete("/users/{item_id}", tags=["users"])
async def delete_users_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], remove : bool = False):
user_repository = users.UserRepository(database=database.database) user_repository = users.UserRepository(database=database.database)
user = user_repository.find_one_by_id(ObjectId(item_id)) user = user_repository.find_one_by_id(ObjectId(item_id))
user.disabled = True
if remove is True: if remove is True:
user.removed = True user.status = -1
user.deleted_at = datetime.today()
content = {"message": "users are deleted"}
else:
user.status = 0
user.disabled_at = datetime.today()
content = {"message": "users are disabled"}
user_repository.save(user) user_repository.save(user)
return user response = JSONResponse(content=content)
return response
@router.put("/users/me",tags=["users"], response_model=users.User, response_model_exclude=["id", "password", "roles", "disabled"]) @router.put("/users/me",tags=["users"])
async def read_users_me(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], userSingle: users.UserIn | None = None): async def update_users_me(current_user: Annotated[users.User, Depends(users_token.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], userSingle: users.UserIn | None = None):
user_repository = users.UserRepository(database=database.database) user_repository = users.UserRepository(database=database.database)
current_user.username = userSingle.username current_user.username = userSingle.username
current_user.password = user_token.get_password_hash(userSingle.password) current_user.password = user_token.get_password_hash(userSingle.password)
current_user.roles = userSingle.roles current_user.roles = userSingle.roles
current_user.email = userSingle.email current_user.email = userSingle.email
user_repository.save(current_user) user_repository.save(current_user)
content = {"message": "user is updated"}
response = JSONResponse(content=content)
return current_user return current_user
@router.put("/users", tags=["users"], response_model=users.User, status_code=status.HTTP_200_OK) @router.put("/users", tags=["users"], status_code=status.HTTP_201_CREATED)
async def read_users_id(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], userSingle: users.UserIn | None = None): async def update_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], userSingle: users.UserIn | None = None):
if userSingle is None: if userSingle is None:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
@@ -93,13 +160,90 @@ async def read_users_id(authorize: Annotated[bool, Depends(permissions_checker.P
) )
user_repository = users.UserRepository(database=database.database) user_repository = users.UserRepository(database=database.database)
user = user_repository.find_one_by({"username": {'$eq': userSingle.username}}) user = user_repository.find_one_by({"$or":[{"username": {'$eq': userSingle.username}}, {"email": {"$eq": userSingle.email}}]})
if user is not None:
if user.username == userSingle.username:
raise HTTPException(
status_code=status.HTTP_204_NO_CONTENT,
detail="username"
)
if user.email == userSingle.email:
raise HTTPException(
status_code=status.HTTP_204_NO_CONTENT,
detail="email"
)
user = users.User(username=userSingle.username, password=users_token.get_password_hash(userSingle.password), email=userSingle.email)
user.roles = userSingle.roles
user.firstName = userSingle.firstName
user.name = userSingle.name
user.birth = userSingle.birth
user.created_at = datetime.today()
user_repository.save(user)
content = {"message": "user is created"}
response = JSONResponse(content=content, status_code=status.HTTP_201_CREATED)
return response
@router.put("/users/{item_id}", tags=["users"], status_code=status.HTTP_200_OK)
async def update_users_id(item_id: str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], userSingle: users.UserIn | None = None, response: Response = Response):
if userSingle is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Body request is empty"
)
user_repository = users.UserRepository(database=database.database)
user = user_repository.find_one_by({"id": {'$eq': ObjectId(item_id)}})
if user is None: if user is None:
response.status_code = status.HTTP_201_CREATED raise HTTPException(
user = users.User() status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
user.username = userSingle.username user.username = userSingle.username
user.password = user_token.get_password_hash(userSingle.password) user.password = users_token.get_password_hash(userSingle.password)
user.roles = userSingle.roles user.roles = userSingle.roles
user.email = userSingle.email user.email = userSingle.email
user.firstName = userSingle.firstName
user.name = userSingle.name
user.birth = userSingle.birth
user.updated_at = datetime.today()
user_repository.save(user) user_repository.save(user)
return user content = {"message": "user is updated"}
response = JSONResponse(content=content)
return response
@router.patch("/users/groups",tags=["users"])
async def patch_users_groups(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], userids: users.UserIDS | None = None):
if len(userids.ids) == 0:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="userids should be greater than 0"
)
user_repository = users.UserRepository(database=database.database)
content = {"message": "users are enabled"}
for i in userids.ids:
user = user_repository.find_one_by_id(ObjectId(i))
user.status = 1
user.disabled_at = None
user.deleted_at = None
user_repository.save(user)
response = JSONResponse(content=content)
return response
@router.patch("/users/{item_id}", tags=["users"], response_model=users.User)
async def patch_users_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]):
user_repository = users.UserRepository(database=database.database)
user = user_repository.find_one_by_id(ObjectId(item_id))
user.status = 1
user.disabled_at = None
user.deleted_at = None
user_repository.save(user)
content = {"message": "user is enabled"}
response = JSONResponse(content=content)
return response