first commit
This commit is contained in:
573
belgeler/USER_CREATE_UPDATE_WITH_AVATAR.md
Normal file
573
belgeler/USER_CREATE_UPDATE_WITH_AVATAR.md
Normal file
@@ -0,0 +1,573 @@
|
||||
# 🎉 User Create & Update - Avatar Upload Desteği
|
||||
|
||||
## ✨ Yeni Özellik
|
||||
|
||||
Artık yeni kullanıcı oluştururken ve mevcut kullanıcıyı güncellerken **aynı request'te avatar da yükleyebilirsiniz!**
|
||||
|
||||
---
|
||||
|
||||
## 📋 Güncellenen Endpoint'ler
|
||||
|
||||
### 1. POST /v1/admin/users (Create User)
|
||||
|
||||
**Artık Multipart/Form-Data Destekliyor!**
|
||||
|
||||
**Önceden:**
|
||||
```bash
|
||||
# Sadece JSON
|
||||
curl -X POST http://localhost:8080/v1/admin/users \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"user@example.com","password":"Pass123!","user_name":"john"}'
|
||||
```
|
||||
|
||||
**Şimdi:**
|
||||
```bash
|
||||
# Multipart ile avatar da ekleyebilirsiniz!
|
||||
curl -X POST http://localhost:8080/v1/admin/users \
|
||||
-H "Authorization: Bearer ADMIN_TOKEN" \
|
||||
-F "email=user@example.com" \
|
||||
-F "password=Pass123!" \
|
||||
-F "user_name=john" \
|
||||
-F "email_verified=true" \
|
||||
-F "roles=admin,user" \
|
||||
-F "avatar=@/path/to/photo.jpg"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"id": "new-user-uuid",
|
||||
"username": "john",
|
||||
"email": "user@example.com",
|
||||
"avatar": "/uploads/avatars/new-user-uuid_1707012345.jpg",
|
||||
"email_verified": true,
|
||||
"roles": [
|
||||
{"name": "admin"},
|
||||
{"name": "user"}
|
||||
],
|
||||
"created_at": "2026-02-04T..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. PUT /v1/admin/users/:id (Update User)
|
||||
|
||||
**Hem JSON hem Multipart Destekliyor!**
|
||||
|
||||
**JSON ile (avatar URL):**
|
||||
```bash
|
||||
curl -X PUT http://localhost:8080/v1/admin/users/USER_ID \
|
||||
-H "Authorization: Bearer ADMIN_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "new@example.com",
|
||||
"user_name": "newname",
|
||||
"roles": ["admin"]
|
||||
}'
|
||||
```
|
||||
|
||||
**Multipart ile (avatar dosya):**
|
||||
```bash
|
||||
curl -X PUT http://localhost:8080/v1/admin/users/USER_ID \
|
||||
-H "Authorization: Bearer ADMIN_TOKEN" \
|
||||
-F "email=new@example.com" \
|
||||
-F "user_name=newname" \
|
||||
-F "roles=admin,user" \
|
||||
-F "avatar=@/path/to/new-photo.jpg"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "User updated successfully",
|
||||
"user": {
|
||||
"id": "user-uuid",
|
||||
"username": "newname",
|
||||
"email": "new@example.com",
|
||||
"avatar": "/uploads/avatars/user-uuid_1707012346.jpg",
|
||||
"roles": [
|
||||
{"name": "admin"},
|
||||
{"name": "user"}
|
||||
],
|
||||
"updated_at": "2026-02-04T..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Form Fields
|
||||
|
||||
### Create User (POST)
|
||||
|
||||
| Field | Type | Required | Açıklama |
|
||||
|-------|------|----------|----------|
|
||||
| `email` | string | ✅ | Email adresi |
|
||||
| `password` | string | ✅ | Şifre (min 6 karakter) |
|
||||
| `user_name` | string | ✅ | Kullanıcı adı |
|
||||
| `email_verified` | boolean | ❌ | Email doğrulanmış mı? (true/false) |
|
||||
| `roles` | string | ❌ | Roller (comma separated: "admin,user") |
|
||||
| `avatar` | file | ❌ | Avatar dosyası (max 5MB, jpg/png/gif/webp) |
|
||||
|
||||
### Update User (PUT)
|
||||
|
||||
| Field | Type | Required | Açıklama |
|
||||
|-------|------|----------|----------|
|
||||
| `email` | string | ❌ | Yeni email |
|
||||
| `password` | string | ❌ | Yeni şifre |
|
||||
| `user_name` | string | ❌ | Yeni kullanıcı adı |
|
||||
| `email_verified` | boolean | ❌ | Email doğrulama durumu |
|
||||
| `roles` | string | ❌ | Yeni roller (comma separated) |
|
||||
| `avatar` | file | ❌ | Yeni avatar dosyası |
|
||||
|
||||
**Not:** Tüm alanlar optional, sadece güncellemek istediklerinizi gönderin.
|
||||
|
||||
---
|
||||
|
||||
## 💻 Frontend Kullanımı
|
||||
|
||||
### HTML Form - Yeni Kullanıcı Oluştur
|
||||
|
||||
```html
|
||||
<form id="createUserForm">
|
||||
<input type="email" name="email" required placeholder="Email">
|
||||
<input type="password" name="password" required placeholder="Password">
|
||||
<input type="text" name="user_name" required placeholder="Username">
|
||||
|
||||
<label>
|
||||
<input type="checkbox" name="email_verified" value="true">
|
||||
Email Verified
|
||||
</label>
|
||||
|
||||
<select name="roles" multiple>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="user">User</option>
|
||||
</select>
|
||||
|
||||
<input type="file" name="avatar" accept="image/*">
|
||||
|
||||
<button type="submit">Create User</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.getElementById('createUserForm').onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
|
||||
// Roles'ü comma separated yap
|
||||
const rolesSelect = e.target.roles;
|
||||
const selectedRoles = Array.from(rolesSelect.selectedOptions).map(opt => opt.value);
|
||||
formData.set('roles', selectedRoles.join(','));
|
||||
|
||||
const token = localStorage.getItem('admin_token');
|
||||
|
||||
const response = await fetch('http://localhost:8080/v1/admin/users', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('User created:', data);
|
||||
alert('User created successfully!');
|
||||
if (data.avatar) {
|
||||
console.log('Avatar URL:', data.avatar);
|
||||
}
|
||||
} else {
|
||||
alert('Error: ' + data.error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
### React - Kullanıcı Güncelle
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react';
|
||||
|
||||
function UpdateUserForm({ userId }) {
|
||||
const [formData, setFormData] = useState({
|
||||
email: '',
|
||||
user_name: '',
|
||||
password: '',
|
||||
email_verified: false,
|
||||
roles: [],
|
||||
avatar: null
|
||||
});
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const data = new FormData();
|
||||
|
||||
if (formData.email) data.append('email', formData.email);
|
||||
if (formData.user_name) data.append('user_name', formData.user_name);
|
||||
if (formData.password) data.append('password', formData.password);
|
||||
if (formData.email_verified) data.append('email_verified', 'true');
|
||||
if (formData.roles.length > 0) data.append('roles', formData.roles.join(','));
|
||||
if (formData.avatar) data.append('avatar', formData.avatar);
|
||||
|
||||
const token = localStorage.getItem('admin_token');
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8080/v1/admin/users/${userId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: data
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('User updated:', result.user);
|
||||
alert('User updated successfully!');
|
||||
} else {
|
||||
alert('Error: ' + result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Update failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({...formData, email: e.target.value})}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={formData.user_name}
|
||||
onChange={(e) => setFormData({...formData, user_name: e.target.value})}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="New Password"
|
||||
value={formData.password}
|
||||
onChange={(e) => setFormData({...formData, password: e.target.value})}
|
||||
/>
|
||||
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={formData.email_verified}
|
||||
onChange={(e) => setFormData({...formData, email_verified: e.target.checked})}
|
||||
/>
|
||||
Email Verified
|
||||
</label>
|
||||
|
||||
<select
|
||||
multiple
|
||||
value={formData.roles}
|
||||
onChange={(e) => setFormData({
|
||||
...formData,
|
||||
roles: Array.from(e.target.selectedOptions).map(opt => opt.value)
|
||||
})}
|
||||
>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="user">User</option>
|
||||
</select>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={(e) => setFormData({...formData, avatar: e.target.files[0]})}
|
||||
/>
|
||||
|
||||
<button type="submit">Update User</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Vue.js - Yeni Kullanıcı Oluştur
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<form @submit.prevent="createUser">
|
||||
<input v-model="formData.email" type="email" required placeholder="Email">
|
||||
<input v-model="formData.password" type="password" required placeholder="Password">
|
||||
<input v-model="formData.user_name" type="text" required placeholder="Username">
|
||||
|
||||
<label>
|
||||
<input v-model="formData.email_verified" type="checkbox">
|
||||
Email Verified
|
||||
</label>
|
||||
|
||||
<select v-model="formData.roles" multiple>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="user">User</option>
|
||||
</select>
|
||||
|
||||
<input type="file" @change="handleFileChange" accept="image/*">
|
||||
|
||||
<button type="submit">Create User</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
email: '',
|
||||
password: '',
|
||||
user_name: '',
|
||||
email_verified: false,
|
||||
roles: [],
|
||||
avatar: null
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFileChange(e) {
|
||||
this.formData.avatar = e.target.files[0];
|
||||
},
|
||||
|
||||
async createUser() {
|
||||
const data = new FormData();
|
||||
|
||||
data.append('email', this.formData.email);
|
||||
data.append('password', this.formData.password);
|
||||
data.append('user_name', this.formData.user_name);
|
||||
data.append('email_verified', this.formData.email_verified ? 'true' : 'false');
|
||||
|
||||
if (this.formData.roles.length > 0) {
|
||||
data.append('roles', this.formData.roles.join(','));
|
||||
}
|
||||
|
||||
if (this.formData.avatar) {
|
||||
data.append('avatar', this.formData.avatar);
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('admin_token');
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:8080/v1/admin/users', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: data
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('User created:', result);
|
||||
alert('User created successfully!');
|
||||
this.$emit('userCreated', result);
|
||||
} else {
|
||||
alert('Error: ' + result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Test Örnekleri
|
||||
|
||||
### Test 1: Yeni Kullanıcı + Avatar Oluştur
|
||||
|
||||
```bash
|
||||
# Admin token al
|
||||
curl -X POST http://localhost:8080/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"admin@gauth.local","password":"Admin@123"}'
|
||||
|
||||
# Token'ı kaydet
|
||||
TOKEN="eyJhbGci..."
|
||||
|
||||
# Yeni kullanıcı oluştur (avatar ile)
|
||||
curl -X POST http://localhost:8080/v1/admin/users \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-F "email=newuser@example.com" \
|
||||
-F "password=NewUser123!" \
|
||||
-F "user_name=newuser" \
|
||||
-F "email_verified=true" \
|
||||
-F "roles=user" \
|
||||
-F "avatar=@./user-photo.jpg"
|
||||
|
||||
# Response'da avatar URL göreceksiniz!
|
||||
```
|
||||
|
||||
### Test 2: Kullanıcı Güncelle + Yeni Avatar
|
||||
|
||||
```bash
|
||||
USER_ID="user-uuid-here"
|
||||
|
||||
curl -X PUT http://localhost:8080/v1/admin/users/$USER_ID \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-F "email=updated@example.com" \
|
||||
-F "user_name=updatedname" \
|
||||
-F "roles=admin,user" \
|
||||
-F "avatar=@./new-avatar.jpg"
|
||||
```
|
||||
|
||||
### Test 3: Sadece Avatar Değiştir
|
||||
|
||||
```bash
|
||||
# Diğer alanları göndermeden sadece avatar
|
||||
curl -X PUT http://localhost:8080/v1/admin/users/$USER_ID \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-F "avatar=@./profile-pic.jpg"
|
||||
```
|
||||
|
||||
### Test 4: Avatar Olmadan Güncelle
|
||||
|
||||
```bash
|
||||
# Avatar olmadan da güncelleme yapabilirsiniz
|
||||
curl -X PUT http://localhost:8080/v1/admin/users/$USER_ID \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-F "email=another@example.com" \
|
||||
-F "user_name=anothername"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Özellikler
|
||||
|
||||
### ✅ Create User
|
||||
- **Multipart/form-data** ile avatar yükleme
|
||||
- Email, password, username **required**
|
||||
- Roles optional (comma separated: "admin,user")
|
||||
- Email verified optional (true/false)
|
||||
- Avatar optional (max 5MB)
|
||||
- **Tek request**te hem kullanıcı hem avatar oluşturulur
|
||||
|
||||
### ✅ Update User
|
||||
- **Hem JSON hem multipart** destekliyor
|
||||
- Tüm alanlar optional
|
||||
- Avatar dosya ile veya URL ile güncellenebilir
|
||||
- Eski avatar otomatik silinir (local ise)
|
||||
- OAuth avatar'ları korunur
|
||||
|
||||
### ✅ Avatar Validasyonu
|
||||
- Format: JPG, JPEG, PNG, GIF, WebP
|
||||
- Maksimum boyut: 5MB
|
||||
- Otomatik dosya ismi: `{user_id}_{timestamp}.{ext}`
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Content-Type Desteği
|
||||
|
||||
### Create User
|
||||
```
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
### Update User
|
||||
```
|
||||
Content-Type: application/json (JSON için)
|
||||
Content-Type: multipart/form-data (Avatar upload için)
|
||||
```
|
||||
|
||||
Handler otomatik olarak Content-Type'a göre parse eder!
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Önemli Notlar
|
||||
|
||||
### 1. Roles Format
|
||||
|
||||
**Multipart:**
|
||||
```
|
||||
roles=admin,user
|
||||
```
|
||||
|
||||
**JSON:**
|
||||
```json
|
||||
{
|
||||
"roles": ["admin", "user"]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Boolean Fields
|
||||
|
||||
**Multipart:**
|
||||
```
|
||||
email_verified=true
|
||||
```
|
||||
|
||||
**JSON:**
|
||||
```json
|
||||
{
|
||||
"email_verified": true
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Avatar Önceliği
|
||||
|
||||
1. Dosya upload (multipart) → En yüksek öncelik
|
||||
2. Avatar URL (JSON) → Dosya yoksa
|
||||
3. Mevcut avatar → Değişiklik yoksa
|
||||
|
||||
### 4. Eski Avatar Temizleme
|
||||
|
||||
```go
|
||||
// Otomatik temizlik
|
||||
if user.Avatar != "" && strings.HasPrefix(user.Avatar, "/uploads/") {
|
||||
os.Remove("." + user.Avatar) // Eski local dosya silinir
|
||||
}
|
||||
|
||||
// OAuth avatar'lar korunur
|
||||
// https://... ile başlayanlar silinmez
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Özet
|
||||
|
||||
### Artık Yapabilirsiniz
|
||||
|
||||
1. ✅ **Yeni kullanıcı oluştururken avatar yükleyin**
|
||||
2. ✅ **Kullanıcı güncellerken avatar değiştirin**
|
||||
3. ✅ **Hem JSON hem multipart desteği**
|
||||
4. ✅ **Tek request'te tüm işlemler**
|
||||
5. ✅ **Otomatik dosya temizleme**
|
||||
6. ✅ **Validation ve error handling**
|
||||
|
||||
### Build Durumu
|
||||
|
||||
```bash
|
||||
✅ go build -o main .
|
||||
✅ Build successful
|
||||
✅ CreateUser multipart destekliyor
|
||||
✅ UpdateUser multipart + JSON destekliyor
|
||||
```
|
||||
|
||||
### Test Edin
|
||||
|
||||
```bash
|
||||
# Uygulamayı başlat
|
||||
./main
|
||||
|
||||
# Yeni kullanıcı + avatar
|
||||
curl -X POST http://localhost:8080/v1/admin/users \
|
||||
-H "Authorization: Bearer TOKEN" \
|
||||
-F "email=test@example.com" \
|
||||
-F "password=Test123!" \
|
||||
-F "user_name=testuser" \
|
||||
-F "avatar=@photo.jpg"
|
||||
```
|
||||
|
||||
**Artık user create ve update'te avatar yükleyebilirsiniz! 🎉**
|
||||
Reference in New Issue
Block a user