import { put } from '@vercel/blob'; import { sql } from '@vercel/postgres'; import { getUserFromRequest } from '../../../../lib/permission-middleware'; export default async function handler(req, res) { // Set CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // Handle preflight requests if (req.method === 'OPTIONS') { res.status(200).end(); return; } if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } try { // Get authenticated user const user = await getUserFromRequest(req); if (!user) { return res.status(401).json({ error: 'Authentication required' }); } // Get user information for avatar generation const userResult = await sql` SELECT email, first_name, last_name, username FROM users WHERE id = ${user.userId} `; if (userResult.rows.length === 0) { return res.status(404).json({ error: 'User not found' }); } const userData = userResult.rows[0]; try { // Delete old avatar if exists await deleteOldAvatar(user.userId); // Generate avatar using a service (we'll use DiceBear Avatars as an example) const avatarStyle = 'initials'; // You can change this to other styles like 'avataaars', 'bottts', etc. const seed = userData.username || userData.email || `user-${user.userId}`; const initials = getInitials(userData); // Create avatar URL with DiceBear API const avatarUrl = `https://api.dicebear.com/7.x/${avatarStyle}/svg?seed=${encodeURIComponent(seed)}&chars=2&backgroundColor=d84315,ff5722,ff7043&textColor=ffffff&fontSize=40`; // Fetch the generated avatar const avatarResponse = await fetch(avatarUrl); if (!avatarResponse.ok) { throw new Error('Failed to generate avatar'); } const avatarBuffer = Buffer.from(await avatarResponse.arrayBuffer()); // Generate unique filename const filename = `avatars/generated-${user.userId}-${Date.now()}.svg`; // Upload to Vercel Blob const blob = await put(filename, avatarBuffer, { access: 'public', contentType: 'image/svg+xml', }); // Save avatar info to database await sql` INSERT INTO user_avatars (user_id, filename, original_name, mime_type, file_size, file_path, is_active) VALUES (${user.userId}, ${filename}, 'generated-avatar.svg', 'image/svg+xml', ${avatarBuffer.length}, ${blob.url}, true) `; // Update user's avatar_url await sql` UPDATE users SET avatar_url = ${blob.url}, updated_at = CURRENT_TIMESTAMP WHERE id = ${user.userId} `; res.status(200).json({ message: 'Avatar generated successfully', avatar_url: blob.url }); } catch (generateError) { console.error('Avatar generation error:', generateError); res.status(500).json({ error: 'Failed to generate avatar' }); } } catch (error) { console.error('Avatar generation API error:', error); res.status(500).json({ error: 'Internal server error' }); } } /** * Get user initials for avatar generation */ function getInitials(userData) { if (userData.first_name || userData.last_name) { return `${userData.first_name?.charAt(0) || ''}${userData.last_name?.charAt(0) || ''}`.toUpperCase(); } if (userData.username) { return userData.username.substring(0, 2).toUpperCase(); } return userData.email?.charAt(0).toUpperCase() || 'U'; } /** * Delete old avatar from Vercel Blob and database */ async function deleteOldAvatar(userId) { try { // Get current active avatar const avatarResult = await sql` SELECT file_path, filename FROM user_avatars WHERE user_id = ${userId} AND is_active = true `; if (avatarResult.rows.length > 0) { const avatar = avatarResult.rows[0]; // Delete from Vercel Blob try { const { del } = await import('@vercel/blob'); await del(avatar.file_path); } catch (blobError) { console.warn('Failed to delete blob file:', blobError); // Continue anyway - the database record should still be cleaned up } // Mark as inactive in database await sql` UPDATE user_avatars SET is_active = false, updated_at = CURRENT_TIMESTAMP WHERE user_id = ${userId} AND is_active = true `; } } catch (error) { console.error('Error deleting old avatar:', error); // Don't throw - this shouldn't prevent new uploads } }