feat:profile pictures

This commit is contained in:
2025-10-16 18:08:03 +02:00
parent bee03a57ec
commit e54c4fb98e
14 changed files with 559 additions and 11 deletions

View File

@@ -0,0 +1,51 @@
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { deleteFromR2 } from '$lib/server/r2';
import { db } from '$lib/server/db';
import { user } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
export const DELETE: RequestHandler = async ({ request }) => {
try {
const { userId } = await request.json();
if (!userId) {
return json({ error: 'userId is required' }, { status: 400 });
}
// Get current user to find profile picture URL
const currentUser = await db.select().from(user).where(eq(user.id, userId)).limit(1);
if (!currentUser.length) {
return json({ error: 'User not found' }, { status: 404 });
}
const userRecord = currentUser[0];
if (!userRecord.profilePictureUrl) {
return json({ error: 'No profile picture to delete' }, { status: 400 });
}
// Extract the base path from the stored URL (should be like users/123/profile-1234567890-abcdef.webp)
const basePath = userRecord.profilePictureUrl.replace('.webp', '');
// Delete all variants (WebP and JPEG, main and thumbnails)
const deletePromises = [
deleteFromR2(`${basePath}.webp`),
deleteFromR2(`${basePath}.jpg`),
deleteFromR2(`${basePath}-thumb.webp`),
deleteFromR2(`${basePath}-thumb.jpg`)
];
// Execute all deletions, but don't fail if some files don't exist
await Promise.allSettled(deletePromises);
// Update user profile picture URL in database
await db.update(user).set({ profilePictureUrl: null }).where(eq(user.id, userId));
return json({ success: true });
} catch (error) {
console.error('Profile picture delete error:', error);
return json({ error: 'Failed to delete profile picture' }, { status: 500 });
}
};

View File

@@ -0,0 +1,45 @@
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { processAndUploadProfilePicture } from '$lib/server/media-processor';
import { db } from '$lib/server/db';
import { user } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
export const POST: RequestHandler = async ({ request }) => {
try {
const formData = await request.formData();
const file = formData.get('file') as File;
const userId = formData.get('userId') as string;
if (!file || !userId) {
return json({ error: 'File and userId are required' }, { status: 400 });
}
// Validate file type
if (!file.type.startsWith('image/')) {
return json({ error: 'File must be an image' }, { status: 400 });
}
// Validate file size (5MB max)
if (file.size > 5 * 1024 * 1024) {
return json({ error: 'File size must be less than 5MB' }, { status: 400 });
}
// Process and upload profile picture
const result = await processAndUploadProfilePicture(file, userId);
// Update user profile picture URL in database (store the WebP path)
await db.update(user).set({ profilePictureUrl: result.url }).where(eq(user.id, userId));
return json({
success: true,
profilePictureUrl: result.url,
thumbnailUrl: result.thumbnailUrl,
fallbackUrl: result.fallbackUrl,
fallbackThumbnailUrl: result.fallbackThumbnailUrl
});
} catch (error) {
console.error('Profile picture upload error:', error);
return json({ error: 'Failed to upload profile picture' }, { status: 500 });
}
};