first commit
This commit is contained in:
414
SOFT_DELETE_MANAGEMENT.md
Normal file
414
SOFT_DELETE_MANAGEMENT.md
Normal file
@@ -0,0 +1,414 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user