Profilképek Kezelése és Tárolása
Áttekintés
A profilkép kezelő komponens lehetővé teszi a felhasználó számára, hogy személyre szabja a profiljának kinézetét. A funkció beépített korlátozással rendelkezik az erőforrások optimális felhasználása érdekében.
Főbb Jellemzők
- Felhasználónként egy profilkép tárolása
- 90 napos korlátozás a profilkép módosítására
- Automatikus optimalizálás
- Biztonságos tárolás az uploadthing nevezetű felhőalapú szolgáltatásban
Környezeti Változók
UPLOADTHING_TOKEN=""
Technikai Specifikáció
// Erre az útvonalra megy ki kérés, amikor az uploadthing-re sikeresen feltöltődik a fájl.
POST /api/user/profile-picture
- Követelmények:
- OK response az uploadthing-től
- userId és a feltöltött fájl url címe
- Válasz: {
message: string
}
// Az uploadthing által előre elkészített API route, amellyel feltölthető a kép a felhőjükbe.
POST /api/uploadthing
- Követelmények:
- Autentikált felhasználó
- Maximum fájlméret: 2MB
- Támogatott formátumok: .jpg, .jpeg, .png, .webp
- Válasz: {
uploadedBy: string,
fileUrl: string
}
Időzítési Logika
// Először lekérdezzük a user adatát
const [imageChangedAt] = await db
.select()
.from(UsersTable)
.where(eq(UsersTable.id, session.user.id))
.limit(1)
// Aztán pedig egy elágazásban megvizsgáljuk date-fns segítségével,
// hogy az `imageChangedAt` mezőben található (ha van) időpont óta eltelt e 90 nap.
if (
imageChangedAt.imageChangedAt &&
differenceInDays(new Date(), imageChangedAt.imageChangedAt) < 90
) {
return NextResponse.json({
error: `Legközelebb csak ${
90 - differenceInDays(new Date(), imageChangedAt.imageChangedAt)
} nap múlva módosíthatod a profilképet!`,
})
}
// Ha nem, akkor a function tovább fut.
Hibakezelés
const { fileUrl, uploadedBy } = await request.json()
// Ha nem található az uploadthing route által küldött fileUrl vagy uploadedBy,
// visszatérünk egy üzenettel
if (!fileUrl || !uploadedBy) {
return NextResponse.json(
{ error: "Hiba történt a profilkép változtatása közben!" },
{ status: 400 }
)
}
// Illetve, ha a Query nem küld vissza adatot ->
const [updateProfilePicture] = await db
.update(UsersTable)
.set({ image: fileUrl, imageChangedAt: new Date() })
.where(eq(UsersTable.id, session.user.id))
.returning({
id: UsersTable.id,
image: UsersTable.image,
})
// visszatérünk egy hiba üzenettel.
if (!updateProfilePicture) {
return NextResponse.json(
{ error: "Hiba történt a profilkép változtatása közben!" },
{ status: 400 }
)
}
onClientUploadComplete={async (res) => {
// Calling the API Route
const response = await fetch("/api/user/profile-picture", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
fileUrl: res[0].url,
uploadedBy: userId,
}),
})
// Ha az API válasz nem OK, visszatérünk egy hiba üzenettel
if (!response.ok) {
const error = await response.json()
throw new Error(
error.message || "Hiba történt a profilképék módosítása közben!"
)
}
// ----- A function további része ------
}}
Egy speciális uploadthing function-ben elkapjuk a hibát:
onUploadError={(error: Error) => {
// A hibaüzenet kijelzése egy toaster-rel.
toast({
title: "Hiba történt!",
description: error.message,
variant: "destructive",
duration: 3000,
})
}}
Használati Példa
// Kliens oldali implementáció (Next.js)
"use client"
import { useToast } from "@/hooks/use-toast"
import { UploadDropzone } from "@/lib/utils/uploadthing"
import { differenceInDays } from "date-fns"
import { Loader2Icon } from "lucide-react"
import { useRouter } from "next/navigation"
export default function ProfilePictureForm({
imageChangedAt,
userId,
}: {
imageChangedAt: Date | null
userId: string
}) {
const { toast } = useToast()
const router = useRouter()
return (
<section className="p-4 mt-6 border rounded-md border-muted">
<h4 className="text-sm font-medium">Profilkép</h4>
<p className="mt-1 mb-3 text-[.8rem] text-muted-foreground">
Itt tudsz újat beállítani:
</p>
<UploadDropzone
disabled={
imageChangedAt
? differenceInDays(new Date(), imageChangedAt) < 90
: false
}
content={{
button({ ready, isUploading }) {
if (ready) return <div>Kép feltöltése</div>
if (isUploading)
return (
<div className="flex items-center gap-2">
<Loader2Icon className="animate-spin" /> Feltöltés...
</div>
)
return "Betöltés..."
},
allowedContent({ ready, fileTypes, isUploading, files }) {
if (!ready) return "Engedélyezett fájltípusok keresése"
if (isUploading) return "Úgy tűnik valamit éppen feltöltesz"
return `Fájl, amit feltölthetsz: ${fileTypes.join(", ")}. Max 2MB`
},
label({ ready, isDragActive }) {
if (
imageChangedAt &&
differenceInDays(new Date(), imageChangedAt) < 90
)
return `Legközelebb csak ${(
90 - differenceInDays(new Date(), imageChangedAt)
).toString()} nap múlva módosíthatod a profilképedet!`
if (!ready) return "Adatok és egyebek betöltése"
if (isDragActive) return "Igen, ide húzd a képet!"
return "Kattints ide, vagy húzd ide a feltölteni kívánt képet"
},
}}
className={`ut-button:bg-emerald ut-button:text-primary-foreground ut-button:font-sans ${
imageChangedAt && differenceInDays(new Date(), imageChangedAt) < 90
? "ut-button:cursor-not-allowed ut-button:bg-emerald-hover ut-label:cursor-not-allowed pointer-events-none"
: ""
}`}
endpoint="imageUploader"
onClientUploadComplete={async (res) => {
// Calling the API Route
const response = await fetch("/api/user/profile-picture", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
fileUrl: res[0].url,
uploadedBy: userId,
}),
})
// Checking if the response is not OK
if (!response.ok) {
const error = await response.json()
throw new Error(
error.message || "Hiba történt a profilképék módosítása közben!"
)
}
const data = await response.json()
// Displaying the success message
toast({
title: "Sikeres profilkép módosítás!",
description: data.message,
duration: 3000,
className:
"bg-emerald-hover border-emerald text-primary-foreground font-sans",
})
setTimeout(() => {
router.refresh()
}, 3000)
}}
onUploadError={(error: Error) => {
// Displaying the error with a toast message
toast({
title: "Hiba történt!",
description: error.message,
variant: "destructive",
duration: 3000,
})
}}
/>
</section>
)
}
Továbbfejlesztési Lehetőségek
- Képszerkesztő funkció hozzáadása
- Feltöltési korlátozás személyre szabása felhasználói szinten.
Utoljára frissítve: