corrections and improvements
parent
0e041336a4
commit
bac9b4ac82
|
|
@ -3,7 +3,7 @@ from email.mime.multipart import MIMEMultipart
|
|||
from email.mime.text import MIMEText
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Body
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from sqlalchemy import update, select
|
||||
from services.auth_service import AuthService
|
||||
from models.schemas import Token, UserCreate, UserResponse
|
||||
|
|
@ -17,6 +17,7 @@ from utils.logging import logger
|
|||
from utils.security import verify_password, get_password_hash, pwd_context
|
||||
|
||||
router = APIRouter()
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
@router.post("/signup", response_model=UserResponse, status_code=201, summary="User Signup")
|
||||
async def signup(user: UserCreate, db=Depends(get_db)):
|
||||
|
|
@ -42,8 +43,8 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
|
|||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
@router.get("/me", response_model=UserResponse, summary="Get current user")
|
||||
async def read_users_me(current_user: UserResponse = Depends(AuthService.get_current_user)):
|
||||
return current_user
|
||||
async def read_users_me(token:str = Depends(oauth2_scheme) , db=Depends(get_db)):
|
||||
return await AuthService.get_current_user(token, db)
|
||||
|
||||
@router.post("/reset-password")
|
||||
async def reset_password(token: str = Body(...), new_password: str = Body(...), db: AsyncSession = Depends(get_db)):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from services.person_report_service import PersonReportService
|
||||
from models.schemas import PersonReportCreate, PersonReportUpdate, PersonReportResponse, UserResponse
|
||||
from config.database import get_db
|
||||
|
|
@ -6,10 +7,11 @@ from typing import Optional
|
|||
from services.auth_service import AuthService
|
||||
|
||||
router = APIRouter()
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
@router.post("/", response_model=PersonReportResponse, status_code=201)
|
||||
async def create_report(report: PersonReportCreate, db=Depends(get_db)):
|
||||
return await PersonReportService.create_report(report, db)
|
||||
async def create_report(report: PersonReportCreate, image_file: UploadFile = File(None), db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await PersonReportService.create_report(report, db, image_file, token)
|
||||
|
||||
@router.put("/{report_id}", response_model=PersonReportResponse)
|
||||
async def update_report(report_id: int, report: PersonReportUpdate, db=Depends(get_db)):
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from services.points_of_interest_service import PointsOfInterestService
|
||||
from models.schemas import PointOfInterest
|
||||
from models.schemas import CreatePointOfInterest, UpdatePointOfInterest
|
||||
from config.database import get_db
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
@router.post("/", status_code=201)
|
||||
async def create_point(point: PointOfInterest, db=Depends(get_db)):
|
||||
return await PointsOfInterestService.create_point_of_interest(point, db)
|
||||
async def create_point(point: CreatePointOfInterest, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await PointsOfInterestService.create_point_of_interest(point, db, token)
|
||||
|
||||
|
||||
@router.get("/{point_id}")
|
||||
|
|
@ -22,10 +23,10 @@ async def get_all_points(db=Depends(get_db)):
|
|||
|
||||
|
||||
@router.put("/{point_id}")
|
||||
async def update_point(point_id: int, data: dict, db=Depends(get_db)):
|
||||
return await PointsOfInterestService.update_point_of_interest(point_id, data, db)
|
||||
async def update_point(point_id: int, point: UpdatePointOfInterest, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await PointsOfInterestService.update_point_of_interest(point_id, point, db, token)
|
||||
|
||||
|
||||
@router.delete("/{point_id}")
|
||||
async def delete_point(point_id: int, db=Depends(get_db)):
|
||||
return await PointsOfInterestService.delete_point_of_interest(point_id, db)
|
||||
async def delete_point(point_id: int, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await PointsOfInterestService.delete_point_of_interest(point_id, db, token)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from services.shelter_service import ShelterService
|
||||
from models.schemas import Shelter
|
||||
from models.schemas import CreateShelter, UpdateShelter
|
||||
from config.database import get_db
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
@router.post("/", status_code=201)
|
||||
async def create_shelter(shelter: Shelter, db=Depends(get_db)):
|
||||
return await ShelterService.create_shelter(shelter, db)
|
||||
async def create_shelter(shelter: CreateShelter, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await ShelterService.create_shelter(shelter, db, token)
|
||||
|
||||
|
||||
@router.get("/{shelter_id}")
|
||||
|
|
@ -22,10 +23,10 @@ async def get_all_shelters(db=Depends(get_db)):
|
|||
|
||||
|
||||
@router.put("/{shelter_id}")
|
||||
async def update_shelter(shelter_id: int, data: dict, db=Depends(get_db)):
|
||||
return await ShelterService.update_shelter(shelter_id, data, db)
|
||||
async def update_shelter(shelter_id: int, shelter: UpdateShelter, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await ShelterService.update_shelter(shelter_id, shelter, db, token)
|
||||
|
||||
|
||||
@router.delete("/{shelter_id}")
|
||||
async def delete_shelter(shelter_id: int, db=Depends(get_db)):
|
||||
return await ShelterService.delete_shelter(shelter_id, db)
|
||||
async def delete_shelter(shelter_id: int, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await ShelterService.delete_shelter(shelter_id, db, token)
|
||||
|
|
|
|||
|
|
@ -1,26 +1,29 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from services.auth_service import AuthService
|
||||
from services.technical_issue_service import TechnicalIssueService
|
||||
from models.schemas import TechnicalIssue, UpdateTechnicalIssue
|
||||
from models.schemas import CreateTechnicalIssue, UpdateTechnicalIssue
|
||||
from config.database import get_db
|
||||
|
||||
router = APIRouter()
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
@router.post("/", status_code=201)
|
||||
async def create_issue(issue: TechnicalIssue, db=Depends(get_db)):
|
||||
return await TechnicalIssueService.create_issue(issue, db)
|
||||
async def create_issue(issue: CreateTechnicalIssue, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await TechnicalIssueService.create_issue(issue, db, token)
|
||||
|
||||
@router.get("/{issue_id}")
|
||||
async def get_issue(issue_id: int, db=Depends(get_db)):
|
||||
return await TechnicalIssueService.get_issue(issue_id, db)
|
||||
async def get_issue(issue_id: int, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await TechnicalIssueService.get_issue(issue_id, db, token)
|
||||
|
||||
@router.get("/")
|
||||
async def get_all_issues(db=Depends(get_db)):
|
||||
return await TechnicalIssueService.get_all_issues(db)
|
||||
async def get_all_issues(db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await TechnicalIssueService.get_all_issues(db, token)
|
||||
|
||||
@router.put("/{issue_id}")
|
||||
async def update_issue(issue_id: int, issue_data: UpdateTechnicalIssue, db=Depends(get_db)):
|
||||
return await TechnicalIssueService.update_issue(issue_id, issue_data, db)
|
||||
async def update_issue(issue_id: int, issue_data: UpdateTechnicalIssue, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await TechnicalIssueService.update_issue(issue_id, issue_data, db, token)
|
||||
|
||||
@router.delete("/{issue_id}")
|
||||
async def delete_issue(issue_id: int, db=Depends(get_db)):
|
||||
return await TechnicalIssueService.delete_issue(issue_id, db)
|
||||
async def delete_issue(issue_id: int, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await TechnicalIssueService.delete_issue(issue_id, db, token)
|
||||
|
|
|
|||
|
|
@ -1,28 +1,16 @@
|
|||
# app/api/v1/upload.py
|
||||
from fastapi import APIRouter, UploadFile, File, HTTPException
|
||||
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from config.database import get_db
|
||||
from services.auth_service import AuthService
|
||||
from services.upload_service import UploadService
|
||||
import uuid
|
||||
from utils.logging import logger
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Instancier UploadService avec le nom de ton bucket S3
|
||||
upload_service = UploadService(bucket_name="ton-nom-de-bucket")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
@router.post("/")
|
||||
async def upload_file(file: UploadFile = File(...)):
|
||||
# Générer un nom de fichier unique
|
||||
file_extension = file.filename.split(".")[-1] # Récupérer l'extension du fichier
|
||||
unique_filename = f"{uuid.uuid4()}.{file_extension}" # Générer un nom unique
|
||||
|
||||
try:
|
||||
# Lire le contenu du fichier de manière asynchron
|
||||
file_content = await file.read()
|
||||
|
||||
# Uploader le fichier sur S3
|
||||
file_url = upload_service.upload_file(file_content, unique_filename)
|
||||
|
||||
# Retourner l'URL du fichier uploadé
|
||||
return {"file_url": file_url}
|
||||
except Exception as e:
|
||||
# Gérer les erreurs
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
async def upload_file(file: UploadFile, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
user = await AuthService.get_current_user(token, db)
|
||||
logger.info(f"User {user} is uploading a file")
|
||||
return await UploadService.upload_image_to_s3(file, user["id"])
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from services.user_service import UserService
|
||||
from models.schemas import UserUpdateRole, UserBlockBan, UserResponse
|
||||
|
|
@ -7,27 +8,28 @@ from services.auth_service import AuthService
|
|||
from typing import Optional
|
||||
|
||||
router = APIRouter()
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
@router.get("/", response_model=list[UserResponse])
|
||||
async def list_users(status: Optional[str] = None, db=Depends(get_db)):
|
||||
return await UserService.list_users(status, db)
|
||||
async def list_users(status: Optional[str] = None, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await UserService.list_users(token, status, db)
|
||||
|
||||
@router.patch("/role", status_code=200, dependencies=[Depends(AuthService.admin_required)])
|
||||
async def change_role(user_update: UserUpdateRole, db: AsyncSession = Depends(get_db)):
|
||||
return await UserService.change_user_role(user_update, db)
|
||||
@router.patch("/role", status_code=200)
|
||||
async def change_role(user_update: UserUpdateRole, db: AsyncSession = Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await UserService.change_user_role(user_update, db, token)
|
||||
|
||||
@router.post("/block", status_code=200, dependencies=[Depends(AuthService.admin_required)])
|
||||
async def block_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db)):
|
||||
return await UserService.block_user(user_action, db)
|
||||
@router.post("/block", status_code=200)
|
||||
async def block_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await UserService.block_user(user_action, db, token)
|
||||
|
||||
@router.post("/ban", status_code=200, dependencies=[Depends(AuthService.admin_required)])
|
||||
async def ban_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db)):
|
||||
return await UserService.ban_user(user_action, db)
|
||||
@router.post("/ban", status_code=200)
|
||||
async def ban_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await UserService.ban_user(user_action, db, token)
|
||||
|
||||
@router.post("/unblock", status_code=200, dependencies=[Depends(AuthService.admin_required)])
|
||||
async def unblock_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db)):
|
||||
return await UserService.unblock_user(user_action, db)
|
||||
@router.post("/unblock", status_code=200)
|
||||
async def unblock_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await UserService.unblock_user(user_action, db, token)
|
||||
|
||||
@router.post("/unban", status_code=200, dependencies=[Depends(AuthService.admin_required)])
|
||||
async def unban_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db)):
|
||||
return await UserService.unban_user(user_action, db)
|
||||
@router.post("/unban", status_code=200)
|
||||
async def unban_user(user_action: UserBlockBan, db: AsyncSession = Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||
return await UserService.unban_user(user_action, db, token)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from sqlalchemy import Table, Column, Integer, String, DateTime, Boolean, MetaData, ForeignKey, Text
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
metadata = MetaData()
|
||||
|
||||
|
|
@ -100,6 +100,8 @@ shelters_table = Table(
|
|||
Column("description", Text),
|
||||
Column("status", String(50), default="available"),
|
||||
Column("contact_person", String(255)),
|
||||
Column('contact_email', String(255)),
|
||||
Column('contact_phone', String(20)),
|
||||
Column("gps_coordinates", String(100), nullable=False),
|
||||
Column("added_by", Integer, ForeignKey("users.id"), nullable=False)
|
||||
)
|
||||
|
|
@ -115,6 +117,6 @@ person_reports_table = Table(
|
|||
Column("gps_coordinates", String(100)),
|
||||
Column("photo_url", String(255)),
|
||||
Column("reporter_email", String(255), ForeignKey("users.email"), nullable=False),
|
||||
Column("created_at", DateTime, default=datetime.utcnow),
|
||||
Column("updated_at", DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
Column("created_at", DateTime, default=datetime.now(timezone.utc)),
|
||||
Column("updated_at", DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
|
||||
)
|
||||
|
|
@ -122,9 +122,12 @@ class TechnicalIssue(BaseModel):
|
|||
description: str
|
||||
status: str
|
||||
|
||||
class CreateTechnicalIssue(BaseModel):
|
||||
description: str
|
||||
status: str
|
||||
|
||||
class UpdateTechnicalIssue(BaseModel):
|
||||
status: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
class PersonReportBase(BaseModel):
|
||||
full_name: str
|
||||
|
|
@ -135,8 +138,12 @@ class PersonReportBase(BaseModel):
|
|||
photo_url: Optional[str] = None
|
||||
reporter_email: str
|
||||
|
||||
class PersonReportCreate(PersonReportBase):
|
||||
pass
|
||||
class PersonReportCreate(BaseModel):
|
||||
full_name: str
|
||||
date_of_birth: datetime
|
||||
status: str
|
||||
location: Optional[str] = None
|
||||
gps_coordinates: Optional[str] = None
|
||||
|
||||
class PersonReportUpdate(BaseModel):
|
||||
status: Optional[str] = None
|
||||
|
|
@ -159,6 +166,19 @@ class PointOfInterest(BaseModel):
|
|||
gps_coordinates: str
|
||||
added_by: int # ID de l'utilisateur qui a ajouté ce point
|
||||
|
||||
class CreatePointOfInterest(BaseModel):
|
||||
label: str
|
||||
description: Optional[str] = None
|
||||
icon: Optional[str] = None # URL de l'icône
|
||||
organization: Optional[str] = None
|
||||
gps_coordinates: str
|
||||
|
||||
class UpdatePointOfInterest(BaseModel):
|
||||
description: Optional[str] = None
|
||||
icon: Optional[str] = None # URL de l'icône
|
||||
organization: Optional[str] = None
|
||||
gps_coordinates: Optional[str] = None
|
||||
|
||||
class Shelter(BaseModel):
|
||||
label: str
|
||||
description: Optional[str] = None
|
||||
|
|
@ -167,6 +187,24 @@ class Shelter(BaseModel):
|
|||
gps_coordinates: str
|
||||
added_by: int # ID de l'utilisateur qui a ajouté cet abri
|
||||
|
||||
class CreateShelter(BaseModel):
|
||||
label: str
|
||||
description: Optional[str] = None
|
||||
status: str # "available", "full", "closed"
|
||||
contact_person: Optional[str] = None
|
||||
contact_email: Optional[str] = None
|
||||
contact_phone: Optional[str] = None
|
||||
gps_coordinates: str
|
||||
|
||||
class UpdateShelter(BaseModel):
|
||||
label: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
status: Optional[str] = None # "available", "full", "closed"
|
||||
contact_person: Optional[str] = None
|
||||
contact_email: Optional[str] = None
|
||||
contact_phone: Optional[str] = None
|
||||
gps_coordinates: Optional[str] = None
|
||||
|
||||
class PersonReportResponse(PersonReportBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional
|
||||
from jose import JWTError, jwt
|
||||
from passlib.context import CryptContext
|
||||
|
|
@ -19,9 +19,10 @@ logger.info("Test log message")
|
|||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
# Configuration pour OAuth2
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
|
||||
class AuthService:
|
||||
|
||||
@staticmethod
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
passEncr = pwd_context.hash(plain_password)
|
||||
|
|
@ -58,14 +59,14 @@ class AuthService:
|
|||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
expire = datetime.now(timezone.utc) + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=settings.access_token_expire_minutes)
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.access_token_expire_minutes)
|
||||
to_encode.update({"exp": expire})
|
||||
return jwt.encode(to_encode, settings.secret_key, algorithm=settings.algorithm)
|
||||
|
||||
@staticmethod
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme), db=Depends(get_db)):
|
||||
async def get_current_user(token: str, db):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
|
|
@ -147,7 +148,7 @@ class AuthService:
|
|||
|
||||
|
||||
@staticmethod
|
||||
async def admin_required(token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
|
||||
async def admin_required(token: str, db):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You do not have permission to perform this action.",
|
||||
|
|
@ -177,7 +178,7 @@ class AuthService:
|
|||
|
||||
|
||||
@staticmethod
|
||||
async def update_user(user_id: int, updates: dict, token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
|
||||
async def update_user(user_id: int, updates: dict, token: str, db):
|
||||
"""
|
||||
Met à jour les informations d'un utilisateur.
|
||||
- Un utilisateur peut mettre à jour ses propres informations.
|
||||
|
|
@ -240,7 +241,35 @@ class AuthService:
|
|||
return UserResponse(**updated_user)
|
||||
|
||||
@staticmethod
|
||||
async def get_user_by_email(email: str, db: AsyncSession):
|
||||
async def get_user_by_email(email: str, db):
|
||||
query = select(users_table).where(users_table.c.email == email)
|
||||
result = await db.execute(query)
|
||||
return result.mappings().first()
|
||||
return result.mappings().first()
|
||||
|
||||
@staticmethod
|
||||
async def check_permissions(token: str, db, required_permissions: list):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You do not have permission to perform this action.",
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])
|
||||
email: str = payload.get("sub")
|
||||
if email is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(email=email)
|
||||
except JWTError:
|
||||
logger.error("could not decode token", extra={"token": token})
|
||||
raise credentials_exception
|
||||
|
||||
query = users_table.select().where(users_table.c.email == token_data.email)
|
||||
result = await db.execute(query)
|
||||
user = result.mappings().first()
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
|
||||
if user["role"] not in required_permissions:
|
||||
logger.error("permission denied", extra={"email": token_data.email, "required_permissions": required_permissions})
|
||||
raise credentials_exception
|
||||
|
||||
logger.info("permission granted", extra={"email": token_data.email, "role": user["role"]})
|
||||
|
|
@ -100,9 +100,11 @@ class NeedRequestService:
|
|||
# D<>codage du token JWT
|
||||
payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])
|
||||
email: str = payload.get("sub")
|
||||
logger.info("email from payload.get(sub) " +email)
|
||||
if email is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(email=email)
|
||||
logger.info("token_data from TokenData(email=email) " +token_data)
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
|
||||
|
|
@ -111,7 +113,7 @@ class NeedRequestService:
|
|||
|
||||
result = await db.execute(user_query)
|
||||
user = result.mappings().fetchone()
|
||||
logger.info("user loooooooooooooooooooo: " +user)
|
||||
logger.info("user from database: " +user)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,21 @@ from typing import Optional
|
|||
from fastapi import Depends
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
from services.auth_service import AuthService
|
||||
from services.s3_service import UploadService
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
|
||||
class PersonReportService:
|
||||
@staticmethod
|
||||
async def create_report(report: PersonReportCreate, db):
|
||||
query = person_reports_table.insert().values(**report.model_dump())
|
||||
async def create_report(report: PersonReportCreate, db, image_file, token):
|
||||
user = await AuthService.get_current_user(token, db)
|
||||
image_url = await UploadService.upload_image_to_s3(image_file, user.email) if image_file else None
|
||||
|
||||
query = person_reports_table.insert().values(**report.model_dump(),
|
||||
photo_url=image_url,
|
||||
reporter_email=user["email"])
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
await db.commit()
|
||||
|
|
@ -53,7 +61,7 @@ class PersonReportService:
|
|||
if status:
|
||||
query = query.where(person_reports_table.c.status == status)
|
||||
result = await db.execute(query)
|
||||
reports = result.fetchall()
|
||||
reports = result.mappings().all()
|
||||
return [PersonReportResponse(**report) for report in reports]
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
from sqlalchemy import insert, select, update, delete
|
||||
from fastapi import HTTPException
|
||||
from models.db import points_of_interest_table
|
||||
from models.schemas import PointOfInterest
|
||||
from models.schemas import CreatePointOfInterest, PointOfInterest, UpdatePointOfInterest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from services.auth_service import AuthService
|
||||
|
||||
|
||||
class PointsOfInterestService:
|
||||
@staticmethod
|
||||
async def create_point_of_interest(point: PointOfInterest, db: AsyncSession):
|
||||
async def create_point_of_interest(point: CreatePointOfInterest, db, token):
|
||||
user = await AuthService.get_current_user(token, db)
|
||||
query = insert(points_of_interest_table).values(
|
||||
label=point.label,
|
||||
description=point.description,
|
||||
icon=point.icon,
|
||||
organization=point.organization,
|
||||
gps_coordinates=point.gps_coordinates,
|
||||
added_by=point.added_by,
|
||||
added_by=user["id"],
|
||||
)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
|
|
@ -26,26 +29,33 @@ class PointsOfInterestService:
|
|||
raise HTTPException(status_code=500, detail=f"Could not create point of interest: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
async def get_point_of_interest(point_id: int, db: AsyncSession):
|
||||
async def get_point_of_interest(point_id: int, db):
|
||||
query = select(points_of_interest_table).where(points_of_interest_table.c.id == point_id)
|
||||
result = await db.execute(query)
|
||||
point = result.fetchone()
|
||||
point = result.mappings().fetchone()
|
||||
if not point:
|
||||
raise HTTPException(status_code=404, detail="Point of interest not found")
|
||||
return dict(point)
|
||||
|
||||
@staticmethod
|
||||
async def get_all_points_of_interest(db: AsyncSession):
|
||||
async def get_all_points_of_interest(db):
|
||||
query = select(points_of_interest_table)
|
||||
result = await db.execute(query)
|
||||
return [dict(row) for row in result.fetchall()]
|
||||
points = result.mappings().all()
|
||||
return [dict(point) for point in points]
|
||||
|
||||
@staticmethod
|
||||
async def update_point_of_interest(point_id: int, data: dict, db: AsyncSession):
|
||||
async def update_point_of_interest(point_id: int, point: UpdatePointOfInterest, db, token):
|
||||
user = await AuthService.get_current_user(token, db)
|
||||
|
||||
update_values = point.model_dump(exclude_unset=True)
|
||||
if not update_values:
|
||||
raise HTTPException(status_code=400, detail="No fields provided for update")
|
||||
|
||||
query = (
|
||||
update(points_of_interest_table)
|
||||
.where(points_of_interest_table.c.id == point_id)
|
||||
.values(**data)
|
||||
.values(**update_values, added_by=user["id"])
|
||||
)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
|
|
@ -57,8 +67,10 @@ class PointsOfInterestService:
|
|||
await db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"Could not update point of interest: {str(e)}")
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def delete_point_of_interest(point_id: int, db: AsyncSession):
|
||||
async def delete_point_of_interest(point_id: int, db, token):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = delete(points_of_interest_table).where(points_of_interest_table.c.id == point_id)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ from fastapi import Depends, HTTPException, status
|
|||
|
||||
from services.auth_service import AuthService
|
||||
|
||||
# Configuration pour OAuth2
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
class ReportService:
|
||||
@staticmethod
|
||||
|
|
@ -29,7 +27,7 @@ class ReportService:
|
|||
raise HTTPException(status_code=500, detail=f"Could not create user report: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
async def get_report_by_id(report_id: int, db, token: str = Depends(oauth2_scheme)):
|
||||
async def get_report_by_id(report_id: int, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = select(user_reports_table).where(user_reports_table.c.id == report_id)
|
||||
result = await db.execute(query)
|
||||
|
|
@ -39,7 +37,7 @@ class ReportService:
|
|||
return dict(report)
|
||||
|
||||
@staticmethod
|
||||
async def get_all_reports(db, token: str = Depends(oauth2_scheme)):
|
||||
async def get_all_reports(db, token: str):
|
||||
|
||||
await AuthService.admin_required(token, db)
|
||||
query = select(user_reports_table)
|
||||
|
|
@ -48,7 +46,7 @@ class ReportService:
|
|||
return [dict(report) for report in reports]
|
||||
|
||||
@staticmethod
|
||||
async def update_report(report_id: int, report_update: UserReportUpdate, db, token: str = Depends(oauth2_scheme)):
|
||||
async def update_report(report_id: int, report_update: UserReportUpdate, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = (
|
||||
update(user_reports_table)
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ from jose import jwt, JWTError
|
|||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from services.auth_service import AuthService
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
|
||||
|
||||
|
||||
class RoleService:
|
||||
@staticmethod
|
||||
async def get_all_roles(db: AsyncSession):
|
||||
|
|
@ -37,7 +34,7 @@ class RoleService:
|
|||
return roles_with_permissions
|
||||
|
||||
@staticmethod
|
||||
async def create_role(name: str, db, token: str):
|
||||
async def create_role(name: str, db: AsyncSession, token: str):
|
||||
"""
|
||||
Crée un nouveau rôle avec les permissions spécifiées (réservé aux administrateurs).
|
||||
"""
|
||||
|
|
@ -126,7 +123,7 @@ class RoleService:
|
|||
return permissions_result
|
||||
|
||||
@staticmethod
|
||||
async def get_role(role_id: int, db):
|
||||
async def get_role(role_id: int, db: AsyncSession):
|
||||
"""
|
||||
Récupère les détails d'un rôle spécifique, y compris ses permissions associées.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,51 +1,64 @@
|
|||
from sqlalchemy import insert, select, update, delete
|
||||
from fastapi import HTTPException
|
||||
from models.db import shelters_table
|
||||
from models.schemas import Shelter
|
||||
from models.schemas import Shelter, UpdateShelter
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from services.auth_service import AuthService
|
||||
|
||||
|
||||
class ShelterService:
|
||||
|
||||
permissions = ["admin", "moderator"]
|
||||
|
||||
@staticmethod
|
||||
async def create_shelter(shelter: Shelter, db: AsyncSession):
|
||||
async def create_shelter(shelter: Shelter, db, token):
|
||||
user = await AuthService.get_current_user(token, db)
|
||||
query = insert(shelters_table).values(
|
||||
label=shelter.label,
|
||||
description=shelter.description,
|
||||
status=shelter.status,
|
||||
contact_person=shelter.contact_person,
|
||||
gps_coordinates=shelter.gps_coordinates,
|
||||
added_by=shelter.added_by,
|
||||
added_by=user["id"],
|
||||
)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
await db.commit()
|
||||
shelter_id = result.inserted_primary_key[0]
|
||||
return {"id": shelter_id, **shelter.dict()}
|
||||
return {"id": shelter_id, **shelter.model_dump()}
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"Could not create shelter: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
async def get_shelter(shelter_id: int, db: AsyncSession):
|
||||
async def get_shelter(shelter_id: int, db):
|
||||
query = select(shelters_table).where(shelters_table.c.id == shelter_id)
|
||||
result = await db.execute(query)
|
||||
shelter = result.fetchone()
|
||||
shelter = result.mappings().fetchone()
|
||||
if not shelter:
|
||||
raise HTTPException(status_code=404, detail="Shelter not found")
|
||||
return dict(shelter)
|
||||
|
||||
@staticmethod
|
||||
async def get_all_shelters(db: AsyncSession):
|
||||
async def get_all_shelters(db):
|
||||
query = select(shelters_table)
|
||||
result = await db.execute(query)
|
||||
return [dict(row) for row in result.fetchall()]
|
||||
shelters = result.mappings().fetchall()
|
||||
return [dict(shelter) for shelter in shelters]
|
||||
|
||||
@staticmethod
|
||||
async def update_shelter(shelter_id: int, data: dict, db: AsyncSession):
|
||||
async def update_shelter(shelter_id: int, shelter: UpdateShelter, db, token):
|
||||
user = await AuthService.get_current_user(token, db)
|
||||
update_values = shelter.model_dump(exclude_unset=True)
|
||||
if not update_values:
|
||||
raise HTTPException(status_code=400, detail="No fields provided for update")
|
||||
|
||||
query = (
|
||||
update(shelters_table)
|
||||
.where(shelters_table.c.id == shelter_id)
|
||||
.values(**data)
|
||||
.values(**update_values,
|
||||
added_by=user["id"])
|
||||
)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
|
|
@ -58,7 +71,9 @@ class ShelterService:
|
|||
raise HTTPException(status_code=500, detail=f"Could not update shelter: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
async def delete_shelter(shelter_id: int, db: AsyncSession):
|
||||
async def delete_shelter(shelter_id: int, db, token):
|
||||
#await AuthService.check_permissions(token, db, ShelterService.permissions)
|
||||
await AuthService.admin_required(token, db)
|
||||
query = delete(shelters_table).where(shelters_table.c.id == shelter_id)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
from sqlalchemy import insert, select, update, delete
|
||||
from fastapi import HTTPException
|
||||
from models.db import technical_issues_table
|
||||
from models.schemas import TechnicalIssue, UpdateTechnicalIssue
|
||||
from models.schemas import CreateTechnicalIssue, UpdateTechnicalIssue
|
||||
from services.auth_service import AuthService
|
||||
|
||||
|
||||
class TechnicalIssueService:
|
||||
@staticmethod
|
||||
async def create_issue(issue: TechnicalIssue, db):
|
||||
async def create_issue(issue: CreateTechnicalIssue, db, token: str):
|
||||
user = await AuthService.get_current_user(token, db);
|
||||
query = insert(technical_issues_table).values(
|
||||
user_id=issue.user_id,
|
||||
user_id=user["id"],
|
||||
description=issue.description,
|
||||
status=issue.status
|
||||
)
|
||||
|
|
@ -16,32 +18,36 @@ class TechnicalIssueService:
|
|||
result = await db.execute(query)
|
||||
await db.commit()
|
||||
issue_id = result.inserted_primary_key[0]
|
||||
return {"id": issue_id, **issue.dict()}
|
||||
return {"id": issue_id, **issue.model_dump()}
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"Could not create issue: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
async def get_issue(issue_id: int, db):
|
||||
async def get_issue(issue_id: int, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = select(technical_issues_table).where(technical_issues_table.c.id == issue_id)
|
||||
result = await db.execute(query)
|
||||
issue = result.fetchone()
|
||||
issue = result.mappings().fetchone()
|
||||
if not issue:
|
||||
raise HTTPException(status_code=404, detail="Technical issue not found")
|
||||
return dict(issue)
|
||||
|
||||
@staticmethod
|
||||
async def get_all_issues(db):
|
||||
async def get_all_issues(db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = select(technical_issues_table)
|
||||
result = await db.execute(query)
|
||||
return [dict(row) for row in result.fetchall()]
|
||||
technical_issues = result.mappings().all()
|
||||
return [dict(technical_issue) for technical_issue in technical_issues]
|
||||
|
||||
@staticmethod
|
||||
async def update_issue(issue_id: int, issue_data: UpdateTechnicalIssue, db):
|
||||
async def update_issue(issue_id: int, issue_data: UpdateTechnicalIssue, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = (
|
||||
update(technical_issues_table)
|
||||
.where(technical_issues_table.c.id == issue_id)
|
||||
.values(**issue_data.dict(exclude_unset=True))
|
||||
.values(**issue_data.model_dump(exclude_unset=True))
|
||||
)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
|
|
@ -54,7 +60,8 @@ class TechnicalIssueService:
|
|||
raise HTTPException(status_code=500, detail=f"Could not update issue: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
async def delete_issue(issue_id: int, db):
|
||||
async def delete_issue(issue_id: int, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = delete(technical_issues_table).where(technical_issues_table.c.id == issue_id)
|
||||
try:
|
||||
result = await db.execute(query)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,71 @@
|
|||
# app/services/upload_service.py
|
||||
from typing import Self
|
||||
import uuid
|
||||
import boto3
|
||||
from botocore.exceptions import NoCredentialsError
|
||||
from botocore.exceptions import NoCredentialsError, BotoCoreError, ClientError
|
||||
from fastapi import HTTPException, UploadFile
|
||||
|
||||
class UploadService:
|
||||
def __init__(self, bucket_name):
|
||||
self.s3_client = boto3.client('s3')
|
||||
self.bucket_name = bucket_name
|
||||
|
||||
def upload_file(self, file, file_name):
|
||||
async def upload_image_to_s3(file: UploadFile, account_id: str) -> dict:
|
||||
|
||||
# Configuration globale
|
||||
EXTENSIONS_AUTHORIZED = {
|
||||
"docx": "doc",
|
||||
"pdf": "doc",
|
||||
"png": "image",
|
||||
"jpg": "image",
|
||||
"jpeg": "image",
|
||||
"mp4": "video",
|
||||
}
|
||||
FILE_PATH_S3 = {
|
||||
"image": "images/",
|
||||
"video": "videos/",
|
||||
"doc": "docs/",
|
||||
}
|
||||
|
||||
S3_CONFIG = {
|
||||
"bucket": "sywmtnsg",
|
||||
"endpoint_url": "https://ht2-storage.n0c.com:5443",
|
||||
"region_name": "ht2-storage",
|
||||
"aws_access_key_id": "X89EU8NHL54CIKGMZP8Q",
|
||||
"aws_secret_access_key": "71RsicRiiSgXDcAZLM4vCpEZESMJ4iA9sOKp0UQy",
|
||||
}
|
||||
|
||||
# Validate file extension
|
||||
extension = file.filename.split(".")[-1].lower()
|
||||
if extension not in EXTENSIONS_AUTHORIZED:
|
||||
raise HTTPException(status_code=400, detail="Unsupported file extension.")
|
||||
|
||||
# Generate unique file name
|
||||
new_filename = f"{uuid.uuid4()}.{extension}"
|
||||
file_type = EXTENSIONS_AUTHORIZED[extension]
|
||||
storage_path = f"public/{FILE_PATH_S3[file_type]}{account_id}/{new_filename}"
|
||||
|
||||
# Initialize S3 client
|
||||
try:
|
||||
self.s3_client.upload_fileobj(file, self.bucket_name, file_name)
|
||||
return f"https://{self.bucket_name}.s3.amazonaws.com/{file_name}"
|
||||
except NoCredentialsError:
|
||||
raise Exception("AWS credentials not found")
|
||||
except Exception as e:
|
||||
raise e
|
||||
s3_client = boto3.client(
|
||||
"s3",
|
||||
endpoint_url=S3_CONFIG["endpoint_url"],
|
||||
region_name=S3_CONFIG["region_name"],
|
||||
aws_access_key_id=S3_CONFIG["aws_access_key_id"],
|
||||
aws_secret_access_key=S3_CONFIG["aws_secret_access_key"],
|
||||
)
|
||||
|
||||
# Upload file
|
||||
s3_client.upload_fileobj(
|
||||
file.file,
|
||||
S3_CONFIG["bucket"],
|
||||
storage_path,
|
||||
ExtraArgs={"ACL": "public-read"},
|
||||
)
|
||||
except (BotoCoreError, ClientError) as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to upload file: {str(e)}")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"path_with_name": storage_path.replace("public", ""),
|
||||
"filename": new_filename,
|
||||
"original_filename": file.filename,
|
||||
"filetype": extension,
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||
from models.schemas import UserCreate, UserResponse, UserUpdateRole, UserBlockBan
|
||||
from config.database import get_db
|
||||
from models.db import users_table
|
||||
from services.auth_service import AuthService
|
||||
from utils.security import get_password_hash
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -11,7 +12,9 @@ class UserService:
|
|||
|
||||
|
||||
@staticmethod
|
||||
async def list_users(status: Optional[str] = None, db=Depends(get_db)):
|
||||
async def list_users(token: str, status: Optional[str] = None, db=Depends(get_db)):
|
||||
await AuthService.check_permissions(token, ["admin"], db)
|
||||
|
||||
query = select(users_table)
|
||||
if status:
|
||||
query = query.where(users_table.c.status == status)
|
||||
|
|
@ -20,7 +23,7 @@ class UserService:
|
|||
return [UserResponse(**user) for user in users]
|
||||
|
||||
@staticmethod
|
||||
async def create_user(user: UserCreate, db: AsyncSession):
|
||||
async def create_user(user: UserCreate, db):
|
||||
hashed_password = get_password_hash(user.password)
|
||||
query = users_table.insert().values(
|
||||
email=user.email,
|
||||
|
|
@ -42,7 +45,9 @@ class UserService:
|
|||
raise HTTPException(status_code=500, detail=f"Error creating user: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
async def change_user_role(user_update: UserUpdateRole, db: AsyncSession):
|
||||
async def change_user_role(user_update: UserUpdateRole, db, token: str):
|
||||
await AuthService.check_permissions(token, ["admin"], db)
|
||||
|
||||
query = (
|
||||
update(users_table)
|
||||
.where(users_table.c.email == user_update.email)
|
||||
|
|
@ -55,7 +60,8 @@ class UserService:
|
|||
return {"message": f"Role updated to {user_update.new_role}"}
|
||||
|
||||
@staticmethod
|
||||
async def block_user(user_action: UserBlockBan, db: AsyncSession):
|
||||
async def block_user(user_action: UserBlockBan, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = (
|
||||
update(users_table)
|
||||
.where(users_table.c.email == user_action.email)
|
||||
|
|
@ -68,7 +74,9 @@ class UserService:
|
|||
return {"message": f"User {user_action.email} blocked"}
|
||||
|
||||
@staticmethod
|
||||
async def ban_user(user_action: UserBlockBan, db: AsyncSession):
|
||||
async def ban_user(user_action: UserBlockBan, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
|
||||
query = (
|
||||
update(users_table)
|
||||
.where(users_table.c.email == user_action.email)
|
||||
|
|
@ -81,7 +89,8 @@ class UserService:
|
|||
return {"message": f"User {user_action.email} banned"}
|
||||
|
||||
@staticmethod
|
||||
async def unblock_user(user_action: UserBlockBan, db: AsyncSession):
|
||||
async def unblock_user(user_action: UserBlockBan, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = (
|
||||
update(users_table)
|
||||
.where(users_table.c.email == user_action.email)
|
||||
|
|
@ -94,7 +103,8 @@ class UserService:
|
|||
return {"message": f"User {user_action.email} unblocked"}
|
||||
|
||||
@staticmethod
|
||||
async def unban_user(user_action: UserBlockBan, db: AsyncSession):
|
||||
async def unban_user(user_action: UserBlockBan, db, token: str):
|
||||
await AuthService.admin_required(token, db)
|
||||
query = (
|
||||
update(users_table)
|
||||
.where(users_table.c.email == user_action.email)
|
||||
|
|
|
|||
Loading…
Reference in New Issue