Users API Endpoints¶
Version: 1.0.0
Overview¶
The Users API provides comprehensive admin functionality for managing user accounts, profiles, and wallets within the Saga platform. This API enables administrators to perform CRUD operations on users, search for accounts, manage wallets, and clean up test data.
Key Features¶
- User Management: Complete CRUD operations for user accounts
- Wallet Integration: View and manage user wallets (personal and deposit addresses)
- Search & Discovery: Email-based search with autocomplete suggestions
- Admin Profile: Retrieve current administrator profile information
- Test Data Management: Cleanup utilities for development environments
- Canonical Architecture: Uses ComprehensiveUserService for single source of truth
Architecture¶
Clean Architecture Layers:
AdminUserManagementRouter- HTTP routing and request handlingComprehensiveUserService(Canonical) - Business logic for user operationsCanonicalUserRepository- Data persistence layerAdminService- Legacy admin operations (being phased out)
Security:
- All endpoints require admin JWT token authentication
- Admin middleware enforced at router level
- Test data cleanup restricted to development environments only
Table of Contents¶
Admin Endpoints¶
- Get Users List
- Create User
- Get User Details
- Update User
- Get User Wallets
- Get Admin Profile
- Search Users by Email
- Delete Test Data
- Cleanup Test Data
Use Cases¶
Admin Endpoints¶
1. Get Users List: `GET /api/admin/users`¶
Retrieves paginated list of all users with optional filtering by status and email search. Supports querying specific user by ID through `userId` parameter.
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
- Header: `Authorization: Bearer
`
Query Parameters¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `page` | integer | No | 1 | Page number (1-indexed) |
| `limit` | integer | No | 50 | Results per page (max 100) |
| `status` | string | No | - | Filter by user status (active, inactive, suspended) |
| `search` | string | No | - | Search by email (partial match) |
| `userId` | string | No | - | Get specific user by ID (overrides pagination) |
Response: Success (200 OK)¶
{
"success": true,
"data": {
"users": [
{
"id": "user_abc123",
"email": "user@example.com",
"status": "active",
"role": "user",
"totalInvestments": 0,
"totalAmount": "0.00",
"createdAt": "2025-10-06T10:00:00Z",
"updatedAt": "2025-10-06T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 100,
"totalPages": 2
}
},
"message": "Список пользователей",
"trace_id": "trace_xyz789"
}
Response: User by ID (200 OK)¶
{
"success": true,
"data": {
"users": [
{
"id": "user_abc123",
"email": "user@example.com",
"status": "active",
"role": "user",
"totalInvestments": 0,
"totalAmount": "0.00",
"createdAt": "2025-10-06T10:00:00Z",
"updatedAt": "2025-10-06T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 1,
"total": 1,
"totalPages": 1
}
},
"message": "Пользователь найден"
}
cURL Examples¶
# Get all users with pagination
curl -X GET "https://api.saga.surf/api/admin/users?page=1&limit=20" \\
-H "Authorization: Bearer <admin_token>"
# Filter by status
curl -X GET "https://api.saga.surf/api/admin/users?status=active&page=1&limit=50" \\
-H "Authorization: Bearer <admin_token>"
# Search by email
curl -X GET "https://api.saga.surf/api/admin/users?search=john&page=1" \\
-H "Authorization: Bearer <admin_token>"
# Get specific user by ID
curl -X GET "https://api.saga.surf/api/admin/users?userId=user_abc123" \\
-H "Authorization: Bearer <admin_token>"
TypeScript/React Example¶
interface AdminUser {
id: string;
email: string;
status: string;
role: string;
totalInvestments: number;
totalAmount: string;
createdAt: string;
updatedAt: string;
}
interface GetUsersParams {
page?: number;
limit?: number;
status?: string;
search?: string;
userId?: string;
}
const getUsers = async (
params: GetUsersParams = {}
): Promise<{ users: AdminUser[]; pagination: any }> => {
const adminToken = localStorage.getItem('admin_auth_token');
const queryParams = new URLSearchParams();
if (params.page) queryParams.set('page', params.page.toString());
if (params.limit) queryParams.set('limit', params.limit.toString());
if (params.status) queryParams.set('status', params.status);
if (params.search) queryParams.set('search', params.search);
if (params.userId) queryParams.set('userId', params.userId);
const response = await fetch(
\`/api/admin/users?\${queryParams}\`,
{
headers: {
'Authorization': \`Bearer \${adminToken}\`
}
}
);
const { data } = await response.json();
return data;
};
2. Create User: `POST /api/admin/users`¶
Creates a new user account (primarily for testing purposes). Uses canonical ComprehensiveUserService for user creation.
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Request Body¶
{
"id": "user_test_123",
"email": "newuser@saga-test.com",
"status": "active",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}
Fields:
- `id` (string, optional): User ID (auto-generated if not provided)
- `email` (string, required): User email address
- `status` (string, optional): Initial status (default: "active")
- `walletAddress` (string, optional): Ethereum wallet address
Response: Success (201 Created)¶
{
"success": true,
"data": {
"id": "user_test_123",
"email": "newuser@saga-test.com",
"status": "active",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"createdAt": "2025-10-06T10:30:00Z",
"updatedAt": "2025-10-06T10:30:00Z",
"profile": null,
"permissions": []
},
"message": "Пользователь создан через canonical service",
"trace_id": "trace_abc123"
}
Response: Validation Error (400 Bad Request)¶
{
"success": false,
"error": {
"message": "Ошибка валидации данных пользователя",
"code": "VALIDATION_ERROR",
"details": "Email обязателен"
}
}
cURL Example¶
curl -X POST https://api.saga.surf/api/admin/users \\
-H "Authorization: Bearer <admin_token>" \\
-H "Content-Type: application/json" \\
-d '{
"email": "newuser@saga-test.com",
"status": "active",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}'
TypeScript/React Example¶
interface CreateUserRequest {
email: string;
status?: string;
walletAddress?: string;
}
const createUser = async (
request: CreateUserRequest
): Promise<any> => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch('/api/admin/users', {
method: 'POST',
headers: {
'Authorization': \`Bearer \${adminToken}\`,
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Failed to create user');
}
const { data } = await response.json();
return data;
};
3. Get User Details: `GET /api/admin/users/:id`¶
Retrieves detailed information about a specific user by ID.
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Path Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | string | Yes | User ID |
Response: Success (200 OK)¶
{
"success": true,
"data": {
"user": {
"id": "user_abc123",
"email": "user@example.com",
"status": "active",
"walletAddress": "",
"createdAt": "2025-10-06T10:00:00Z",
"updatedAt": "2025-10-06T10:00:00Z"
}
},
"message": "Информация о пользователе получена"
}
Response: Not Found (404)¶
cURL Example¶
curl -X GET https://api.saga.surf/api/admin/users/user_abc123 \\
-H "Authorization: Bearer <admin_token>"
4. Update User: `PUT /api/admin/users/:id`¶
Updates user information (email and/or status).
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Path Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | string | Yes | User ID |
Request Body¶
Fields (all optional):
- `email` (string): New email address
- `status` (string): New status (active, inactive, suspended)
Response: Success (200 OK)¶
{
"success": true,
"data": {
"userId": "user_abc123",
"email": "newemail@example.com",
"updatedProfile": {
"id": "user_abc123",
"email": "newemail@example.com",
"status": "active",
"walletAddress": "",
"createdAt": "2025-10-06T10:00:00Z",
"updatedAt": "2025-10-06T11:00:00Z",
"profile": null,
"permissions": []
}
},
"message": "Пользователь успешно обновлен"
}
Response: Not Found (404)¶
cURL Example¶
curl -X PUT https://api.saga.surf/api/admin/users/user_abc123 \\
-H "Authorization: Bearer <admin_token>" \\
-H "Content-Type: application/json" \\
-d '{
"email": "newemail@example.com",
"status": "active"
}'
TypeScript/React Example¶
interface UpdateUserRequest {
email?: string;
status?: string;
}
const updateUser = async (
userId: string,
request: UpdateUserRequest
): Promise<any> => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch(\`/api/admin/users/\${userId}\`, {
method: 'PUT',
headers: {
'Authorization': \`Bearer \${adminToken}\`,
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Failed to update user');
}
const { data } = await response.json();
return data;
};
5. Get User Wallets: `GET /api/admin/users/:id/wallets`¶
Retrieves all wallets associated with a specific user (personal wallets and deposit addresses).
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Path Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
| `id` | string | Yes | User ID |
Response: Success (200 OK)¶
{
"success": true,
"data": {
"user": {
"id": "user_abc123",
"email": "user@example.com",
"status": "active",
"personalWallets": [
{
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"networkId": 1337,
"type": "personal",
"createdAt": "2025-10-06T10:00:00Z"
}
],
"depositWallets": [
{
"address": "0x123456789abcdef...",
"networkId": 1337,
"type": "deposit",
"derivationPath": "m/44'/60'/0'/0/0",
"addressIndex": 0,
"createdAt": "2025-10-06T10:00:00Z"
}
]
}
},
"message": "Информация о пользователе и кошельках получена"
}
Response: Not Found (404)¶
cURL Example¶
curl -X GET https://api.saga.surf/api/admin/users/user_abc123/wallets \\
-H "Authorization: Bearer <admin_token>"
TypeScript/React Example¶
interface Wallet {
address: string;
networkId: number;
type: string;
derivationPath?: string;
addressIndex?: number;
createdAt: string;
}
interface UserWithWallets {
id: string;
email: string;
status: string;
personalWallets: Wallet[];
depositWallets: Wallet[];
}
const getUserWallets = async (
userId: string
): Promise<UserWithWallets> => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch(
\`/api/admin/users/\${userId}/wallets\`,
{
headers: {
'Authorization': \`Bearer \${adminToken}\`
}
}
);
const { data } = await response.json();
return data.user;
};
6. Get Admin Profile: `GET /api/admin/profile`¶
Retrieves the profile information of the currently authenticated administrator.
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Response: Success (200 OK)¶
{
"success": true,
"data": {
"id": "admin_123",
"email": "admin@saga.surf",
"role": "superadmin",
"permissions": [
"users:read",
"users:write",
"investments:read",
"investments:write"
],
"status": "active",
"createdAt": "<RFC3339_TIMESTAMP>",
"updatedAt": "<RFC3339_TIMESTAMP>"
},
"message": "Admin profile retrieved successfully"
}
Response: Unauthorized (401)¶
{
"success": false,
"error": {
"message": "Требуется администраторская авторизация",
"code": "UNAUTHORIZED"
}
}
cURL Example¶
TypeScript/React Example¶
interface AdminProfile {
id: string;
email: string;
role: string;
permissions: string[];
status: string;
createdAt: string;
updatedAt: string;
}
const getAdminProfile = async (): Promise<AdminProfile> => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch('/api/admin/profile', {
headers: {
'Authorization': \`Bearer \${adminToken}\`
}
});
if (!response.ok) {
throw new Error('Failed to fetch admin profile');
}
const { data } = await response.json();
return data;
};
// Usage in component
const AdminProfileCard: React.FC = () => {
const [profile, setProfile] = useState<AdminProfile | null>(null);
useEffect(() => {
const fetchProfile = async () => {
try {
const data = await getAdminProfile();
setProfile(data);
} catch (error) {
console.error('Failed to load profile:', error);
}
};
fetchProfile();
}, []);
if (!profile) return <div>Loading...</div>;
return (
<div className="admin-profile">
<h2>{profile.email}</h2>
<p>Role: {profile.role}</p>
<p>Permissions: {profile.permissions.join(', ')}</p>
</div>
);
};
7. Search Users by Email: `GET /api/admin/search/users`¶
Searches for users by email with autocomplete suggestions (maximum 10 results).
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Query Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
| `email` | string | Yes | Email search query (minimum 1 character) |
Response: Success (200 OK)¶
{
"success": true,
"data": {
"suggestions": [
{
"id": "user_abc123",
"email": "john@example.com",
"label": "john@example.com (ID: user_abc...)"
},
{
"id": "user_xyz789",
"email": "johnny@example.com",
"label": "johnny@example.com (ID: user_xyz...)"
}
],
"count": 2
},
"message": "Email suggestions retrieved"
}
Response: Validation Error (400 Bad Request)¶
cURL Example¶
curl -X GET "https://api.saga.surf/api/admin/search/users?email=john" \\
-H "Authorization: Bearer <admin_token>"
TypeScript/React Example¶
interface EmailSuggestion {
id: string;
email: string;
label: string;
}
const searchUsersByEmail = async (
email: string
): Promise<EmailSuggestion[]> => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch(
\`/api/admin/search/users?email=\${encodeURIComponent(email)}\`,
{
headers: {
'Authorization': \`Bearer \${adminToken}\`
}
}
);
const { data } = await response.json();
return data.suggestions;
};
// Autocomplete component
const UserEmailAutocomplete: React.FC = () => {
const [query, setQuery] = useState('');
const [suggestions, setSuggestions] = useState<EmailSuggestion[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchSuggestions = async () => {
if (query.length < 1) {
setSuggestions([]);
return;
}
setLoading(true);
try {
const results = await searchUsersByEmail(query);
setSuggestions(results);
} catch (error) {
console.error('Search failed:', error);
} finally {
setLoading(false);
}
};
const debounce = setTimeout(fetchSuggestions, 300);
return () => clearTimeout(debounce);
}, [query]);
return (
<div className="autocomplete">
<input
type="text"
placeholder="Search by email..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{loading && <div>Loading...</div>}
{suggestions.length > 0 && (
<ul className="suggestions">
{suggestions.map((s) => (
<li key={s.id} onClick={() => setQuery(s.email)}>
{s.label}
</li>
))}
</ul>
)}
</div>
);
};
8. Delete Test Data: `DELETE /api/admin/test-data`¶
Deletes all test users from the database (development environments only).
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Security¶
- Restricted: Only available in development environments
- Protection: Returns 403 Forbidden in production
Response: Success (200 OK)¶
Response: Production Protection (403 Forbidden)¶
{
"success": false,
"error": {
"message": "Операция разрешена только в тестовом окружении",
"code": "PRODUCTION_PROTECTION",
"details": "Cleanup операции недоступны в production для защиты данных"
}
}
cURL Example¶
curl -X DELETE https://api.saga.surf/api/admin/test-data \\
-H "Authorization: Bearer <admin_token>"
9. Cleanup Test Data: `POST|DELETE /api/admin/cleanup/test-data`¶
Alternative endpoint for test data cleanup (compatibility with E2E tests). Supports both POST and DELETE methods.
Authentication¶
- Required: Yes (Admin JWT Token)
- Type: Admin Token
Security¶
Same as endpoint #8 - restricted to development environments only.
Response: Success (200 OK)¶
cURL Example¶
# Using POST
curl -X POST https://api.saga.surf/api/admin/cleanup/test-data \\
-H "Authorization: Bearer <admin_token>"
# Using DELETE
curl -X DELETE https://api.saga.surf/api/admin/cleanup/test-data \\
-H "Authorization: Bearer <admin_token>"
Use Cases¶
Use Case 1: Complete User Management Dashboard¶
Full-featured admin dashboard for managing users with search, CRUD operations, and wallet viewing.
import { useState, useEffect } from 'react';
// ============= Types =============
interface AdminUser {
id: string;
email: string;
status: string;
role: string;
totalInvestments: number;
totalAmount: string;
createdAt: string;
updatedAt: string;
}
interface Wallet {
address: string;
networkId: number;
type: string;
derivationPath?: string;
addressIndex?: number;
createdAt: string;
}
interface UserWithWallets {
id: string;
email: string;
status: string;
personalWallets: Wallet[];
depositWallets: Wallet[];
}
// ============= API Functions =============
const userAPI = {
getUsers: async (
page: number = 1,
limit: number = 20,
filters: { status?: string; search?: string } = {}
) => {
const adminToken = localStorage.getItem('admin_auth_token');
const params = new URLSearchParams({
page: page.toString(),
limit: limit.toString()
});
if (filters.status) params.set('status', filters.status);
if (filters.search) params.set('search', filters.search);
const response = await fetch(\`/api/admin/users?\${params}\`, {
headers: { 'Authorization': \`Bearer \${adminToken}\` }
});
const { data } = await response.json();
return data;
},
createUser: async (email: string, walletAddress?: string) => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch('/api/admin/users', {
method: 'POST',
headers: {
'Authorization': \`Bearer \${adminToken}\`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, walletAddress, status: 'active' })
});
if (!response.ok) throw new Error('Failed to create user');
const { data } = await response.json();
return data;
},
updateUser: async (
userId: string,
updates: { email?: string; status?: string }
) => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch(\`/api/admin/users/\${userId}\`, {
method: 'PUT',
headers: {
'Authorization': \`Bearer \${adminToken}\`,
'Content-Type': 'application/json'
},
body: JSON.stringify(updates)
});
if (!response.ok) throw new Error('Failed to update user');
const { data } = await response.json();
return data;
},
getUserWallets: async (userId: string): Promise<UserWithWallets> => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch(\`/api/admin/users/\${userId}/wallets\`, {
headers: { 'Authorization': \`Bearer \${adminToken}\` }
});
const { data } = await response.json();
return data.user;
},
searchUsers: async (email: string) => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch(
\`/api/admin/search/users?email=\${encodeURIComponent(email)}\`,
{
headers: { 'Authorization': \`Bearer \${adminToken}\` }
}
);
const { data } = await response.json();
return data.suggestions;
},
deleteTestData: async () => {
const adminToken = localStorage.getItem('admin_auth_token');
const response = await fetch('/api/admin/test-data', {
method: 'DELETE',
headers: { 'Authorization': \`Bearer \${adminToken}\` }
});
if (!response.ok) throw new Error('Failed to delete test data');
const { data } = await response.json();
return data;
}
};
// ============= Main Dashboard Component =============
const UserManagementDashboard: React.FC = () => {
const [users, setUsers] = useState<AdminUser[]>([]);
const [pagination, setPagination] = useState<any>(null);
const [loading, setLoading] = useState(false);
const [selectedUser, setSelectedUser] = useState<string | null>(null);
const [walletsModalOpen, setWalletsModalOpen] = useState(false);
const [userWallets, setUserWallets] = useState<UserWithWallets | null>(null);
// Filters
const [page, setPage] = useState(1);
const [statusFilter, setStatusFilter] = useState('');
const [searchQuery, setSearchQuery] = useState('');
useEffect(() => {
loadUsers();
}, [page, statusFilter, searchQuery]);
const loadUsers = async () => {
setLoading(true);
try {
const data = await userAPI.getUsers(page, 20, {
status: statusFilter,
search: searchQuery
});
setUsers(data.users);
setPagination(data.pagination);
} catch (error) {
console.error('Failed to load users:', error);
} finally {
setLoading(false);
}
};
const handleViewWallets = async (userId: string) => {
try {
const wallets = await userAPI.getUserWallets(userId);
setUserWallets(wallets);
setWalletsModalOpen(true);
} catch (error) {
console.error('Failed to load wallets:', error);
}
};
const handleUpdateUser = async (
userId: string,
updates: { email?: string; status?: string }
) => {
try {
await userAPI.updateUser(userId, updates);
alert('✅ User updated successfully');
loadUsers();
} catch (error: any) {
alert(\`❌ Error: \${error.message}\`);
}
};
const handleCreateUser = async () => {
const email = prompt('Enter new user email:');
if (!email) return;
try {
await userAPI.createUser(email);
alert('✅ User created successfully');
loadUsers();
} catch (error: any) {
alert(\`❌ Error: \${error.message}\`);
}
};
const handleDeleteTestData = async () => {
if (!confirm('⚠️ Delete all test users? This cannot be undone.')) return;
try {
await userAPI.deleteTestData();
alert('✅ Test data deleted successfully');
loadUsers();
} catch (error: any) {
alert(\`❌ Error: \${error.message}\`);
}
};
return (
<div className="user-management-dashboard">
<header className="dashboard-header">
<h1>👥 User Management</h1>
<div className="header-actions">
<button onClick={handleCreateUser} className="btn-primary">
➕ Create User
</button>
<button onClick={handleDeleteTestData} className="btn-danger">
🗑️ Delete Test Data
</button>
</div>
</header>
<div className="filters">
<input
type="text"
placeholder="Search by email..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="search-input"
/>
<select
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
className="status-filter"
>
<option value="">All Statuses</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
<option value="suspended">Suspended</option>
</select>
</div>
{loading ? (
<div className="loading">Loading users...</div>
) : (
<>
<table className="users-table">
<thead>
<tr>
<th>Email</th>
<th>Status</th>
<th>Investments</th>
<th>Total Amount</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user.id}>
<td>{user.email}</td>
<td>
<span className={\`status-badge status-\${user.status}\`}>
{user.status}
</span>
</td>
<td>{user.totalInvestments}</td>
<td>\${user.totalAmount}</td>
<td>{new Date(user.createdAt).toLocaleDateString()}</td>
<td>
<button
onClick={() => handleViewWallets(user.id)}
className="btn-small"
>
🔐 Wallets
</button>
<button
onClick={() => {
const email = prompt('New email:', user.email);
if (email) handleUpdateUser(user.id, { email });
}}
className="btn-small"
>
✏️ Edit
</button>
</td>
</tr>
))}
</tbody>
</table>
{pagination && (
<div className="pagination">
<button
onClick={() => setPage(page - 1)}
disabled={page === 1}
>
← Previous
</button>
<span>
Page {pagination.page} of {pagination.totalPages}
</span>
<button
onClick={() => setPage(page + 1)}
disabled={page === pagination.totalPages}
>
Next →
</button>
</div>
)}
</>
)}
{walletsModalOpen && userWallets && (
<div className="modal">
<div className="modal-content">
<h2>Wallets for {userWallets.email}</h2>
<h3>Personal Wallets</h3>
{userWallets.personalWallets.length === 0 ? (
<p>No personal wallets</p>
) : (
<ul>
{userWallets.personalWallets.map((w, i) => (
<li key={i}>
{w.address} (Network: {w.networkId})
</li>
))}
</ul>
)}
<h3>Deposit Wallets</h3>
{userWallets.depositWallets.length === 0 ? (
<p>No deposit wallets</p>
) : (
<ul>
{userWallets.depositWallets.map((w, i) => (
<li key={i}>
{w.address} (Index: {w.addressIndex})
</li>
))}
</ul>
)}
<button onClick={() => setWalletsModalOpen(false)}>
Close
</button>
</div>
</div>
)}
</div>
);
};
export default UserManagementDashboard;
CSS Styling:
.user-management-dashboard {
padding: 20px;
max-width: 1400px;
margin: 0 auto;
}
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.dashboard-header h1 {
margin: 0;
font-size: 28px;
}
.header-actions {
display: flex;
gap: 10px;
}
.btn-primary {
padding: 10px 20px;
background: #4169E1;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
.btn-danger {
padding: 10px 20px;
background: #DC143C;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
.filters {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.search-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 6px;
}
.status-filter {
padding: 10px;
border: 1px solid #ddd;
border-radius: 6px;
min-width: 150px;
}
.users-table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.users-table th,
.users-table td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.users-table th {
background: #f8f9fa;
font-weight: 600;
color: #333;
}
.status-badge {
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-active {
background: #D4EDDA;
color: #155724;
}
.status-inactive {
background: #F8D7DA;
color: #721C24;
}
.status-suspended {
background: #FFF3CD;
color: #856404;
}
.btn-small {
padding: 5px 10px;
margin-right: 5px;
border: none;
border-radius: 4px;
background: #6c757d;
color: white;
cursor: pointer;
font-size: 12px;
}
.btn-small:hover {
background: #5a6268;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin-top: 20px;
}
.pagination button {
padding: 8px 16px;
border: 1px solid #ddd;
background: white;
border-radius: 4px;
cursor: pointer;
}
.pagination button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 30px;
border-radius: 8px;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.modal-content h2 {
margin-top: 0;
}
.modal-content h3 {
margin-top: 20px;
margin-bottom: 10px;
}
.modal-content ul {
list-style: none;
padding: 0;
}
.modal-content li {
padding: 10px;
background: #f8f9fa;
margin-bottom: 8px;
border-radius: 4px;
font-family: monospace;
font-size: 14px;
}
.modal-content button {
margin-top: 20px;
padding: 10px 20px;
background: #6c757d;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
Technical Details¶
Authentication¶
All endpoints require admin JWT token authentication:
- Admin Token: Generated through admin authentication flow
- Header: `Authorization: Bearer
Canonical Architecture¶
ComprehensiveUserService:
- Single source of truth for user operations
- Eliminates duplicate CreateUser implementations
- Provides consistent validation and error handling
- Clean Architecture pattern: Router → Service → Repository
Error Handling¶
Common HTTP Status Codes:
- `200 OK`: Successful operation
- `201 Created`: User created
- `400 Bad Request`: Validation error
- `401 Unauthorized`: Missing or invalid JWT token
- `403 Forbidden`: Production protection (test data cleanup)
- `404 Not Found`: User not found
- `500 Internal Server Error`: Server error
Error Response Format:
{
"success": false,
"error": {
"message": "Human-readable error message",
"code": "ERROR_CODE",
"details": "Additional context"
},
"trace_id": "trace_abc123"
}
Security Features¶
- Admin-Only Access: All endpoints require admin authentication
- Production Protection: Test data cleanup restricted to development
- Validation: Email and user data validated at service level
- Audit Trail: Admin ID tracked for all operations
- Permission-Based: Future support for fine-grained permissions
Database Schema¶
Users Table:
CREATE TABLE users (
id VARCHAR(255) PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
status VARCHAR(50) NOT NULL,
wallet_address VARCHAR(255),
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
Wallets Table:
CREATE TABLE wallets (
address VARCHAR(255) PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
network_id INTEGER NOT NULL,
type VARCHAR(50) NOT NULL,
derivation_path VARCHAR(255),
address_index INTEGER,
created_at TIMESTAMP NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
Related Endpoints¶
- Auth API - Admin JWT token generation
- Transactions API - User transaction history
- Withdrawals API - User withdrawal management
Changelog¶
Version 1.0.0 (2025-10-06)¶
- Initial documentation release
- 9 admin endpoints documented
- Complete user management coverage
- Canonical architecture integration
- Comprehensive dashboard implementation
- Production protection for test data cleanup
📋 Метаданные¶
Версия: 2.4.82
Обновлено: 2025-10-21
Статус: Published