# 📤 Avatar Upload API - Multipart Form Data ## ✨ Özellikler Avatar artık **multipart/form-data** ile dosya upload olarak gönderilir (JSON değil). ### ✅ Desteklenen Özellikler - 📁 Dosya upload (multipart/form-data) - 🖼️ Format kontrolü (jpg, jpeg, png, gif, webp) - 📏 Boyut kontrolü (max 5MB) - 🗑️ Eski avatar otomatik silme - 👤 Kullanıcı kendi avatar'ını yükleyebilir - 👨‍💼 Admin herhangi bir kullanıcının avatar'ını yükleyebilir - 🌐 Static file serving --- ## 📋 Endpoint'ler ### 1. Kullanıcı Kendi Avatar'ını Yükler ``` POST /v1/user/avatar Content-Type: multipart/form-data Authorization: Bearer {token} ``` **Form Data:** - `avatar` (file, required) - Avatar dosyası **Desteklenen Formatlar:** - JPG / JPEG - PNG - GIF - WebP **Maksimum Boyut:** 5MB **Response (200):** ```json { "message": "Avatar uploaded successfully", "avatar_url": "/uploads/avatars/user-uuid_1234567890.jpg", "user": { "id": "uuid", "username": "john_doe", "email": "john@example.com", "avatar": "/uploads/avatars/user-uuid_1234567890.jpg" } } ``` **cURL Örneği:** ```bash curl -X POST http://localhost:8080/v1/user/avatar \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "avatar=@/path/to/image.jpg" ``` --- ### 2. Kullanıcı Avatar'ını Siler ``` DELETE /v1/user/avatar Authorization: Bearer {token} ``` **Response (200):** ```json { "message": "Avatar deleted successfully", "user": { "id": "uuid", "username": "john_doe", "email": "john@example.com", "avatar": "" } } ``` **cURL Örneği:** ```bash curl -X DELETE http://localhost:8080/v1/user/avatar \ -H "Authorization: Bearer YOUR_TOKEN" ``` --- ### 3. Admin Kullanıcı Avatar'ı Yükler ``` POST /v1/admin/users/{user_id}/avatar Content-Type: multipart/form-data Authorization: Bearer {admin_token} ``` **Form Data:** - `avatar` (file, required) - Avatar dosyası **Response (200):** ```json { "message": "Avatar uploaded successfully", "avatar_url": "/uploads/avatars/user-uuid_1234567890.jpg", "user": { "id": "uuid", "username": "john_doe", "email": "john@example.com", "avatar": "/uploads/avatars/user-uuid_1234567890.jpg" } } ``` **cURL Örneği:** ```bash curl -X POST http://localhost:8080/v1/admin/users/USER_ID/avatar \ -H "Authorization: Bearer ADMIN_TOKEN" \ -F "avatar=@/path/to/image.jpg" ``` --- ### 4. Avatar Görüntüleme (Static File) ``` GET /uploads/avatars/{filename} ``` Avatar dosyaları otomatik olarak static file server tarafından sunulur. **Örnek:** ``` http://localhost:8080/uploads/avatars/user-uuid_1234567890.jpg ``` **HTML'de Kullanım:** ```html Avatar ``` --- ## 🧪 Test Örnekleri ### Test 1: Avatar Yükleme (cURL) ```bash # 1. Login olun curl -X POST http://localhost:8080/v1/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "test@example.com", "password": "Test123!" }' # Response'dan token alın TOKEN="eyJhbGci..." # 2. Avatar yükleyin curl -X POST http://localhost:8080/v1/user/avatar \ -H "Authorization: Bearer $TOKEN" \ -F "avatar=@./my-photo.jpg" # Response: # { # "message": "Avatar uploaded successfully", # "avatar_url": "/uploads/avatars/uuid_1234567890.jpg" # } ``` ### Test 2: Avatar Silme ```bash curl -X DELETE http://localhost:8080/v1/user/avatar \ -H "Authorization: Bearer $TOKEN" ``` ### Test 3: Admin Avatar Upload ```bash # Admin token ile ADMIN_TOKEN="admin_token_here" USER_ID="user-uuid-here" curl -X POST http://localhost:8080/v1/admin/users/$USER_ID/avatar \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -F "avatar=@./profile-picture.jpg" ``` --- ## 💻 Frontend Kullanımı ### HTML Form ```html
``` ### React Component ```jsx import { useState } from 'react'; function AvatarUpload() { const [uploading, setUploading] = useState(false); const [avatarUrl, setAvatarUrl] = useState(''); const handleUpload = async (e) => { e.preventDefault(); const file = e.target.avatar.files[0]; if (!file) return; // Validate file size if (file.size > 5 * 1024 * 1024) { alert('File size must be less than 5MB'); return; } // Validate file type const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp']; if (!allowedTypes.includes(file.type)) { alert('Only JPG, PNG, GIF, and WebP images are allowed'); return; } const formData = new FormData(); formData.append('avatar', file); const token = localStorage.getItem('access_token'); setUploading(true); try { const response = await fetch('http://localhost:8080/v1/user/avatar', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: formData }); const data = await response.json(); if (response.ok) { setAvatarUrl('http://localhost:8080' + data.avatar_url); alert('Avatar uploaded successfully!'); } else { alert('Error: ' + data.error); } } catch (error) { console.error('Upload failed:', error); alert('Upload failed'); } finally { setUploading(false); } }; const handleDelete = async () => { const token = localStorage.getItem('access_token'); try { const response = await fetch('http://localhost:8080/v1/user/avatar', { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); if (response.ok) { setAvatarUrl(''); alert('Avatar deleted successfully!'); } else { alert('Error: ' + data.error); } } catch (error) { console.error('Delete failed:', error); } }; return (
{avatarUrl && (
Avatar
)}
); } export default AvatarUpload; ``` ### Vue.js Component ```vue ``` --- ## 📊 Dosya Sistemi ### Klasör Yapısı ``` AuthCentral/ ├── uploads/ │ └── avatars/ │ ├── uuid1_1234567890.jpg │ ├── uuid2_1234567891.png │ └── uuid3_1234567892.webp ├── api/ │ └── handlers/ │ └── avatar_handler.go └── main.go ``` ### Avatar URL Formatı **Uploaded Files:** ``` /uploads/avatars/{user_id}_{timestamp}.{ext} Örnek: /uploads/avatars/550e8400-e29b-41d4-a716-446655440000_1707012345.jpg ``` **OAuth Avatar URLs (değişmez):** ``` https://lh3.googleusercontent.com/a/... https://avatars.githubusercontent.com/u/... ``` --- ## 🔒 Güvenlik ### Dosya Validasyonu 1. ✅ **Dosya Boyutu:** Maksimum 5MB 2. ✅ **Dosya Formatı:** Sadece jpg, jpeg, png, gif, webp 3. ✅ **Authentication:** Bearer token zorunlu 4. ✅ **Authorization:** Kullanıcı sadece kendi avatar'ını yükleyebilir (admin hariç) ### Dosya İsimlendirme - Unique filename: `{user_id}_{timestamp}.{ext}` - Collision önleme: Timestamp kullanımı - User ID ile ilişkilendirme ### Eski Dosya Temizleme - Yeni avatar yüklendiğinde eski dosya otomatik silinir - Sadece `/uploads/` ile başlayan dosyalar silinir (OAuth URL'leri korunur) --- ## ⚠️ Önemli Notlar ### 1. Static File Serving Uploads klasörü `/uploads` route'u ile sunuluyor: ```go r.Static("/uploads", "./uploads") ``` Avatar URL'si: ``` http://localhost:8080/uploads/avatars/filename.jpg ``` ### 2. Dosya Boyutu Limiti Frontend'de validasyon yapmayı unutmayın: ```javascript if (file.size > 5 * 1024 * 1024) { alert('File too large! Max 5MB'); return; } ``` ### 3. CORS Frontend farklı origin'den çalışıyorsa, static files için de CORS gerekebilir. ### 4. Production Deployment Production'da: - Cloud storage kullanın (S3, Google Cloud Storage, etc.) - CDN kullanın - Image optimization yapın - Thumbnail oluşturun --- ## 📋 Endpoint Özeti | Method | Endpoint | Auth | Açıklama | |--------|----------|------|----------| | POST | `/v1/user/avatar` | ✅ User | Kendi avatar'ını yükle | | DELETE | `/v1/user/avatar` | ✅ User | Kendi avatar'ını sil | | POST | `/v1/admin/users/:id/avatar` | ✅ Admin | Kullanıcı avatar'ı yükle | | GET | `/uploads/avatars/{filename}` | ❌ | Avatar görüntüle | --- ## 🎯 Kullanım Akışı ### Normal Kullanıcı 1. Login → Token al 2. POST `/v1/user/avatar` (multipart/form-data ile dosya gönder) 3. Response'da avatar URL gelir 4. Avatar'ı görüntüle: `GET /uploads/avatars/{filename}` 5. İsterseniz DELETE ile silin ### Admin 1. Admin login → Token al 2. POST `/v1/admin/users/{user_id}/avatar` (herhangi bir kullanıcı için) 3. Kullanıcının avatar'ı güncellenir --- ## ✅ Başarı Kriterleri - ✅ Multipart/form-data ile dosya upload - ✅ 5MB boyut limiti - ✅ Format validasyonu - ✅ Eski avatar otomatik silme - ✅ Static file serving - ✅ User + Admin endpoint'leri - ✅ Authentication & Authorization **Avatar upload sistemi tam çalışıyor! 🎉**