First commit from visualcode
parent
25524998c1
commit
b3ef8d4a19
|
|
@ -0,0 +1,27 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
from services.auth_service import AuthService
|
||||||
|
from models.schemas import Token, UserCreate, UserResponse
|
||||||
|
from config.database import get_db
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.post("/signup", response_model=UserResponse, status_code=201)
|
||||||
|
async def signup(user: UserCreate, db=Depends(get_db)):
|
||||||
|
return await AuthService.create_user(user, db)
|
||||||
|
|
||||||
|
@router.post("/token", response_model=Token)
|
||||||
|
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db=Depends(get_db)):
|
||||||
|
user = await AuthService.authenticate_user(form_data.username, form_data.password, db)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Incorrect email or password",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
access_token = AuthService.create_access_token(data={"sub": user["email"]})
|
||||||
|
return {"access_token": access_token, "token_type": "bearer"}
|
||||||
|
|
||||||
|
@router.get("/me", response_model=UserResponse)
|
||||||
|
async def read_users_me(current_user: UserResponse = Depends(AuthService.get_current_user)):
|
||||||
|
return current_user
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from services.message_service import MessageService
|
||||||
|
from models.schemas import TechnicalIssue
|
||||||
|
from config.database import get_db
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.post("/", status_code=201)
|
||||||
|
async def report_issue(issue: TechnicalIssue, db=Depends(get_db)):
|
||||||
|
return await MessageService.create_issue(issue, db)
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
|
from services.need_request_service import NeedRequestService
|
||||||
|
from models.schemas import NeedRequestCreate, NeedRequestUpdate
|
||||||
|
from config.database import get_db
|
||||||
|
from services.auth_service import AuthService
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/", status_code=status.HTTP_201_CREATED)
|
||||||
|
async def request_need(need: NeedRequestCreate, db=Depends(get_db)):
|
||||||
|
|
||||||
|
return await NeedRequestService.create_need(need, db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{need_id}", status_code=status.HTTP_200_OK)
|
||||||
|
async def get_need(need_id: int, db=Depends(get_db)):
|
||||||
|
|
||||||
|
need = await NeedRequestService.get_need_by_id(need_id, db)
|
||||||
|
if not need:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Need request not found")
|
||||||
|
return need
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{need_id}", status_code=status.HTTP_200_OK)
|
||||||
|
async def update_need(
|
||||||
|
need_id: int,
|
||||||
|
need_update: NeedRequestUpdate,
|
||||||
|
db=Depends(get_db),
|
||||||
|
current_user=Depends(AuthService.get_current_user),
|
||||||
|
):
|
||||||
|
|
||||||
|
need = await NeedRequestService.get_need_by_id(need_id, db)
|
||||||
|
if not need:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Need request not found")
|
||||||
|
|
||||||
|
# Vérifie si l'utilisateur est l'auteur ou un administrateur
|
||||||
|
if need.requester_email != current_user.email and not await admin_required(db=db):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="You do not have permission to update this need request",
|
||||||
|
)
|
||||||
|
|
||||||
|
return await NeedRequestService.update_need(need_id, need_update, db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{need_id}", status_code=status.HTTP_200_OK)
|
||||||
|
async def delete_need(
|
||||||
|
need_id: int,
|
||||||
|
db=Depends(get_db),
|
||||||
|
current_user=Depends(AuthService.get_current_user),
|
||||||
|
):
|
||||||
|
|
||||||
|
need = await NeedRequestService.get_need_by_id(need_id, db)
|
||||||
|
if not need:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Need request not found")
|
||||||
|
|
||||||
|
# Vérifie si l'utilisateur est l'auteur ou un administrateur
|
||||||
|
if need.requester_email != current_user.email and not await admin_required(db=db):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="You do not have permission to delete this need request",
|
||||||
|
)
|
||||||
|
|
||||||
|
return await NeedRequestService.delete_need(need_id, db)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from services.person_report_service import PersonReportService
|
||||||
|
from models.schemas import PersonReportCreate, PersonReportUpdate, PersonReportResponse
|
||||||
|
from config.database import get_db
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
@router.put("/{report_id}", response_model=PersonReportResponse)
|
||||||
|
async def update_report(report_id: int, report: PersonReportUpdate, db=Depends(get_db)):
|
||||||
|
return await PersonReportService.update_report(report_id, report, db)
|
||||||
|
|
||||||
|
@router.get("/{report_id}", response_model=PersonReportResponse)
|
||||||
|
async def get_report(report_id: int, db=Depends(get_db)):
|
||||||
|
return await PersonReportService.get_report(report_id, db)
|
||||||
|
|
||||||
|
@router.get("/", response_model=list[PersonReportResponse])
|
||||||
|
async def list_reports(status: Optional[str] = None, db=Depends(get_db)):
|
||||||
|
return await PersonReportService.list_reports(status, db)
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from services.points_of_interest_service import PointsOfInterestService
|
||||||
|
from models.schemas import PointOfInterest
|
||||||
|
from config.database import get_db
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/", status_code=201)
|
||||||
|
async def create_point(point: PointOfInterest, db=Depends(get_db)):
|
||||||
|
return await PointsOfInterestService.create_point_of_interest(point, db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{point_id}")
|
||||||
|
async def get_point(point_id: int, db=Depends(get_db)):
|
||||||
|
return await PointsOfInterestService.get_point_of_interest(point_id, db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_all_points(db=Depends(get_db)):
|
||||||
|
return await PointsOfInterestService.get_all_points_of_interest(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)
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status
from services.report_service import ReportService
from models.schemas import UserReport, UserReportUpdate
from config.database import get_db
from services.auth_service import AuthService
admin_required = AuthService.admin_required
router = APIRouter()
@router.post("/", status_code=status.HTTP_201_CREATED)
async def report_user(report: UserReport, db=Depends(get_db)):
return await ReportService.create_report(report, db)
@router.get("/{report_id}", status_code=status.HTTP_200_OK)
async def get_report(report_id: int, db=Depends(get_db), current_user=Depends(admin_required)):
return await ReportService.get_report_by_id(report_id, db)
@router.get("/", status_code=status.HTTP_200_OK)
async def get_all_reports(db=Depends(get_db), current_user=Depends(admin_required)):
return await ReportService.get_all_reports(db)
@router.put("/{report_id}", status_code=status.HTTP_200_OK)
async def update_report(report_id: int, report_update: UserReportUpdate, db=Depends(get_db), current_user=Depends(admin_required)):
return await ReportService.update_report(report_id, report_update, db)
@router.delete("/{report_id}", status_code=status.HTTP_200_OK)
async def delete_report(report_id: int, db=Depends(get_db), current_user=Depends(admin_required)):
return await ReportService.delete_report(report_id, db)
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from services.role_service import RoleService
|
||||||
|
from models.schemas import Role
|
||||||
|
from config.database import get_db
|
||||||
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/", status_code=201)
|
||||||
|
async def create_role(role: Role, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||||
|
return await RoleService.create_role(role, db, token)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{role_id}")
|
||||||
|
async def get_role(role_id: int, db=Depends(get_db)):
|
||||||
|
return await RoleService.get_role(role_id, db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_all_roles(db=Depends(get_db)):
|
||||||
|
return await RoleService.get_all_roles(db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{role_id}")
|
||||||
|
async def update_role(role_id: int, data: dict, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||||
|
return await RoleService.update_role(role_id, data, db, token)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{role_id}")
|
||||||
|
async def delete_role(role_id: int, db=Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||||
|
return await RoleService.delete_role(role_id, db, token)
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from services.shelter_service import ShelterService
|
||||||
|
from models.schemas import Shelter
|
||||||
|
from config.database import get_db
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/", status_code=201)
|
||||||
|
async def create_shelter(shelter: Shelter, db=Depends(get_db)):
|
||||||
|
return await ShelterService.create_shelter(shelter, db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{shelter_id}")
|
||||||
|
async def get_shelter(shelter_id: int, db=Depends(get_db)):
|
||||||
|
return await ShelterService.get_shelter(shelter_id, db)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_all_shelters(db=Depends(get_db)):
|
||||||
|
return await ShelterService.get_all_shelters(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)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{shelter_id}")
|
||||||
|
async def delete_shelter(shelter_id: int, db=Depends(get_db)):
|
||||||
|
return await ShelterService.delete_shelter(shelter_id, db)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from services.technical_issue_service import TechnicalIssueService
|
||||||
|
from models.schemas import TechnicalIssue, UpdateTechnicalIssue
|
||||||
|
from config.database import get_db
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.post("/", status_code=201)
|
||||||
|
async def create_issue(issue: TechnicalIssue, db=Depends(get_db)):
|
||||||
|
return await TechnicalIssueService.create_issue(issue, db)
|
||||||
|
|
||||||
|
@router.get("/{issue_id}")
|
||||||
|
async def get_issue(issue_id: int, db=Depends(get_db)):
|
||||||
|
return await TechnicalIssueService.get_issue(issue_id, db)
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_all_issues(db=Depends(get_db)):
|
||||||
|
return await TechnicalIssueService.get_all_issues(db)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
@router.delete("/{issue_id}")
|
||||||
|
async def delete_issue(issue_id: int, db=Depends(get_db)):
|
||||||
|
return await TechnicalIssueService.delete_issue(issue_id, db)
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# app/api/v1/upload.py
|
||||||
|
from fastapi import APIRouter, UploadFile, File, HTTPException
|
||||||
|
from services.upload_service import UploadService
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
# Instancier UploadService avec le nom de ton bucket S3
|
||||||
|
upload_service = UploadService(bucket_name="ton-nom-de-bucket")
|
||||||
|
|
||||||
|
@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))
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from services.user_service import UserService
|
||||||
|
from models.schemas import UserUpdateRole, UserBlockBan, UserResponse
|
||||||
|
from config.database import get_db
|
||||||
|
from services.auth_service import AuthService
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
@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.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("/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("/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("/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)
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy import Table, Column, Integer, String, DateTime, Boolean, MetaData, ForeignKey, Text
|
||||||
|
from config.settings import settings
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Configuration du logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Métadonnées pour les tables
|
||||||
|
metadata = MetaData()
|
||||||
|
|
||||||
|
# Table des utilisateurs
|
||||||
|
users_table = Table(
|
||||||
|
"users",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("email", String(255), unique=True, nullable=False), # VARCHAR(255)
|
||||||
|
Column("full_name", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("phone", String(20), nullable=False), # VARCHAR(20)
|
||||||
|
Column("date_of_birth", DateTime, nullable=False),
|
||||||
|
Column("organization", String(255)), # VARCHAR(255)
|
||||||
|
Column("hashed_password", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("role", String(50), default="user"), # VARCHAR(50)
|
||||||
|
Column("is_blocked", Boolean, default=False),
|
||||||
|
Column("is_deleted", Boolean, default=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des rôles
|
||||||
|
roles_table = Table(
|
||||||
|
"roles",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("name", String(50), unique=True, nullable=False), # VARCHAR(50)
|
||||||
|
Column("permissions", Text) # Stocke les permissions sous forme de chaîne séparée par des virgules
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des demandes de besoin
|
||||||
|
need_requests_table = Table(
|
||||||
|
"need_requests",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("category", String(50), nullable=False), # Ex: "water", "food", "medical assistance"
|
||||||
|
Column("description", Text, nullable=False),
|
||||||
|
Column("adults", Integer, nullable=False),
|
||||||
|
Column("children", Integer, nullable=False),
|
||||||
|
Column("vulnerable", Integer, nullable=False),
|
||||||
|
Column("location", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100)), # VARCHAR(100)
|
||||||
|
Column("requester_email", String(255), ForeignKey("users.email"), nullable=False), # VARCHAR(255)
|
||||||
|
Column("assigned_to", Integer, ForeignKey("users.id")), # Utilisateur qui a pris en charge la demande
|
||||||
|
Column("status", String(50), default="pending") # Ex: "pending", "in_progress", "completed"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des signalements d'utilisateurs
|
||||||
|
user_reports_table = Table(
|
||||||
|
"user_reports",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("reporter_id", Integer, ForeignKey("users.id"), nullable=False),
|
||||||
|
Column("reported_user_id", Integer, ForeignKey("users.id"), nullable=False),
|
||||||
|
Column("reason", Text, nullable=False),
|
||||||
|
Column("status", String(50), default="pending") # Ex: "pending", "resolved"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des problèmes techniques
|
||||||
|
technical_issues_table = Table(
|
||||||
|
"technical_issues",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("user_id", Integer, ForeignKey("users.id"), nullable=False),
|
||||||
|
Column("description", Text, nullable=False),
|
||||||
|
Column("status", String(50), default="open") # Ex: "open", "in_progress", "resolved"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des points d'intérêt
|
||||||
|
points_of_interest_table = Table(
|
||||||
|
"points_of_interest",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("label", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("description", Text),
|
||||||
|
Column("icon", String(255)), # URL de l'icône (VARCHAR(255))
|
||||||
|
Column("organization", String(255)), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100), nullable=False), # VARCHAR(100)
|
||||||
|
Column("added_by", Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des abris
|
||||||
|
shelters_table = Table(
|
||||||
|
"shelters",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("label", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("description", Text),
|
||||||
|
Column("status", String(50), default="available"), # Ex: "available", "full", "closed"
|
||||||
|
Column("contact_person", String(255)), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100), nullable=False), # VARCHAR(100)
|
||||||
|
Column("added_by", Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des déclarations de personnes
|
||||||
|
person_reports_table = Table(
|
||||||
|
"person_reports",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("full_name", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("date_of_birth", DateTime, nullable=False),
|
||||||
|
Column("status", String(50), nullable=False), # Ex: "missing", "safe", "deceased"
|
||||||
|
Column("location", String(255)), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100)), # VARCHAR(100)
|
||||||
|
Column("photo_url", String(255)), # URL de la photo (VARCHAR(255))
|
||||||
|
Column("reporter_email", String(255), ForeignKey("users.email"), nullable=False), # VARCHAR(255)
|
||||||
|
Column("created_at", DateTime, default=datetime.utcnow), # Date de création
|
||||||
|
Column("updated_at", DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Date de mise à jour
|
||||||
|
)
|
||||||
|
|
||||||
|
# Connexion à la base de données
|
||||||
|
DATABASE_URL = settings.database_url
|
||||||
|
engine = create_async_engine(DATABASE_URL, echo=True)
|
||||||
|
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||||
|
|
||||||
|
# Fonction pour initialiser la base de données
|
||||||
|
async def init_db():
|
||||||
|
try:
|
||||||
|
async with engine.begin() as conn:
|
||||||
|
await conn.run_sync(metadata.create_all)
|
||||||
|
logger.info("Database tables created successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating database tables: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Fonction pour obtenir une session de base de données
|
||||||
|
async def get_db():
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
yield session
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
from pydantic_settings import BaseSettings
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
database_url: str = "mysql+aiomysql://sywmtnsg_admin:EEy_>2JJS0@localhost:6033/sywmtnsg_dm_management"
|
||||||
|
secret_key: str = "LAGs7G8Sis9aQHcipROxpjYRxFZKjr4wNm-_O0pBTkjNYv1rgPUR87VcNswH_VYGpIrsyGdqnNa3vcVSH0f5Tg"
|
||||||
|
algorithm: str = "HS256"
|
||||||
|
access_token_expire_minutes: int = 30
|
||||||
|
aws_access_key_id: str = ""
|
||||||
|
aws_secret_access_key: str = ""
|
||||||
|
aws_bucket_name: str = ""
|
||||||
|
celery_broker_url: str = ""
|
||||||
|
log_level: str = "INFO"
|
||||||
|
email_host: str = ""
|
||||||
|
email_port: int = 587
|
||||||
|
email_username: str = ""
|
||||||
|
email_password: str = ""
|
||||||
|
gdpr_deletion_delay_days: int = 7
|
||||||
|
testing: bool = False
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = ".env"
|
||||||
|
env_file_encoding = "utf-8"
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy import Table, Column, Integer, String, DateTime, Boolean, MetaData, ForeignKey, Text
|
||||||
|
from config.settings import settings
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Configuration du logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Métadonnées pour les tables
|
||||||
|
metadata = MetaData()
|
||||||
|
|
||||||
|
# Table des utilisateurs
|
||||||
|
users_table = Table(
|
||||||
|
"users",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("email", String(255), unique=True, nullable=False), # VARCHAR(255)
|
||||||
|
Column("full_name", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("phone", String(20), nullable=False), # VARCHAR(20)
|
||||||
|
Column("date_of_birth", DateTime, nullable=False),
|
||||||
|
Column("organization", String(255)), # VARCHAR(255)
|
||||||
|
Column("hashed_password", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("role", String(50), default="user"), # VARCHAR(50)
|
||||||
|
Column("is_blocked", Boolean, default=False),
|
||||||
|
Column("is_deleted", Boolean, default=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des rôles
|
||||||
|
roles_table = Table(
|
||||||
|
"roles",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("name", String(50), unique=True, nullable=False), # VARCHAR(50)
|
||||||
|
Column("permissions", Text) # Stocke les permissions sous forme de chaîne séparée par des virgules
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des demandes de besoin
|
||||||
|
need_requests_table = Table(
|
||||||
|
"need_requests",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("category", String(50), nullable=False), # Ex: "water", "food", "medical assistance"
|
||||||
|
Column("description", Text, nullable=False),
|
||||||
|
Column("adults", Integer, nullable=False),
|
||||||
|
Column("children", Integer, nullable=False),
|
||||||
|
Column("vulnerable", Integer, nullable=False),
|
||||||
|
Column("location", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100)), # VARCHAR(100)
|
||||||
|
Column("requester_email", String(255), ForeignKey("users.email"), nullable=False), # VARCHAR(255)
|
||||||
|
Column("assigned_to", Integer, ForeignKey("users.id")), # Utilisateur qui a pris en charge la demande
|
||||||
|
Column("status", String(50), default="pending"), # Ex: "pending", "in_progress", "completed"
|
||||||
|
Column("deleted", DateTime, default=None) # Marque la suppression logique
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des signalements d'utilisateurs
|
||||||
|
user_reports_table = Table(
|
||||||
|
"user_reports",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("reporter_id", Integer, ForeignKey("users.id"), nullable=False),
|
||||||
|
Column("reported_user_id", Integer, ForeignKey("users.id"), nullable=False),
|
||||||
|
Column("reason", Text, nullable=False),
|
||||||
|
Column("status", String(50), default="pending") # Ex: "pending", "resolved"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des problèmes techniques
|
||||||
|
technical_issues_table = Table(
|
||||||
|
"technical_issues",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("user_id", Integer, ForeignKey("users.id"), nullable=False),
|
||||||
|
Column("description", Text, nullable=False),
|
||||||
|
Column("status", String(50), default="open") # Ex: "open", "in_progress", "resolved"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des points d'intérêt
|
||||||
|
points_of_interest_table = Table(
|
||||||
|
"points_of_interest",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("label", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("description", Text),
|
||||||
|
Column("icon", String(255)), # URL de l'icône (VARCHAR(255))
|
||||||
|
Column("organization", String(255)), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100), nullable=False), # VARCHAR(100)
|
||||||
|
Column("added_by", Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des abris
|
||||||
|
shelters_table = Table(
|
||||||
|
"shelters",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("label", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("description", Text),
|
||||||
|
Column("status", String(50), default="available"), # Ex: "available", "full", "closed"
|
||||||
|
Column("contact_person", String(255)), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100), nullable=False), # VARCHAR(100)
|
||||||
|
Column("added_by", Integer, ForeignKey("users.id"), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table des déclarations de personnes
|
||||||
|
person_reports_table = Table(
|
||||||
|
"person_reports",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True),
|
||||||
|
Column("full_name", String(255), nullable=False), # VARCHAR(255)
|
||||||
|
Column("date_of_birth", DateTime, nullable=False),
|
||||||
|
Column("status", String(50), nullable=False), # Ex: "missing", "safe", "deceased"
|
||||||
|
Column("location", String(255)), # VARCHAR(255)
|
||||||
|
Column("gps_coordinates", String(100)), # VARCHAR(100)
|
||||||
|
Column("photo_url", String(255)), # URL de la photo (VARCHAR(255))
|
||||||
|
Column("reporter_email", String(255), ForeignKey("users.email"), nullable=False), # VARCHAR(255)
|
||||||
|
Column("created_at", DateTime, default=datetime.utcnow), # Date de création
|
||||||
|
Column("updated_at", DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Date de mise à jour
|
||||||
|
)
|
||||||
|
|
||||||
|
# Connexion à la base de données
|
||||||
|
DATABASE_URL = settings.database_url
|
||||||
|
engine = create_async_engine(DATABASE_URL, echo=True)
|
||||||
|
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||||
|
|
||||||
|
# Fonction pour initialiser la base de données
|
||||||
|
async def init_db():
|
||||||
|
try:
|
||||||
|
async with engine.begin() as conn:
|
||||||
|
await conn.run_sync(metadata.create_all)
|
||||||
|
logger.info("Database tables created successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating database tables: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Fonction pour obtenir une session de base de données
|
||||||
|
async def get_db():
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
yield session
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
from pydantic import BaseModel, EmailStr
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
class UserBase(BaseModel):
|
||||||
|
email: EmailStr
|
||||||
|
full_name: str
|
||||||
|
phone: str
|
||||||
|
date_of_birth: datetime
|
||||||
|
organization: Optional[str] = None
|
||||||
|
|
||||||
|
class UserCreate(UserBase):
|
||||||
|
password: str
|
||||||
|
role: str
|
||||||
|
|
||||||
|
class UserResponse(BaseModel):
|
||||||
|
email: EmailStr
|
||||||
|
full_name: str
|
||||||
|
phone: str
|
||||||
|
date_of_birth: str
|
||||||
|
organization: Optional[str] = None
|
||||||
|
role: str
|
||||||
|
is_active: bool
|
||||||
|
is_banned: bool
|
||||||
|
|
||||||
|
class UserUpdateRole(BaseModel):
|
||||||
|
email: EmailStr
|
||||||
|
new_role: str
|
||||||
|
|
||||||
|
class UserBlockBan(BaseModel):
|
||||||
|
email: EmailStr
|
||||||
|
|
||||||
|
class Role(BaseModel):
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
permissions: List[str]
|
||||||
|
|
||||||
|
# Demande de besoin (NeedRequest)
|
||||||
|
class NeedRequestBase(BaseModel):
|
||||||
|
category: str
|
||||||
|
description: str
|
||||||
|
adults: int
|
||||||
|
children: int
|
||||||
|
vulnerable: int
|
||||||
|
location: str
|
||||||
|
gps_coordinates: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class NeedRequestCreate(NeedRequestBase):
|
||||||
|
requester_email: EmailStr
|
||||||
|
|
||||||
|
|
||||||
|
class NeedRequestUpdate(BaseModel):
|
||||||
|
category: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
adults: Optional[int] = None
|
||||||
|
children: Optional[int] = None
|
||||||
|
vulnerable: Optional[int] = None
|
||||||
|
location: Optional[str] = None
|
||||||
|
gps_coordinates: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class NeedRequestResponse(NeedRequestBase):
|
||||||
|
id: int
|
||||||
|
requester_email: EmailStr
|
||||||
|
status: str
|
||||||
|
deleted: Optional[datetime] = None
|
||||||
|
|
||||||
|
class UserReport(BaseModel):
|
||||||
|
reporter_id: int
|
||||||
|
reported_user_id: int
|
||||||
|
reason: str
|
||||||
|
status: Optional[str] = "pending"
|
||||||
|
|
||||||
|
class UserReportUpdate(BaseModel):
|
||||||
|
reason: Optional[str] = None
|
||||||
|
status: Optional[str] = None # Exemples : "pending", "resolved"
|
||||||
|
|
||||||
|
class TechnicalIssue(BaseModel):
|
||||||
|
user_id: int
|
||||||
|
description: str
|
||||||
|
status: str
|
||||||
|
|
||||||
|
class UpdateTechnicalIssue(BaseModel):
|
||||||
|
status: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
|
||||||
|
class PersonReportBase(BaseModel):
|
||||||
|
full_name: str
|
||||||
|
date_of_birth: datetime
|
||||||
|
status: str # "missing", "safe", "deceased"
|
||||||
|
location: Optional[str] = None
|
||||||
|
gps_coordinates: Optional[str] = None
|
||||||
|
photo_url: Optional[str] = None
|
||||||
|
reporter_email: str
|
||||||
|
|
||||||
|
class PersonReportCreate(PersonReportBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PersonReportUpdate(BaseModel):
|
||||||
|
status: Optional[str] = None
|
||||||
|
location: Optional[str] = None
|
||||||
|
gps_coordinates: Optional[str] = None
|
||||||
|
photo_url: Optional[str] = None
|
||||||
|
|
||||||
|
class Token(BaseModel):
|
||||||
|
access_token: str
|
||||||
|
token_type: str
|
||||||
|
|
||||||
|
class TokenData(BaseModel):
|
||||||
|
email: Optional[str] = None
|
||||||
|
|
||||||
|
class PointOfInterest(BaseModel):
|
||||||
|
label: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
icon: Optional[str] = None # URL de l'icône
|
||||||
|
organization: Optional[str] = None
|
||||||
|
gps_coordinates: str
|
||||||
|
added_by: int # ID de l'utilisateur qui a ajouté ce point
|
||||||
|
|
||||||
|
class Shelter(BaseModel):
|
||||||
|
label: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
status: str # "available", "full", "closed"
|
||||||
|
contact_person: Optional[str] = None
|
||||||
|
gps_coordinates: str
|
||||||
|
added_by: int # ID de l'utilisateur qui a ajouté cet abri
|
||||||
|
|
||||||
|
class PersonReportResponse(PersonReportBase):
|
||||||
|
id: int
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_USER=sywmtnsg_admin
|
||||||
|
DB_PASSWORD=EEy_>2JJS0
|
||||||
|
DB_NAME=sywmtnsg_dm_management
|
||||||
|
SECRET_KEY=LAGs7G8Sis9aQHcipROxpjYRxFZKjr4wNm-_O0pBTkjNYv1rgPUR87VcNswH_VYGpIrsyGdqnNa3vcVSH0f5Tg
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Optional
|
||||||
|
from jose import JWTError, jwt
|
||||||
|
from passlib.context import CryptContext
|
||||||
|
from fastapi import Depends, HTTPException, status
|
||||||
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
from config.settings import settings
|
||||||
|
from models.schemas import TokenData, UserCreate, UserResponse
|
||||||
|
from config.database import get_db
|
||||||
|
from models.db import users_table
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession # Import ajouté ici
|
||||||
|
|
||||||
|
# Configuration pour le hachage des mots de passe
|
||||||
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||||
|
|
||||||
|
# Configuration pour OAuth2
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||||
|
|
||||||
|
class AuthService:
|
||||||
|
@staticmethod
|
||||||
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||||
|
return pwd_context.verify(plain_password, hashed_password)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_password_hash(password: str) -> str:
|
||||||
|
return pwd_context.hash(password)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def authenticate_user(email: str, password: str, db):
|
||||||
|
query = select(users_table).where(users_table.c.email == email)
|
||||||
|
result = await db.execute(query)
|
||||||
|
user = result.fetchone()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
return None
|
||||||
|
if not AuthService.verify_password(password, user["hashed_password"]):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
|
||||||
|
to_encode = data.copy()
|
||||||
|
if expires_delta:
|
||||||
|
expire = datetime.utcnow() + expires_delta
|
||||||
|
else:
|
||||||
|
expire = datetime.utcnow() + 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)):
|
||||||
|
credentials_exception = HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Could not validate credentials",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
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:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
query = select(users_table).where(users_table.c.email == token_data.email)
|
||||||
|
result = await db.execute(query)
|
||||||
|
user = result.fetchone()
|
||||||
|
if user is None:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
return UserResponse(**user)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def create_user(user: UserCreate, db):
|
||||||
|
query = select(users_table).where(users_table.c.email == user.email)
|
||||||
|
result = await db.execute(query)
|
||||||
|
existing_user = result.fetchone()
|
||||||
|
if existing_user:
|
||||||
|
raise HTTPException(status_code=400, detail="Email already registered")
|
||||||
|
|
||||||
|
hashed_password = AuthService.get_password_hash(user.password)
|
||||||
|
|
||||||
|
query = insert(users_table).values(
|
||||||
|
email=user.email,
|
||||||
|
full_name=user.full_name,
|
||||||
|
phone=user.phone,
|
||||||
|
date_of_birth=user.date_of_birth,
|
||||||
|
organization=user.organization,
|
||||||
|
hashed_password=hashed_password,
|
||||||
|
role="user"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
user_id = result.inserted_primary_key[0]
|
||||||
|
return await AuthService.get_current_user(db=db)
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not create user: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def admin_required(token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
|
||||||
|
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:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
query = users_table.select().where(users_table.c.email == token_data.email)
|
||||||
|
result = await db.execute(query)
|
||||||
|
user = result.fetchone()
|
||||||
|
if user is None:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
if user["role"] != "admin":
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_user(user_id: int, updates: dict, token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
|
||||||
|
"""
|
||||||
|
Met à jour les informations d'un utilisateur.
|
||||||
|
- Un utilisateur peut mettre à jour ses propres informations.
|
||||||
|
- Un administrateur peut mettre à jour les informations de n'importe quel utilisateur.
|
||||||
|
"""
|
||||||
|
# Décodage et vérification des permissions
|
||||||
|
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:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
query = select(users_table).where(users_table.c.email == token_data.email)
|
||||||
|
result = await db.execute(query)
|
||||||
|
current_user = result.fetchone()
|
||||||
|
if current_user is None:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
# Vérifier si l'utilisateur est l'admin ou lui-même
|
||||||
|
if current_user["id"] != user_id and current_user["role"] != "admin":
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
# Mise à jour des champs autorisés
|
||||||
|
allowed_updates = {"full_name", "phone", "date_of_birth", "organization", "email"}
|
||||||
|
updates = {key: value for key, value in updates.items() if key in allowed_updates}
|
||||||
|
|
||||||
|
if "email" in updates: # Vérifie si l'email existe déjà
|
||||||
|
existing_email_query = select(users_table).where(users_table.c.email == updates["email"])
|
||||||
|
result = await db.execute(existing_email_query)
|
||||||
|
existing_user = result.fetchone()
|
||||||
|
if existing_user and existing_user["id"] != user_id:
|
||||||
|
raise HTTPException(status_code=400, detail="Email already registered by another user.")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
update(users_table)
|
||||||
|
.where(users_table.c.id == user_id)
|
||||||
|
.values(**updates)
|
||||||
|
.execution_options(synchronize_session="fetch")
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not update user: {str(e)}")
|
||||||
|
|
||||||
|
# Récupérer les informations mises à jour
|
||||||
|
updated_user_query = select(users_table).where(users_table.c.id == user_id)
|
||||||
|
result = await db.execute(updated_user_query)
|
||||||
|
updated_user = result.fetchone()
|
||||||
|
|
||||||
|
return UserResponse(**updated_user)
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
from celery import Celery
|
||||||
|
from config.settings import settings
|
||||||
|
from config.database import get_db
|
||||||
|
from models.db import users_table # Assure-toi d'avoir une table `users_table` définie dans `db.py`
|
||||||
|
|
||||||
|
celery_app = Celery("tasks", broker=settings.celery_broker_url)
|
||||||
|
|
||||||
|
@celery_app.task
|
||||||
|
async def delete_user_data(user_id: int):
|
||||||
|
async with get_db() as db:
|
||||||
|
try:
|
||||||
|
await db.execute(
|
||||||
|
users_table.update()
|
||||||
|
.where(users_table.c.id == user_id)
|
||||||
|
.values(
|
||||||
|
full_name="xxxxx",
|
||||||
|
email="xxxxx",
|
||||||
|
phone="xxxxx",
|
||||||
|
is_deleted=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise Exception(f"Could not delete user data: {str(e)}")
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
from sqlalchemy import insert
|
||||||
|
from models.schemas import TechnicalIssue
|
||||||
|
from config.database import get_db
|
||||||
|
from models.db import technical_issues_table # Assure-toi d'avoir une table `technical_issues_table` définie dans `db.py`
|
||||||
|
|
||||||
|
class MessageService:
|
||||||
|
@staticmethod
|
||||||
|
async def create_issue(issue: TechnicalIssue, db):
|
||||||
|
query = insert(technical_issues_table).values(
|
||||||
|
user_id=issue.user_id,
|
||||||
|
description=issue.description,
|
||||||
|
status="open" # Par défaut, le statut est "open"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
issue_id = result.inserted_primary_key[0]
|
||||||
|
return {"id": issue_id, **issue.dict()}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not create technical issue: {str(e)}")
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
from sqlalchemy import insert, select, update
|
||||||
|
from models.schemas import NeedRequestCreate
|
||||||
|
from config.database import get_db
|
||||||
|
from models.db import need_requests_table, users_table
|
||||||
|
from models.schemas import TokenData
|
||||||
|
from fastapi import HTTPException, Depends, status
|
||||||
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
from config.settings import settings
|
||||||
|
from jose import jwt, JWTError
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||||
|
|
||||||
|
|
||||||
|
class NeedRequestService:
|
||||||
|
@staticmethod
|
||||||
|
async def create_need(need: NeedRequestCreate, db):
|
||||||
|
query = insert(need_requests_table).values(
|
||||||
|
category=need.category,
|
||||||
|
description=need.description,
|
||||||
|
adults=need.adults,
|
||||||
|
children=need.children,
|
||||||
|
vulnerable=need.vulnerable,
|
||||||
|
location=need.location,
|
||||||
|
gps_coordinates=need.gps_coordinates,
|
||||||
|
requester_email=need.requester_email
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
need_id = result.inserted_primary_key[0]
|
||||||
|
return {"id": need_id, **need.dict()}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not create need request: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_need(need_id: int, db):
|
||||||
|
query = select(need_requests_table).where(need_requests_table.c.id == need_id)
|
||||||
|
result = await db.execute(query)
|
||||||
|
need = result.fetchone()
|
||||||
|
if need is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Need request not found")
|
||||||
|
return dict(need)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_all_needs(db):
|
||||||
|
query = select(need_requests_table).where(need_requests_table.c.deleted == None)
|
||||||
|
result = await db.execute(query)
|
||||||
|
needs = result.fetchall()
|
||||||
|
return [dict(need) for need in needs]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_need(need_id: int, data: dict, db, token: str):
|
||||||
|
user = await NeedRequestService.verify_requester_or_admin(need_id, token, db)
|
||||||
|
if "deleted" in data: # Empêche la mise à jour directe du champ `deleted`
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid update field")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
update(need_requests_table)
|
||||||
|
.where(need_requests_table.c.id == need_id)
|
||||||
|
.values(**data)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Need request not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Need request updated successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not update need request: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def delete_need(need_id: int, db, token: str):
|
||||||
|
user = await NeedRequestService.verify_requester_or_admin(need_id, token, db)
|
||||||
|
|
||||||
|
query = (
|
||||||
|
update(need_requests_table)
|
||||||
|
.where(need_requests_table.c.id == need_id)
|
||||||
|
.values(deleted=datetime.utcnow())
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Need request not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Need request deleted successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not delete need request: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def verify_requester_or_admin(need_id: int, token: str, db):
|
||||||
|
credentials_exception = HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="You do not have permission to perform this action.",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
# Décodage du token JWT
|
||||||
|
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:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
# Récupère l'utilisateur depuis la base de données
|
||||||
|
user_query = select(users_table).where(users_table.c.email == token_data.email)
|
||||||
|
result = await db.execute(user_query)
|
||||||
|
user = result.fetchone()
|
||||||
|
if user is None:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
# Récupère la demande de besoin
|
||||||
|
need_query = select(need_requests_table).where(need_requests_table.c.id == need_id)
|
||||||
|
result = await db.execute(need_query)
|
||||||
|
need = result.fetchone()
|
||||||
|
if need is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Need request not found")
|
||||||
|
|
||||||
|
# Vérifie si l'utilisateur est l'auteur ou un administrateur
|
||||||
|
if need["requester_email"] != user["email"] and user["role"] != "admin":
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
from sqlalchemy import select, update
|
||||||
|
from fastapi import HTTPException
|
||||||
|
from models.schemas import PersonReportCreate, PersonReportUpdate, PersonReportResponse
|
||||||
|
from config.database import get_db
|
||||||
|
from models.db import person_reports_table
|
||||||
|
from typing import Optional
|
||||||
|
from fastapi import Depends
|
||||||
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||||
|
|
||||||
|
|
||||||
|
class PersonReportService:
|
||||||
|
@staticmethod
|
||||||
|
async def create_report(report: PersonReportCreate, db):
|
||||||
|
query = person_reports_table.insert().values(**report.dict())
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
report_id = result.inserted_primary_key[0]
|
||||||
|
return await PersonReportService.get_report(report_id, db)
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not create report: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_report(report_id: int, report: PersonReportUpdate, db):
|
||||||
|
query = (
|
||||||
|
person_reports_table.update()
|
||||||
|
.where(person_reports_table.c.id == report_id)
|
||||||
|
.values(**report.dict(exclude_unset=True))
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
return await PersonReportService.get_report(report_id, db)
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not update report: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_report(report_id: int, db):
|
||||||
|
query = select(person_reports_table).where(person_reports_table.c.id == report_id)
|
||||||
|
result = await db.execute(query)
|
||||||
|
report = result.fetchone()
|
||||||
|
if not report:
|
||||||
|
raise HTTPException(status_code=404, detail="Report not found")
|
||||||
|
return PersonReportResponse(**report)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def list_reports(status: Optional[str] = None, db=Depends(get_db)):
|
||||||
|
query = select(person_reports_table)
|
||||||
|
if status:
|
||||||
|
query = query.where(person_reports_table.c.status == status)
|
||||||
|
result = await db.execute(query)
|
||||||
|
reports = result.fetchall()
|
||||||
|
return [PersonReportResponse(**report) for report in reports]
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
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 sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
|
||||||
|
class PointsOfInterestService:
|
||||||
|
@staticmethod
|
||||||
|
async def create_point_of_interest(point: PointOfInterest, db: AsyncSession):
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
point_id = result.inserted_primary_key[0]
|
||||||
|
return {"id": point_id, **point.dict()}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
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):
|
||||||
|
query = select(points_of_interest_table).where(points_of_interest_table.c.id == point_id)
|
||||||
|
result = await db.execute(query)
|
||||||
|
point = result.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):
|
||||||
|
query = select(points_of_interest_table)
|
||||||
|
result = await db.execute(query)
|
||||||
|
return [dict(row) for row in result.fetchall()]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_point_of_interest(point_id: int, data: dict, db: AsyncSession):
|
||||||
|
query = (
|
||||||
|
update(points_of_interest_table)
|
||||||
|
.where(points_of_interest_table.c.id == point_id)
|
||||||
|
.values(**data)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Point of interest not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Point of interest updated successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
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):
|
||||||
|
query = delete(points_of_interest_table).where(points_of_interest_table.c.id == point_id)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Point of interest not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Point of interest deleted successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not delete point of interest: {str(e)}")
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from sqlalchemy import insert, update, select, delete
from models.schemas import UserReport, UserReportUpdate
from config.database import get_db
from models.db import user_reports_table
from fastapi import HTTPException, status
class ReportService:
@staticmethod
async def create_report(report: UserReport, db):
query = insert(user_reports_table).values(
reporter_id=report.reporter_id,
reported_user_id=report.reported_user_id,
reason=report.reason,
status="pending" # Par défaut, le statut est "pending"
)
try:
result = await db.execute(query)
await db.commit()
report_id = result.inserted_primary_key[0]
return {"id": report_id, **report.dict()}
except Exception as e:
await db.rollback()
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):
query = select(user_reports_table).where(user_reports_table.c.id == report_id)
result = await db.execute(query)
report = result.fetchone()
if not report:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found")
return dict(report)
@staticmethod
async def get_all_reports(db):
query = select(user_reports_table)
result = await db.execute(query)
reports = result.fetchall()
return [dict(report) for report in reports]
@staticmethod
async def update_report(report_id: int, report_update: UserReportUpdate, db):
query = (
update(user_reports_table)
.where(user_reports_table.c.id == report_id)
.values(**report_update.dict(exclude_unset=True))
.returning(user_reports_table.c.id)
)
try:
result = await db.execute(query)
await db.commit()
updated_id = result.fetchone()
if not updated_id:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found")
return {"id": updated_id[0], **report_update.dict()}
except Exception as e:
await db.rollback()
raise HTTPException(status_code=500, detail=f"Could not update report: {str(e)}")
@staticmethod
async def delete_report(report_id: int, db):
query = delete(user_reports_table).where(user_reports_table.c.id == report_id)
try:
result = await db.execute(query)
await db.commit()
if result.rowcount == 0:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Report not found")
return {"detail": "Report deleted successfully"}
except Exception as e:
await db.rollback()
raise HTTPException(status_code=500, detail=f"Could not delete report: {str(e)}")
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
from sqlalchemy import insert, select, update, delete
|
||||||
|
from fastapi import HTTPException, Depends, status
|
||||||
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
from models.schemas import Role, TokenData
|
||||||
|
from models.db import roles_table, users_table
|
||||||
|
from config.database import get_db
|
||||||
|
from config.settings import settings
|
||||||
|
from jose import jwt, JWTError
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||||
|
|
||||||
|
|
||||||
|
class RoleService:
|
||||||
|
@staticmethod
|
||||||
|
async def create_role(role: Role, db: AsyncSession, token: str):
|
||||||
|
"""
|
||||||
|
Crée un nouveau rôle (réservé aux administrateurs).
|
||||||
|
"""
|
||||||
|
await RoleService.admin_required(token, db)
|
||||||
|
query = insert(roles_table).values(
|
||||||
|
name=role.name,
|
||||||
|
permissions=",".join(role.permissions), # Stocke les permissions sous forme de chaîne
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
role_id = result.inserted_primary_key[0]
|
||||||
|
return {"id": role_id, **role.dict()}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not create role: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_role(role_id: int, data: dict, db: AsyncSession, token: str):
|
||||||
|
"""
|
||||||
|
Met à jour un rôle par son ID (réservé aux administrateurs).
|
||||||
|
"""
|
||||||
|
await RoleService.admin_required(token, db)
|
||||||
|
if "permissions" in data:
|
||||||
|
data["permissions"] = ",".join(data["permissions"]) # Convertit les permissions en chaîne
|
||||||
|
query = (
|
||||||
|
update(roles_table)
|
||||||
|
.where(roles_table.c.id == role_id)
|
||||||
|
.values(**data)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Role not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Role updated successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not update role: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def delete_role(role_id: int, db: AsyncSession, token: str):
|
||||||
|
"""
|
||||||
|
Supprime un rôle par son ID (réservé aux administrateurs).
|
||||||
|
"""
|
||||||
|
await RoleService.admin_required(token, db)
|
||||||
|
query = delete(roles_table).where(roles_table.c.id == role_id)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Role not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Role deleted successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not delete role: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def admin_required(token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
|
||||||
|
"""
|
||||||
|
Vérifie si l'utilisateur actuel est un administrateur.
|
||||||
|
"""
|
||||||
|
credentials_exception = HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="You do not have permission to perform this action.",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
# Décodage du token JWT
|
||||||
|
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:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
# Récupère l'utilisateur depuis la base de données
|
||||||
|
user_query = select(users_table).where(users_table.c.email == token_data.email)
|
||||||
|
result = await db.execute(user_query)
|
||||||
|
user = result.fetchone()
|
||||||
|
if user is None:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
# Vérifie si l'utilisateur a le rôle d'administrateur
|
||||||
|
if user["role"] != "admin":
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
from fastapi import UploadFile, HTTPException
|
||||||
|
import boto3
|
||||||
|
from botocore.exceptions import NoCredentialsError
|
||||||
|
from config.settings import settings
|
||||||
|
|
||||||
|
s3_client = boto3.client(
|
||||||
|
's3',
|
||||||
|
aws_access_key_id=settings.aws_access_key_id,
|
||||||
|
aws_secret_access_key=settings.aws_secret_access_key
|
||||||
|
)
|
||||||
|
|
||||||
|
class UploadService:
|
||||||
|
@staticmethod
|
||||||
|
async def upload_file(file: UploadFile):
|
||||||
|
try:
|
||||||
|
s3_client.upload_fileobj(file.file, settings.aws_bucket_name, file.filename)
|
||||||
|
file_url = f"https://{settings.aws_bucket_name}.s3.amazonaws.com/{file.filename}"
|
||||||
|
return {"file_url": file_url}
|
||||||
|
except NoCredentialsError:
|
||||||
|
raise HTTPException(status_code=500, detail="AWS credentials not available")
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not upload file: {str(e)}")
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
from sqlalchemy import insert, select, update, delete
|
||||||
|
from fastapi import HTTPException
|
||||||
|
from models.db import shelters_table
|
||||||
|
from models.schemas import Shelter
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
|
||||||
|
class ShelterService:
|
||||||
|
@staticmethod
|
||||||
|
async def create_shelter(shelter: Shelter, db: AsyncSession):
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
shelter_id = result.inserted_primary_key[0]
|
||||||
|
return {"id": shelter_id, **shelter.dict()}
|
||||||
|
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):
|
||||||
|
query = select(shelters_table).where(shelters_table.c.id == shelter_id)
|
||||||
|
result = await db.execute(query)
|
||||||
|
shelter = result.fetchone()
|
||||||
|
if not shelter:
|
||||||
|
raise HTTPException(status_code=404, detail="Shelter not found")
|
||||||
|
return dict(shelter)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_all_shelters(db: AsyncSession):
|
||||||
|
query = select(shelters_table)
|
||||||
|
result = await db.execute(query)
|
||||||
|
return [dict(row) for row in result.fetchall()]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_shelter(shelter_id: int, data: dict, db: AsyncSession):
|
||||||
|
query = (
|
||||||
|
update(shelters_table)
|
||||||
|
.where(shelters_table.c.id == shelter_id)
|
||||||
|
.values(**data)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Shelter not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Shelter updated successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not update shelter: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def delete_shelter(shelter_id: int, db: AsyncSession):
|
||||||
|
query = delete(shelters_table).where(shelters_table.c.id == shelter_id)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Shelter not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Shelter deleted successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not delete shelter: {str(e)}")
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
from sqlalchemy import insert, select, update, delete
|
||||||
|
from fastapi import HTTPException
|
||||||
|
from models.db import technical_issues_table
|
||||||
|
from models.schemas import TechnicalIssue, UpdateTechnicalIssue
|
||||||
|
|
||||||
|
|
||||||
|
class TechnicalIssueService:
|
||||||
|
@staticmethod
|
||||||
|
async def create_issue(issue: TechnicalIssue, db):
|
||||||
|
query = insert(technical_issues_table).values(
|
||||||
|
user_id=issue.user_id,
|
||||||
|
description=issue.description,
|
||||||
|
status=issue.status
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
issue_id = result.inserted_primary_key[0]
|
||||||
|
return {"id": issue_id, **issue.dict()}
|
||||||
|
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):
|
||||||
|
query = select(technical_issues_table).where(technical_issues_table.c.id == issue_id)
|
||||||
|
result = await db.execute(query)
|
||||||
|
issue = result.fetchone()
|
||||||
|
if not issue:
|
||||||
|
raise HTTPException(status_code=404, detail="Technical issue not found")
|
||||||
|
return dict(issue)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_all_issues(db):
|
||||||
|
query = select(technical_issues_table)
|
||||||
|
result = await db.execute(query)
|
||||||
|
return [dict(row) for row in result.fetchall()]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_issue(issue_id: int, issue_data: UpdateTechnicalIssue, db):
|
||||||
|
query = (
|
||||||
|
update(technical_issues_table)
|
||||||
|
.where(technical_issues_table.c.id == issue_id)
|
||||||
|
.values(**issue_data.dict(exclude_unset=True))
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Technical issue not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Technical issue updated successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not update issue: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def delete_issue(issue_id: int, db):
|
||||||
|
query = delete(technical_issues_table).where(technical_issues_table.c.id == issue_id)
|
||||||
|
try:
|
||||||
|
result = await db.execute(query)
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Technical issue not found")
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "Technical issue deleted successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Could not delete issue: {str(e)}")
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# app/services/upload_service.py
|
||||||
|
import boto3
|
||||||
|
from botocore.exceptions import NoCredentialsError
|
||||||
|
|
||||||
|
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):
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
from sqlalchemy import update, select
|
||||||
|
from fastapi import Depends, HTTPException, status
|
||||||
|
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 utils.security import get_password_hash
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class UserService:
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def list_users(status: Optional[str] = None, db=Depends(get_db)):
|
||||||
|
query = select(person_reports_table)
|
||||||
|
if status:
|
||||||
|
query = query.where(users_table.c.status == status)
|
||||||
|
result = await db.execute(query)
|
||||||
|
users = result.fetchall()
|
||||||
|
return [UserResponse(**user) for user in users]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def create_user(user: UserCreate, db: AsyncSession):
|
||||||
|
hashed_password = get_password_hash(user.password)
|
||||||
|
query = users_table.insert().values(
|
||||||
|
email=user.email,
|
||||||
|
full_name=user.full_name,
|
||||||
|
phone=user.phone,
|
||||||
|
date_of_birth=user.date_of_birth,
|
||||||
|
organization=user.organization,
|
||||||
|
hashed_password=hashed_password,
|
||||||
|
role=user.role, # Par défaut, rôle "user"
|
||||||
|
is_active=True,
|
||||||
|
is_banned=False
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
return {"message": "User created successfully"}
|
||||||
|
except Exception as e:
|
||||||
|
await db.rollback()
|
||||||
|
raise HTTPException(status_code=500, detail=f"Error creating user: {str(e)}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def change_user_role(user_update: UserUpdateRole, db: AsyncSession):
|
||||||
|
query = (
|
||||||
|
update(users_table)
|
||||||
|
.where(users_table.c.email == user_update.email)
|
||||||
|
.values(role=user_update.new_role)
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
return {"message": f"Role updated to {user_update.new_role}"}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def block_user(user_action: UserBlockBan, db: AsyncSession):
|
||||||
|
query = (
|
||||||
|
update(users_table)
|
||||||
|
.where(users_table.c.email == user_action.email)
|
||||||
|
.values(is_active=False)
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
return {"message": f"User {user_action.email} blocked"}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def ban_user(user_action: UserBlockBan, db: AsyncSession):
|
||||||
|
query = (
|
||||||
|
update(users_table)
|
||||||
|
.where(users_table.c.email == user_action.email)
|
||||||
|
.values(is_banned=True)
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
return {"message": f"User {user_action.email} banned"}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def unblock_user(user_action: UserBlockBan, db: AsyncSession):
|
||||||
|
query = (
|
||||||
|
update(users_table)
|
||||||
|
.where(users_table.c.email == user_action.email)
|
||||||
|
.values(is_active=True)
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
return {"message": f"User {user_action.email} unblocked"}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def unban_user(user_action: UserBlockBan, db: AsyncSession):
|
||||||
|
query = (
|
||||||
|
update(users_table)
|
||||||
|
.where(users_table.c.email == user_action.email)
|
||||||
|
.values(is_banned=False)
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
await db.commit()
|
||||||
|
if result.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
return {"message": f"User {user_action.email} unbanned"}
|
||||||
BIN
tmp/restart.txt
BIN
tmp/restart.txt
Binary file not shown.
BIN
utils/helpers.py
BIN
utils/helpers.py
Binary file not shown.
BIN
utils/logging.py
BIN
utils/logging.py
Binary file not shown.
Loading…
Reference in New Issue