user reaearch and fix function delete

This commit is contained in:
nBiqoz
2025-10-20 17:17:03 +02:00
parent e0232b1fcb
commit 0efe96f4e2
2 changed files with 189 additions and 90 deletions

View File

@@ -1,12 +1,35 @@
import { NextRequest, NextResponse } from 'next/server';
import { getDatabase } from '@/lib/db/mongodb';
import { NextRequest, NextResponse } from "next/server";
import { getDatabase } from "@/lib/db/mongodb";
import { ObjectId } from "mongodb";
const ALLOWED_COLLECTIONS = [
'accessroles', 'aclentries', 'actions', 'agentcategories', 'agents',
'assistants', 'balances', 'banners', 'conversations', 'conversationtags',
'files', 'groups', 'keys', 'memoryentries', 'messages', 'pluginauths',
'presets', 'projects', 'promptgroups', 'prompts', 'roles', 'sessions',
'sharedlinks', 'tokens', 'toolcalls', 'transactions', 'users'
"accessroles",
"aclentries",
"actions",
"agentcategories",
"agents",
"assistants",
"balances",
"banners",
"conversations",
"conversationtags",
"files",
"groups",
"keys",
"memoryentries",
"messages",
"pluginauths",
"presets",
"projects",
"promptgroups",
"prompts",
"roles",
"sessions",
"sharedlinks",
"tokens",
"toolcalls",
"transactions",
"users",
];
export async function GET(
@@ -16,30 +39,51 @@ export async function GET(
const { collection } = await params;
try {
if (!ALLOWED_COLLECTIONS.includes(collection)) {
return NextResponse.json(
{ error: 'Collection non autorisée' },
{ error: "Collection non autorisée" },
{ status: 400 }
);
}
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '20');
const filter = JSON.parse(searchParams.get('filter') || '{}');
const page = parseInt(searchParams.get("page") || "1");
const limit = parseInt(searchParams.get("limit") || "20");
const filter = JSON.parse(searchParams.get("filter") || "{}");
// Gestion spéciale pour la collection users avec recherche par email ou id
if (collection === "users") {
const email = searchParams.get("email");
const id = searchParams.get("id");
if (email) {
filter.email = email.toLowerCase();
} else if (id) {
// Vérifier si l'ID est un ObjectId valide
if (ObjectId.isValid(id)) {
filter._id = new ObjectId(id);
} else {
// Si l'ID n'est pas valide, retourner une erreur
return NextResponse.json(
{ error: "ID utilisateur invalide" },
{ status: 400 }
);
}
}
}
const db = await getDatabase();
const skip = (page - 1) * limit;
const [data, total] = await Promise.all([
db.collection(collection)
db
.collection(collection)
.find(filter)
.skip(skip)
.limit(limit)
.sort({ createdAt: -1 })
.toArray(),
db.collection(collection).countDocuments(filter)
db.collection(collection).countDocuments(filter),
]);
return NextResponse.json({
@@ -47,13 +91,10 @@ export async function GET(
total,
page,
limit,
totalPages: Math.ceil(total / limit)
totalPages: Math.ceil(total / limit),
});
} catch (error) {
console.error(`Erreur lors de la récupération de ${collection}:`, error);
return NextResponse.json(
{ error: 'Erreur serveur' },
{ status: 500 }
);
return NextResponse.json({ error: "Erreur serveur" }, { status: 500 });
}
}

View File

@@ -13,13 +13,14 @@ import {
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { Input } from "@/components/ui/input";
import { ChevronLeft, ChevronRight, Search } from "lucide-react";
import { formatDate } from "@/lib/utils";
import { LibreChatUser, LibreChatBalance } from "@/lib/types";
export function UsersTable() {
const [page, setPage] = useState(1);
const [searchTerm, setSearchTerm] = useState("");
const limit = 20;
// Charger les utilisateurs
@@ -46,6 +47,20 @@ export function UsersTable() {
return map;
}, [balances]);
// Filtrer les utilisateurs selon le terme de recherche
const filteredUsers = useMemo(() => {
if (!searchTerm.trim()) {
return users;
}
const searchLower = searchTerm.toLowerCase();
return users.filter(
(user) =>
user.name?.toLowerCase().includes(searchLower) ||
user.email?.toLowerCase().includes(searchLower)
);
}, [users, searchTerm]);
const totalPages = Math.ceil(total / limit);
const handlePrevPage = () => {
@@ -76,7 +91,20 @@ export function UsersTable() {
return (
<Card>
<CardHeader>
<CardTitle>Liste des utilisateurs ({total})</CardTitle>
<div className="flex flex-col space-y-4 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
<CardTitle>
Liste des utilisateurs ({searchTerm ? filteredUsers.length : total})
</CardTitle>
<div className="relative w-full sm:w-80">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="Rechercher par nom ou email..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
/>
</div>
</div>
</CardHeader>
<CardContent>
<div className="rounded-md border">
@@ -93,9 +121,11 @@ export function UsersTable() {
</TableRow>
</TableHeader>
<TableBody>
{users.map((user) => {
{filteredUsers.length > 0 ? (
filteredUsers.map((user) => {
const userCredits = creditsMap.get(user._id) || 0;
const isActive = new Date(user.updatedAt || user.createdAt) >
const isActive =
new Date(user.updatedAt || user.createdAt) >
new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); // 30 jours en millisecondes
return (
@@ -112,7 +142,11 @@ export function UsersTable() {
<span className="text-sm">{user.email}</span>
</TableCell>
<TableCell>
<Badge variant={user.role === 'ADMIN' ? 'default' : 'secondary'}>
<Badge
variant={
user.role === "ADMIN" ? "default" : "secondary"
}
>
{user.role}
</Badge>
</TableCell>
@@ -122,9 +156,11 @@ export function UsersTable() {
</span>
</TableCell>
<TableCell>
<Badge variant={isActive ? 'default' : 'secondary'}>
{isActive ? 'Actif' : 'Inactif'}
<span className="text-sm text-muted-foreground">
<Badge variant={isActive ? "default" : "secondary"}>
{isActive ? "Actif" : "Inactif"}
</Badge>
</span>
</TableCell>
<TableCell>
<span className="text-sm text-muted-foreground">
@@ -133,12 +169,25 @@ export function UsersTable() {
</TableCell>
</TableRow>
);
})}
})
) : (
<TableRow>
<TableCell
colSpan={7}
className="text-center py-8 text-muted-foreground"
>
{searchTerm
? `Aucun utilisateur trouvé pour "${searchTerm}"`
: "Aucun utilisateur trouvé"}
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{/* Pagination */}
{/* Pagination - masquée lors de la recherche */}
{!searchTerm && (
<div className="flex items-center justify-between space-x-2 py-4">
<div className="text-sm text-muted-foreground">
Page {page} sur {totalPages} ({total} éléments au total)
@@ -164,6 +213,15 @@ export function UsersTable() {
</Button>
</div>
</div>
)}
{/* Info de recherche */}
{searchTerm && (
<div className="py-4 text-sm text-muted-foreground">
{filteredUsers.length} résultat(s) trouvé(s) pour &quot;{searchTerm}
&quot;
</div>
)}
</CardContent>
</Card>
);