415 lines
11 KiB
Markdown
415 lines
11 KiB
Markdown
# Soft Delete Kullanıcı Yönetimi
|
||
|
||
## Genel Bakış
|
||
|
||
AuthCentral'da silinen kullanıcılar soft delete ile yönetilir. Bu, kullanıcıların veritabanından silinmeden sadece işaretlenerek pasif hale getirilmesi anlamına gelir.
|
||
|
||
## Yeni Endpoint'ler
|
||
|
||
### 1. Silinen Kullanıcıları Listele
|
||
|
||
```bash
|
||
GET /v1/admin/users/deleted
|
||
```
|
||
|
||
**Query Parameters:**
|
||
- `page` (int, optional) - Sayfa numarası (default: 1)
|
||
- `limit` (int, optional) - Sayfa başına kayıt (default: 10, max: 100)
|
||
|
||
**Örnek:**
|
||
```bash
|
||
TOKEN=$(curl -s -X POST http://localhost:8080/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"email":"admin@gauth.local","password":"Admin@123"}' | jq -r '.access_token')
|
||
|
||
curl -X GET "http://localhost:8080/v1/admin/users/deleted?page=1&limit=10" \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
```
|
||
|
||
**Yanıt:**
|
||
```json
|
||
{
|
||
"users": [
|
||
{
|
||
"id": "ca567947-ef2a-49ad-b955-bf0ef6bbf136",
|
||
"username": "Delete Me",
|
||
"email": "deleteme@test.com",
|
||
"avatar": "",
|
||
"email_verified": true,
|
||
"created_at": "2026-02-05T00:03:08.360433+03:00",
|
||
"updated_at": "2026-02-05T00:03:08.38027+03:00",
|
||
"deleted_at": "2026-02-05T00:03:25.549299+03:00",
|
||
"roles": [
|
||
{
|
||
"id": 2,
|
||
"name": "user",
|
||
"description": "Default user role"
|
||
}
|
||
],
|
||
"social_accounts": []
|
||
}
|
||
],
|
||
"pagination": {
|
||
"page": 1,
|
||
"limit": 10,
|
||
"total": 12,
|
||
"totalPages": 2
|
||
}
|
||
}
|
||
```
|
||
|
||
**Özellikler:**
|
||
- ✅ `deleted_at` field'ı görünür (normal endpoint'lerde gizli)
|
||
- ✅ Pagination desteği
|
||
- ✅ Sadece soft delete edilmiş kullanıcılar gösterilir
|
||
- ✅ En son silinen kullanıcılar önce gelir (deleted_at DESC)
|
||
|
||
### 2. Kullanıcıyı Geri Yükle (Restore)
|
||
|
||
```bash
|
||
POST /v1/admin/users/{id}/restore
|
||
```
|
||
|
||
**Path Parameters:**
|
||
- `id` (uuid, required) - Kullanıcı ID
|
||
|
||
**Örnek:**
|
||
```bash
|
||
TOKEN=$(curl -s -X POST http://localhost:8080/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"email":"admin@gauth.local","password":"Admin@123"}' | jq -r '.access_token')
|
||
|
||
USER_ID="ca567947-ef2a-49ad-b955-bf0ef6bbf136"
|
||
|
||
curl -X POST "http://localhost:8080/v1/admin/users/$USER_ID/restore" \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
```
|
||
|
||
**Başarılı Yanıt:**
|
||
```json
|
||
{
|
||
"message": "User restored successfully"
|
||
}
|
||
```
|
||
|
||
**Hata Yanıtları:**
|
||
```json
|
||
{
|
||
"error": "deleted user not found"
|
||
}
|
||
```
|
||
|
||
## Kullanım Senaryoları
|
||
|
||
### Senaryo 1: Silinen Kullanıcıları İnceleme
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
# Admin login
|
||
TOKEN=$(curl -s -X POST http://localhost:8080/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"email":"admin@gauth.local","password":"Admin@123"}' | jq -r '.access_token')
|
||
|
||
# Tüm silinen kullanıcıları listele
|
||
echo "=== Deleted Users ==="
|
||
curl -s -X GET "http://localhost:8080/v1/admin/users/deleted" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.users[] | {id, email, username, deleted_at}'
|
||
```
|
||
|
||
### Senaryo 2: Kullanıcıyı Soft Delete ve Restore
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
TOKEN=$(curl -s -X POST http://localhost:8080/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"email":"admin@gauth.local","password":"Admin@123"}' | jq -r '.access_token')
|
||
|
||
USER_ID="abc-123-def-456"
|
||
|
||
# 1. Kullanıcıyı soft delete yap
|
||
echo "Step 1: Soft delete user"
|
||
curl -s -X DELETE "http://localhost:8080/v1/admin/users/$USER_ID" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.'
|
||
|
||
# 2. Silinen kullanıcılar listesinde kontrol et
|
||
echo -e "\nStep 2: Check deleted users"
|
||
curl -s -X GET "http://localhost:8080/v1/admin/users/deleted" \
|
||
-H "Authorization: Bearer $TOKEN" | jq ".users[] | select(.id==\"$USER_ID\")"
|
||
|
||
# 3. Kullanıcıyı geri yükle
|
||
echo -e "\nStep 3: Restore user"
|
||
curl -s -X POST "http://localhost:8080/v1/admin/users/$USER_ID/restore" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.'
|
||
|
||
# 4. Normal kullanıcı listesinde kontrol et
|
||
echo -e "\nStep 4: Verify user is restored"
|
||
curl -s -X GET "http://localhost:8080/v1/admin/users/$USER_ID" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '{id, email, username}'
|
||
```
|
||
|
||
### Senaryo 3: Frontend İçin Silinen Kullanıcılar Yönetimi
|
||
|
||
**Frontend JavaScript Örneği:**
|
||
|
||
```javascript
|
||
// API Client
|
||
class AdminAPI {
|
||
constructor(baseURL, token) {
|
||
this.baseURL = baseURL;
|
||
this.token = token;
|
||
}
|
||
|
||
async getDeletedUsers(page = 1, limit = 10) {
|
||
const response = await fetch(
|
||
`${this.baseURL}/v1/admin/users/deleted?page=${page}&limit=${limit}`,
|
||
{
|
||
headers: {
|
||
'Authorization': `Bearer ${this.token}`
|
||
}
|
||
}
|
||
);
|
||
return response.json();
|
||
}
|
||
|
||
async restoreUser(userId) {
|
||
const response = await fetch(
|
||
`${this.baseURL}/v1/admin/users/${userId}/restore`,
|
||
{
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `Bearer ${this.token}`
|
||
}
|
||
}
|
||
);
|
||
return response.json();
|
||
}
|
||
|
||
async softDeleteUser(userId) {
|
||
const response = await fetch(
|
||
`${this.baseURL}/v1/admin/users/${userId}`,
|
||
{
|
||
method: 'DELETE',
|
||
headers: {
|
||
'Authorization': `Bearer ${this.token}`
|
||
}
|
||
}
|
||
);
|
||
return response.json();
|
||
}
|
||
|
||
async hardDeleteUser(userId) {
|
||
const response = await fetch(
|
||
`${this.baseURL}/v1/admin/users/${userId}?hard=true`,
|
||
{
|
||
method: 'DELETE',
|
||
headers: {
|
||
'Authorization': `Bearer ${this.token}`
|
||
}
|
||
}
|
||
);
|
||
return response.json();
|
||
}
|
||
}
|
||
|
||
// Kullanım
|
||
const admin = new AdminAPI('http://localhost:8080', YOUR_TOKEN);
|
||
|
||
// Silinen kullanıcıları getir
|
||
const deletedUsers = await admin.getDeletedUsers(1, 10);
|
||
console.log(deletedUsers);
|
||
|
||
// Kullanıcıyı geri yükle
|
||
const result = await admin.restoreUser('user-uuid-here');
|
||
console.log(result);
|
||
```
|
||
|
||
## API Endpoint'leri Özeti
|
||
|
||
| Endpoint | Method | Açıklama | Query/Body |
|
||
|----------|--------|----------|------------|
|
||
| `/v1/admin/users` | GET | Aktif kullanıcılar | `?page=1&limit=10` |
|
||
| `/v1/admin/users/deleted` | GET | **Silinen kullanıcılar** | `?page=1&limit=10` |
|
||
| `/v1/admin/users/{id}` | DELETE | Soft delete | - |
|
||
| `/v1/admin/users/{id}?hard=true` | DELETE | Hard delete (kalıcı) | `?hard=true` |
|
||
| `/v1/admin/users/{id}/restore` | POST | **Kullanıcıyı geri yükle** | - |
|
||
|
||
## Soft Delete vs Hard Delete
|
||
|
||
| Özellik | Soft Delete | Hard Delete |
|
||
|---------|-------------|-------------|
|
||
| **Veritabanı** | `deleted_at` timestamp set edilir | Tamamen silinir |
|
||
| **Görünürlük** | `/deleted` endpoint'inde görünür | Hiçbir yerde görünmez |
|
||
| **Geri Getirme** | ✅ `/restore` ile mümkün | ❌ İmkansız |
|
||
| **İlişkiler** | Korunur | Silinir |
|
||
| **Kullanım** | Varsayılan, güvenli | Dikkatli kullanılmalı |
|
||
| **Komut** | `DELETE /users/{id}` | `DELETE /users/{id}?hard=true` |
|
||
|
||
## Frontend Entegrasyonu
|
||
|
||
### React Örneği
|
||
|
||
```jsx
|
||
import React, { useState, useEffect } from 'react';
|
||
|
||
function DeletedUsersManager() {
|
||
const [deletedUsers, setDeletedUsers] = useState([]);
|
||
const [loading, setLoading] = useState(false);
|
||
const [page, setPage] = useState(1);
|
||
|
||
const fetchDeletedUsers = async () => {
|
||
setLoading(true);
|
||
try {
|
||
const response = await fetch(
|
||
`http://localhost:8080/v1/admin/users/deleted?page=${page}&limit=10`,
|
||
{
|
||
headers: {
|
||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||
}
|
||
}
|
||
);
|
||
const data = await response.json();
|
||
setDeletedUsers(data.users);
|
||
} catch (error) {
|
||
console.error('Error fetching deleted users:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const restoreUser = async (userId) => {
|
||
if (!confirm('Bu kullanıcıyı geri yüklemek istediğinize emin misiniz?')) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(
|
||
`http://localhost:8080/v1/admin/users/${userId}/restore`,
|
||
{
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||
}
|
||
}
|
||
);
|
||
|
||
if (response.ok) {
|
||
alert('Kullanıcı başarıyla geri yüklendi!');
|
||
fetchDeletedUsers(); // Listeyi yenile
|
||
}
|
||
} catch (error) {
|
||
console.error('Error restoring user:', error);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
fetchDeletedUsers();
|
||
}, [page]);
|
||
|
||
return (
|
||
<div className="deleted-users-manager">
|
||
<h2>Silinen Kullanıcılar</h2>
|
||
|
||
{loading ? (
|
||
<p>Yükleniyor...</p>
|
||
) : (
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Email</th>
|
||
<th>Kullanıcı Adı</th>
|
||
<th>Silinme Tarihi</th>
|
||
<th>İşlemler</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{deletedUsers.map(user => (
|
||
<tr key={user.id}>
|
||
<td>{user.email}</td>
|
||
<td>{user.username}</td>
|
||
<td>{new Date(user.deleted_at).toLocaleString('tr-TR')}</td>
|
||
<td>
|
||
<button onClick={() => restoreUser(user.id)}>
|
||
Geri Yükle
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
)}
|
||
|
||
<div className="pagination">
|
||
<button onClick={() => setPage(p => Math.max(1, p - 1))}>
|
||
Önceki
|
||
</button>
|
||
<span>Sayfa {page}</span>
|
||
<button onClick={() => setPage(p => p + 1)}>
|
||
Sonraki
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default DeletedUsersManager;
|
||
```
|
||
|
||
## Güvenlik Notları
|
||
|
||
✅ **İyi Pratikler:**
|
||
- Silinen kullanıcıları düzenli olarak gözden geçirin
|
||
- Restore işleminden önce kullanıcıyı doğrulayın
|
||
- Hard delete yapmadan önce soft delete kullanın
|
||
- Kritik kullanıcılar için restore geçmişi tutun
|
||
|
||
⚠️ **Dikkat Edilmesi Gerekenler:**
|
||
- Sadece admin rolündeki kullanıcılar bu endpoint'lere erişebilir
|
||
- Restore edilen kullanıcı önceki tüm rolleri ve ayarları ile geri gelir
|
||
- Soft delete edilmiş kullanıcılar login yapamaz
|
||
- Hard delete geri alınamaz, dikkatli kullanın
|
||
|
||
## Test Komutları
|
||
|
||
```bash
|
||
# 1. Kullanıcı oluştur
|
||
TOKEN=$(curl -s -X POST http://localhost:8080/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"email":"admin@gauth.local","password":"Admin@123"}' | jq -r '.access_token')
|
||
|
||
curl -X POST http://localhost:8080/v1/admin/users \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-F "email=test@example.com" \
|
||
-F "password=test123" \
|
||
-F "user_name=Test User" \
|
||
-F "roles=user"
|
||
|
||
# 2. Kullanıcıyı soft delete yap
|
||
curl -X DELETE "http://localhost:8080/v1/admin/users/USER_ID" \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
|
||
# 3. Silinen kullanıcıları listele
|
||
curl -X GET "http://localhost:8080/v1/admin/users/deleted" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.'
|
||
|
||
# 4. Kullanıcıyı geri yükle
|
||
curl -X POST "http://localhost:8080/v1/admin/users/USER_ID/restore" \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
```
|
||
|
||
## Özet
|
||
|
||
🎯 **Yeni Özellikler:**
|
||
- ✅ Silinen kullanıcıları listeleme
|
||
- ✅ Kullanıcıyı geri yükleme (restore)
|
||
- ✅ `deleted_at` field'ı görünürlüğü
|
||
- ✅ Pagination desteği
|
||
- ✅ Frontend entegrasyonu için hazır
|
||
|
||
📊 **Kullanım:**
|
||
- Soft delete varsayılan silme yöntemi
|
||
- Hard delete sadece kalıcı silme için
|
||
- Restore ile yanlışlıkla silinen kullanıcılar kurtarılabilir
|
||
- Frontend'de silinen kullanıcılar yönetilebilir
|