This commit is contained in:
Louis Mazin 2026-09-15 01:33:45 +02:00
parent f69de9a8df
commit 27c1d69ae8
6 changed files with 49 additions and 49 deletions

View File

@ -1,32 +0,0 @@
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from decouple import config
from django.db import transaction
class Command(BaseCommand):
help = 'Check if any users exist and create admin if none exist'
def handle(self, *args, **options):
# Check if any users exist
if User.objects.exists():
self.stdout.write(
self.style.WARNING('Users already exist, skipping admin creation')
)
return
# Get credentials from environment variables or use defaults
username = config('ADMIN_USERNAME', default='admin')
password = config('ADMIN_PASSWORD', default='admin123')
email = config('ADMIN_EMAIL', default='admin@boulangerie.fr')
# Create the admin user
with transaction.atomic():
user = User.objects.create_superuser(
username=username,
email=email,
password=password
)
self.stdout.write(
self.style.SUCCESS(f'Successfully created admin user "{username}"')
)

View File

@ -5,8 +5,8 @@ urlpatterns = [
path('register/', RegisterView.as_view(), name='register'),
path('me/', UserMeView.as_view(), name='me'),
path('', UserListView.as_view(), name='user-list'),
path('<int:user_id>/', UserDetailView.as_view(), name='user-detail'),
path('<int:user_id>/promote/', UserPromoteView.as_view(), name='user-promote'),
path('<int:user_id>/delete/', UserDeleteView.as_view(), name='user-delete'),
path('<int:user>/', UserDetailView.as_view(), name='user-detail'),
path('<int:user>/promote/', UserPromoteView.as_view(), name='user-promote'),
path('<int:user>/delete/', UserDeleteView.as_view(), name='user-delete'),
path('invitations/', InvitationCreateView.as_view(), name='invitation-create'),
]

View File

@ -35,6 +35,8 @@ class UserListView(generics.ListAPIView):
class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = (IsAuthenticated,)
serializer_class = UserSerializer
lookup_field = 'id'
lookup_url_kwarg = 'user'
def get_queryset(self):
if not (self.request.user.is_staff or self.request.user.is_superuser):
@ -61,14 +63,14 @@ class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
class UserPromoteView(APIView):
permission_classes = (IsAuthenticated,)
def post(self, request, user_id):
def post(self, request, user):
if not (request.user.is_staff or request.user.is_superuser):
raise PermissionDenied('Seuls les administrateurs peuvent promouvoir des utilisateurs.')
try:
user = User.objects.get(id=user_id)
user.is_staff = True
user.save()
user_obj = User.objects.get(id=user)
user_obj.is_staff = True
user_obj.save()
return Response({'message': 'Utilisateur promu avec succès'}, status=200)
except User.DoesNotExist:
return Response({'error': 'Utilisateur non trouvé'}, status=404)
@ -77,17 +79,17 @@ class UserPromoteView(APIView):
class UserDeleteView(APIView):
permission_classes = (IsAuthenticated,)
def delete(self, request, user_id):
def delete(self, request, user):
if not (request.user.is_staff or request.user.is_superuser):
raise PermissionDenied('Seuls les administrateurs peuvent supprimer des utilisateurs.')
try:
user = User.objects.get(id=user_id)
user_obj = User.objects.get(id=user)
# Ne pas permettre de supprimer l'utilisateur courant
if user == request.user:
if user_obj == request.user:
return Response({'error': 'Vous ne pouvez pas supprimer votre propre compte'}, status=400)
user.delete()
user_obj.delete()
return Response({'message': 'Utilisateur supprimé avec succès'}, status=200)
except User.DoesNotExist:
return Response({'error': 'Utilisateur non trouvé'}, status=404)

View File

@ -1,7 +1,7 @@
import { Routes, Route, Navigate } from 'react-router-dom';
import LoginPage from './pages/LoginPage';
import HomePage from './pages/HomePage';
import ProtectedRoute from './components/ProtectedRoute';
import AdminRoute from './components/AdminRoute';
import Header from './components/Header';
import AdminUsersPage from './pages/AdminUsersPage';
import AdminInvitePage from './pages/AdminInvitePage';
@ -31,14 +31,14 @@ function App() {
}
/>
<Route path="/users" element={
<ProtectedRoute>
<AdminRoute>
<AdminUsersPage />
</ProtectedRoute>
</AdminRoute>
} />
<Route path="/invite" element={
<ProtectedRoute>
<AdminRoute>
<AdminInvitePage />
</ProtectedRoute>
</AdminRoute>
} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>

View File

@ -0,0 +1,26 @@
import React from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
const AdminRoute = ({ children }) => {
const { user, loading, isAdmin } = useAuth();
const location = useLocation();
if (loading) {
return <div>Loading...</div>;
}
if (!user) {
// Redirect them to the /login page, but save the current location they were trying to go to.
return <Navigate to="/login" state={{ from: location }} replace />;
}
// If user is not admin, redirect to profile page
if (!isAdmin) {
return <Navigate to="/profile" state={{ from: location }} replace />;
}
return children;
};
export default AdminRoute;

View File

@ -41,10 +41,14 @@ const AdminUsersPage = () => {
};
const handleMakeAdmin = async (userId) => {
if (!window.confirm('Êtes-vous sûr de vouloir promouvoir cet utilisateur ?')) {
return;
}
try {
await userApi.promoteUser(userId);
setUsers(users.map(user =>
user.id === userId ? { ...user, is_staff: true } : user
user.id === userId ? { ...user, is_admin: true } : user
));
} catch (err) {
setError('Erreur lors de la promotion de l\'utilisateur');