# 📤 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
```
---
## 🧪 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 && (
)}
);
}
export default AvatarUpload;
```
### Vue.js Component
```vue
{{ error }}
```
---
## 📊 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! 🎉**