First commit from visualcode

main
Anaz 2025-01-09 12:47:47 +04:00
parent 25524998c1
commit b3ef8d4a19
35 changed files with 1630 additions and 0 deletions

27
api/v1/auth.py Normal file
View File

@ -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

10
api/v1/messages.py Normal file
View File

@ -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)

65
api/v1/need_requests.py Normal file
View File

@ -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)

23
api/v1/person_reports.py Normal file
View File

@ -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)

View File

@ -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)

1
api/v1/reports.py Normal file
View File

@ -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)

33
api/v1/roles.py Normal file
View File

@ -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)

31
api/v1/shelters.py Normal file
View File

@ -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)

View File

@ -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)

28
api/v1/uploads.py Normal file
View File

@ -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))

33
api/v1/users.py Normal file
View File

@ -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)

138
config/database.py Normal file
View File

@ -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

24
config/settings.py Normal file
View File

@ -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()

139
models/db.py Normal file
View File

@ -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

133
models/schemas.py Normal file
View File

@ -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

5
old.env Normal file
View File

@ -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

193
services/auth_service.py Normal file
View File

@ -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)

View File

@ -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)}")

View File

@ -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)}")

View File

@ -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

View File

@ -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]

View File

@ -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)}")

View File

@ -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)}")

104
services/role_service.py Normal file
View File

@ -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

22
services/s3_service.py Normal file
View File

@ -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)}")

View File

@ -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)}")

View File

@ -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)}")

View File

@ -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

107
services/user_service.py Normal file
View File

@ -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"}

View File

0
tests/unit/test_auth.py Normal file
View File

0
tests/unit/test_user.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.