# 📤 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
<img src="http://localhost:8080/uploads/avatars/user-uuid_1234567890.jpg" alt="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
<form id="avatarForm">
  <input type="file" id="avatarInput" accept="image/*" required>
  <button type="submit">Upload Avatar</button>
</form>

<script>
document.getElementById('avatarForm').addEventListener('submit', async (e) => {
  e.preventDefault();
  
  const fileInput = document.getElementById('avatarInput');
  const file = fileInput.files[0];
  
  if (!file) {
    alert('Please select a file');
    return;
  }
  
  const formData = new FormData();
  formData.append('avatar', file);
  
  const token = localStorage.getItem('access_token');
  
  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) {
      console.log('Avatar uploaded:', data.avatar_url);
      alert('Avatar uploaded successfully!');
      
      // Avatar'ı göster
      document.getElementById('userAvatar').src = 
        'http://localhost:8080' + data.avatar_url;
    } else {
      alert('Error: ' + data.error);
    }
  } catch (error) {
    console.error('Upload failed:', error);
    alert('Upload failed');
  }
});
</script>
```

### 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 (
    <div className="avatar-upload">
      {avatarUrl && (
        <div>
          <img 
            src={avatarUrl} 
            alt="Avatar" 
            className="w-32 h-32 rounded-full object-cover"
          />
          <button onClick={handleDelete} className="btn-danger">
            Delete Avatar
          </button>
        </div>
      )}
      
      <form onSubmit={handleUpload}>
        <input 
          type="file" 
          name="avatar"
          accept="image/jpeg,image/jpg,image/png,image/gif,image/webp"
          disabled={uploading}
        />
        <button type="submit" disabled={uploading}>
          {uploading ? 'Uploading...' : 'Upload Avatar'}
        </button>
      </form>
    </div>
  );
}

export default AvatarUpload;
```

### Vue.js Component

```vue
<template>
  <div class="avatar-upload">
    <div v-if="avatarUrl">
      <img :src="avatarUrl" alt="Avatar" class="avatar-preview" />
      <button @click="deleteAvatar" class="btn-danger">Delete Avatar</button>
    </div>
    
    <form @submit.prevent="uploadAvatar">
      <input 
        type="file" 
        ref="fileInput"
        accept="image/*"
        @change="handleFileSelect"
        :disabled="uploading"
      />
      <button type="submit" :disabled="uploading || !selectedFile">
        {{ uploading ? 'Uploading...' : 'Upload Avatar' }}
      </button>
    </form>
    
    <div v-if="error" class="error">{{ error }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      avatarUrl: '',
      selectedFile: null,
      uploading: false,
      error: ''
    }
  },
  methods: {
    handleFileSelect(event) {
      const file = event.target.files[0];
      
      if (!file) return;
      
      // Validate file size (5MB)
      if (file.size > 5 * 1024 * 1024) {
        this.error = 'File size must be less than 5MB';
        this.$refs.fileInput.value = '';
        return;
      }
      
      // Validate file type
      const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
      if (!allowedTypes.includes(file.type)) {
        this.error = 'Only JPG, PNG, GIF, and WebP images are allowed';
        this.$refs.fileInput.value = '';
        return;
      }
      
      this.selectedFile = file;
      this.error = '';
    },
    
    async uploadAvatar() {
      if (!this.selectedFile) return;
      
      const formData = new FormData();
      formData.append('avatar', this.selectedFile);
      
      const token = localStorage.getItem('access_token');
      
      this.uploading = true;
      this.error = '';
      
      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) {
          this.avatarUrl = 'http://localhost:8080' + data.avatar_url;
          this.$refs.fileInput.value = '';
          this.selectedFile = null;
        } else {
          this.error = data.error || 'Upload failed';
        }
      } catch (error) {
        this.error = 'Upload failed: ' + error.message;
      } finally {
        this.uploading = false;
      }
    },
    
    async deleteAvatar() {
      const token = localStorage.getItem('access_token');
      
      try {
        const response = await fetch('http://localhost:8080/v1/user/avatar', {
          method: 'DELETE',
          headers: {
            'Authorization': `Bearer ${token}`
          }
        });
        
        if (response.ok) {
          this.avatarUrl = '';
        } else {
          const data = await response.json();
          this.error = data.error || 'Delete failed';
        }
      } catch (error) {
        this.error = 'Delete failed: ' + error.message;
      }
    }
  }
}
</script>

<style scoped>
.avatar-preview {
  width: 128px;
  height: 128px;
  border-radius: 50%;
  object-fit: cover;
  margin-bottom: 1rem;
}
</style>
```

---

## 📊 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! 🎉**
