Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions LocalMind-Backend/src/api/v1/user/user.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ enum UserConstant {

API_KEY_REVEALED = 'API key revealed successfully',
API_KEY_REVEAL_FAILED = 'Failed to reveal API key',

// ✅ ADMIN OPERATIONS
ADMIN_STATS_SUCCESS = 'Admin statistics fetched successfully',
ADMIN_STATS_FAILED = 'Failed to fetch admin statistics',
ADMIN_ONLY = 'Access denied. Admin privileges required',
}

export default UserConstant
Expand Down
11 changes: 11 additions & 0 deletions LocalMind-Backend/src/api/v1/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class UserController {
this.profile = this.profile.bind(this)
this.apiEndPointCreater = this.apiEndPointCreater.bind(this)
this.getApiKey = this.getApiKey.bind(this)
this.getAdminStats = this.getAdminStats.bind(this)
}

private setHeaderToken(res: Response, token: string): void {
Expand Down Expand Up @@ -158,6 +159,16 @@ class UserController {
SendResponse.error(res, err.message || UserConstant.SERVER_ERROR, 500, err)
}
}

async getAdminStats(req: Request, res: Response): Promise<void> {
try {
const stats = await userService.getAdminStats()

SendResponse.success(res, UserConstant.ADMIN_STATS_SUCCESS, stats, StatusConstant.OK)
} catch (err: any) {
SendResponse.error(res, err.message || UserConstant.ADMIN_STATS_FAILED, StatusConstant.INTERNAL_SERVER_ERROR, err)
}
}
}

export default new UserController()
25 changes: 25 additions & 0 deletions LocalMind-Backend/src/api/v1/user/user.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,31 @@ class UserMiddleware {
SendResponse.error(res, err.message || UserConstant.SERVER_ERROR, 500)
}
}

async adminMiddleware(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const token = req.headers.authorization?.split(' ')[1] || req.cookies?.token

if (!token) {
throw new Error(UserConstant.TOKEN_MISSING)
}

const decodedData: Partial<IUser> | null = UserUtils.verifyToken(token)

if (!decodedData) {
throw new Error(UserConstant.INVALID_TOKEN)
}

if (decodedData.role !== 'admin') {
throw new Error(UserConstant.ADMIN_ONLY)
}

req.user = decodedData
return next()
} catch (err: any) {
SendResponse.error(res, err.message || UserConstant.SERVER_ERROR, err.message === UserConstant.ADMIN_ONLY ? 403 : 500)
}
}
}

export default new UserMiddleware()
3 changes: 3 additions & 0 deletions LocalMind-Backend/src/api/v1/user/user.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ router.get('/v1/auth/apiKey/generate', userMiddleware.middleware, userController
router.get('/v1/auth/profile', userMiddleware.middleware, userController.profile)
router.get('/v1/auth/apiKey', userMiddleware.middleware, userController.getApiKey)

// Admin routes
router.get('/v1/admin/stats', userMiddleware.adminMiddleware, userController.getAdminStats)

// router.post("v1/user/apikey/reveal", userMiddleware.middleware, UserController.revealApiKey);

export { router as userRoutes }
24 changes: 24 additions & 0 deletions LocalMind-Backend/src/api/v1/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,29 @@ class userService {
throw new Error(err.message)
}
}

async getAdminStats() {
try {
const totalUsers = await User.countDocuments()
const adminUsers = await User.countDocuments({ role: 'admin' })
const regularUsers = await User.countDocuments({ role: 'user' })
const creatorUsers = await User.countDocuments({ role: 'creator' })

const recentUsers = await User.find()
.select('firstName email role createdAt')
.sort({ createdAt: -1 })
.limit(10)

return {
totalUsers,
adminUsers,
regularUsers,
creatorUsers,
recentUsers,
}
} catch (err: any) {
throw new Error(err.message)
}
}
}
export default new userService()
13 changes: 13 additions & 0 deletions LocalMind-Frontend/src/app/routes/AdminRoutes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'
import { Route, Routes } from 'react-router-dom'
import AdminDashboard from '../../features/Admin/V1/Component/Pages/AdminDashboard'

const AdminRoutes: React.FC = () => {
return (
<Routes>
<Route path="/admin/dashboard" element={<AdminDashboard />} />
</Routes>
)
}

export default AdminRoutes
4 changes: 4 additions & 0 deletions LocalMind-Frontend/src/app/routes/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import { Route, Routes } from 'react-router-dom'
import HomePage from '../../features/Dashboard/V1/Component/Pages/HomePage'
import LoginPage from '../../shared/component/v1/LoginPage'
import AdminDashboard from '../../features/Admin/V1/Component/Pages/AdminDashboard'

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

{/* Admin Dashboard */}
<Route path="/admin/dashboard" element={<AdminDashboard />} />

{/* Chat Page */}
</Routes>
)
Expand Down
Loading