Skip to content

Commit 638042d

Browse files
feat: Add admin dashboard foundation with user statistics
- Add adminMiddleware for role-based access control - Create GET /api/v1/admin/stats endpoint for admin statistics - Implement getAdminStats service to count users by role - Add admin dashboard UI with user statistics cards - Display total users, admins, regular users, and creators - Show recent 10 registered users in table format - Add proper error handling and authentication checks - Update routes to include admin dashboard path
1 parent eca5db1 commit 638042d

8 files changed

Lines changed: 368 additions & 0 deletions

File tree

LocalMind-Backend/src/api/v1/user/user.constant.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ enum UserConstant {
8787

8888
API_KEY_REVEALED = 'API key revealed successfully',
8989
API_KEY_REVEAL_FAILED = 'Failed to reveal API key',
90+
91+
// ✅ ADMIN OPERATIONS
92+
ADMIN_STATS_SUCCESS = 'Admin statistics fetched successfully',
93+
ADMIN_STATS_FAILED = 'Failed to fetch admin statistics',
94+
ADMIN_ONLY = 'Access denied. Admin privileges required',
9095
}
9196

9297
export default UserConstant

LocalMind-Backend/src/api/v1/user/user.controller.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class UserController {
1515
this.profile = this.profile.bind(this)
1616
this.apiEndPointCreater = this.apiEndPointCreater.bind(this)
1717
this.getApiKey = this.getApiKey.bind(this)
18+
this.getAdminStats = this.getAdminStats.bind(this)
1819
}
1920

2021
private setHeaderToken(res: Response, token: string): void {
@@ -158,6 +159,16 @@ class UserController {
158159
SendResponse.error(res, err.message || UserConstant.SERVER_ERROR, 500, err)
159160
}
160161
}
162+
163+
async getAdminStats(req: Request, res: Response): Promise<void> {
164+
try {
165+
const stats = await userService.getAdminStats()
166+
167+
SendResponse.success(res, UserConstant.ADMIN_STATS_SUCCESS, stats, StatusConstant.OK)
168+
} catch (err: any) {
169+
SendResponse.error(res, err.message || UserConstant.ADMIN_STATS_FAILED, StatusConstant.INTERNAL_SERVER_ERROR, err)
170+
}
171+
}
161172
}
162173

163174
export default new UserController()

LocalMind-Backend/src/api/v1/user/user.middleware.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,31 @@ class UserMiddleware {
2424
SendResponse.error(res, err.message || UserConstant.SERVER_ERROR, 500)
2525
}
2626
}
27+
28+
async adminMiddleware(req: Request, res: Response, next: NextFunction): Promise<void> {
29+
try {
30+
const token = req.headers.authorization?.split(' ')[1] || req.cookies?.token
31+
32+
if (!token) {
33+
throw new Error(UserConstant.TOKEN_MISSING)
34+
}
35+
36+
const decodedData: Partial<IUser> | null = UserUtils.verifyToken(token)
37+
38+
if (!decodedData) {
39+
throw new Error(UserConstant.INVALID_TOKEN)
40+
}
41+
42+
if (decodedData.role !== 'admin') {
43+
throw new Error(UserConstant.ADMIN_ONLY)
44+
}
45+
46+
req.user = decodedData
47+
return next()
48+
} catch (err: any) {
49+
SendResponse.error(res, err.message || UserConstant.SERVER_ERROR, err.message === UserConstant.ADMIN_ONLY ? 403 : 500)
50+
}
51+
}
2752
}
2853

2954
export default new UserMiddleware()

LocalMind-Backend/src/api/v1/user/user.routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ router.get('/v1/auth/apiKey/generate', userMiddleware.middleware, userController
1212
router.get('/v1/auth/profile', userMiddleware.middleware, userController.profile)
1313
router.get('/v1/auth/apiKey', userMiddleware.middleware, userController.getApiKey)
1414

15+
// Admin routes
16+
router.get('/v1/admin/stats', userMiddleware.adminMiddleware, userController.getAdminStats)
17+
1518
// router.post("v1/user/apikey/reveal", userMiddleware.middleware, UserController.revealApiKey);
1619

1720
export { router as userRoutes }

LocalMind-Backend/src/api/v1/user/user.service.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,29 @@ class userService {
5050
throw new Error(err.message)
5151
}
5252
}
53+
54+
async getAdminStats() {
55+
try {
56+
const totalUsers = await User.countDocuments()
57+
const adminUsers = await User.countDocuments({ role: 'admin' })
58+
const regularUsers = await User.countDocuments({ role: 'user' })
59+
const creatorUsers = await User.countDocuments({ role: 'creator' })
60+
61+
const recentUsers = await User.find()
62+
.select('firstName email role createdAt')
63+
.sort({ createdAt: -1 })
64+
.limit(10)
65+
66+
return {
67+
totalUsers,
68+
adminUsers,
69+
regularUsers,
70+
creatorUsers,
71+
recentUsers,
72+
}
73+
} catch (err: any) {
74+
throw new Error(err.message)
75+
}
76+
}
5377
}
5478
export default new userService()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react'
2+
import { Route, Routes } from 'react-router-dom'
3+
import AdminDashboard from '../../features/Admin/V1/Component/Pages/AdminDashboard'
4+
5+
const AdminRoutes: React.FC = () => {
6+
return (
7+
<Routes>
8+
<Route path="/admin/dashboard" element={<AdminDashboard />} />
9+
</Routes>
10+
)
11+
}
12+
13+
export default AdminRoutes

LocalMind-Frontend/src/app/routes/AppRoutes.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react'
22
import { Route, Routes } from 'react-router-dom'
33
import HomePage from '../../features/Dashboard/V1/Component/Pages/HomePage'
44
import LoginPage from '../../shared/component/v1/LoginPage'
5+
import AdminDashboard from '../../features/Admin/V1/Component/Pages/AdminDashboard'
56

67
const AppRoutes: React.FC = () => {
78
return (
@@ -18,6 +19,9 @@ const AppRoutes: React.FC = () => {
1819
{/* Forgot Password Page - TODO: Create ForgotPasswordPage component */}
1920
<Route path="/forgot-password" element={<LoginPage />} />
2021

22+
{/* Admin Dashboard */}
23+
<Route path="/admin/dashboard" element={<AdminDashboard />} />
24+
2125
{/* Chat Page */}
2226
</Routes>
2327
)

0 commit comments

Comments
 (0)