# Connect to your database and run the migration
psql $DATABASE_URL < scripts/add-soft-delete.sql
# Verify the migration
psql $DATABASE_URL -c "SELECT column_name FROM information_schema.columns WHERE table_name = 'snippets' AND column_name IN ('is_deleted', 'deleted_at', 'deleted_by');"The following files have been created/modified:
New Files:
lib/activity-logger.ts- Activity logging utilityapp/api/snippets/trash/route.ts- Trash endpointapp/api/snippets/[id]/restore/route.ts- Restore endpointapp/api/snippets/[id]/activity/route.ts- Activity history endpointscripts/add-soft-delete.sql- Database migration
Modified Files:
app/api/snippets/snippet.repository.ts- Added soft delete methodsapp/api/snippets/snippet.service.ts- Added soft delete service methodsapp/api/snippets/[id]/route.ts- Updated DELETE to use soft deleteapp/api/snippets/ownership.middleware.ts- Added includeDeleted parameter
curl -X DELETE http://localhost:3000/api/snippets/{snippet-id} \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"curl http://localhost:3000/api/snippets/trash \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"curl -X POST http://localhost:3000/api/snippets/{snippet-id}/restore \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"curl http://localhost:3000/api/snippets/{snippet-id}/activity \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"Create these React components:
- TrashSection - Display deleted snippets
- DeleteConfirmationDialog - Confirm before delete
- ActivityTimeline - Show activity history
- Updated SnippetCard - Add delete button
See SOFT_DELETE_FRONTEND.md for complete implementation examples.
| Method | Endpoint | Description |
|---|---|---|
| DELETE | /api/snippets/[id] |
Soft delete a snippet |
| GET | /api/snippets/trash |
Get user's deleted snippets |
| POST | /api/snippets/[id]/restore |
Restore a deleted snippet |
| GET | /api/snippets/[id]/activity |
Get activity history |
All endpoints require:
x-wallet-address: <user-wallet-address>
{
"message": "Snippet deleted successfully",
"note": "Snippet moved to trash. You can restore it from the trash section."
}{
"data": [
{
"id": "uuid",
"title": "My Snippet",
"description": "Description",
"language": "javascript",
"deleted_at": "2026-05-26T10:30:00Z",
"deleted_by": "GXXXXXX...",
"is_deleted": true
}
],
"pagination": {
"total": 5,
"limit": 20,
"offset": 0,
"hasMore": false
}
}{
"message": "Snippet restored successfully",
"snippet": {
"id": "uuid",
"title": "My Snippet",
"is_deleted": false,
"deleted_at": null,
"deleted_by": null
}
}{
"snippetId": "uuid",
"activities": [
{
"id": "uuid",
"action": "DELETE",
"userWalletAddress": "GXXXXXX...",
"details": {
"title": "My Snippet",
"language": "javascript",
"deletedAt": "2026-05-26T10:30:00Z"
},
"createdAt": "2026-05-26T10:30:00Z"
}
],
"total": 1
}# 1. Create a snippet
SNIPPET_ID=$(curl -X POST http://localhost:3000/api/snippets \
-H "Content-Type: application/json" \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7" \
-d '{
"title": "Test",
"description": "Test",
"code": "console.log(\"test\");",
"language": "javascript",
"tags": ["test"]
}' | jq -r '.id')
# 2. Delete the snippet
curl -X DELETE http://localhost:3000/api/snippets/$SNIPPET_ID \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"
# 3. Verify it's in trash
curl http://localhost:3000/api/snippets/trash \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"
# 4. Restore it
curl -X POST http://localhost:3000/api/snippets/$SNIPPET_ID/restore \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"
# 5. View activity
curl http://localhost:3000/api/snippets/$SNIPPET_ID/activity \
-H "x-wallet-address: GBRPYHIL2CI3WHZDTOOQFC6EB4KJJGUJMUF6NS4ZGKYYWRYNX6YJGW7"npm test -- soft-delete
npm test -- activity-logger
npm test -- --testPathPattern=integrationis_deleted BOOLEAN DEFAULT FALSE
deleted_at TIMESTAMP
deleted_by VARCHAR(255)CREATE TABLE activity_logs (
id UUID PRIMARY KEY,
snippet_id UUID NOT NULL REFERENCES snippets(id) ON DELETE CASCADE,
action VARCHAR(50) NOT NULL,
user_wallet_address VARCHAR(255),
details JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);idx_snippets_is_deleted- Filter active snippetsidx_snippets_deleted_at- Sort deleted snippetsidx_snippets_active- Combined index for active queriesidx_activity_logs_snippet_id- Activity log queriesidx_activity_logs_action- Filter by actionidx_activity_logs_created_at- Sort by dateidx_activity_logs_user- User activity queries
- Users can only restore their own snippets
- Users can only view their own trash
- Wallet address is verified from request headers
- All delete/restore actions are logged
- Includes user wallet address and timestamp
- Immutable audit trail
✅ Soft Delete - Snippets marked as deleted, not removed ✅ Trash Management - View and manage deleted snippets ✅ Restore Functionality - Recover deleted snippets ✅ Activity Logging - Complete audit trail ✅ Ownership Verification - Secure access control ✅ Performance Optimized - Efficient queries with indexes ✅ Pagination Support - Handle large trash ✅ Error Handling - Clear error messages
SOFT_DELETE_IMPLEMENTATION.md- Complete technical documentationSOFT_DELETE_TESTING.md- Comprehensive testing guideSOFT_DELETE_FRONTEND.md- Frontend integration guideSOFT_DELETE_SUMMARY.md- Implementation summary
Solution: Verify migration ran and is_deleted column exists
SELECT COUNT(*) FROM snippets WHERE is_deleted = true;Solution: Ensure includeDeleted=true in ownership check
Solution: Verify activity_logs table exists and ActivityLogger.log() is called
- ✅ Run database migration
- ✅ Deploy backend code
- ⏳ Implement frontend components
- ⏳ Run comprehensive tests
- ⏳ Deploy to production
For detailed information, refer to:
- Technical details:
SOFT_DELETE_IMPLEMENTATION.md - Testing guide:
SOFT_DELETE_TESTING.md - Frontend guide:
SOFT_DELETE_FRONTEND.md
- Automatic cleanup (permanent delete after 30 days)
- Bulk restore/delete operations
- Advanced trash filtering
- User notifications
- Admin dashboard
- Expiration warnings