Add admin capabilities for managing users on the platform

Introduce new API endpoints and backend logic for administrative user management, including fetching, viewing, and updating user roles, while ensuring sensitive data like password hashes are excluded from responses. Implement Replit user ID formatting for internal consistency and extend storage interfaces to support user retrieval and counting.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 170e18f0-0f13-4eca-8643-546bba1dd8cc
Replit-Commit-Checkpoint-Type: full_checkpoint
This commit is contained in:
sebastjanartic
2025-09-02 12:19:53 +00:00
parent d283a0d4a5
commit 68bb99739b
3 changed files with 214 additions and 11 deletions
+109 -7
View File
@@ -96,7 +96,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
const user = await storage.createUser(userData);
// Remove password from response
const { password, ...userResponse } = user;
const { passwordHash, ...userResponse } = user;
res.status(201).json(userResponse);
} catch (error) {
if (error instanceof z.ZodError) {
@@ -123,7 +123,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
req.session.userId = user.id;
// Remove password from response
const { password: _, ...userResponse } = user;
const { passwordHash, ...userResponse } = user;
res.json(userResponse);
} catch (error) {
res.status(500).json({ message: "Failed to login" });
@@ -143,7 +143,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(404).json({ message: "User not found" });
}
const { password, ...userResponse } = user;
const { passwordHash, ...userResponse } = user;
res.json(userResponse);
} catch (error) {
res.status(500).json({ message: "Failed to fetch user" });
@@ -860,7 +860,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Auth route to get current user
app.get('/api/auth/user', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const userId = `replit_${req.user.claims.sub}`;
const user = await storage.getUser(userId);
res.json(user);
} catch (error) {
@@ -934,10 +934,54 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// User management (super admin only)
// ===== USER MANAGEMENT ROUTES (ADMIN) =====
// Get all users (admin only)
app.get('/api/admin/users', isAdmin, async (req, res) => {
try {
const limit = parseInt(req.query.limit as string) || 50;
const offset = parseInt(req.query.offset as string) || 0;
const search = req.query.search as string;
// For now, get all users - in production you'd want pagination
const users = await storage.getAllUsers ? await storage.getAllUsers(limit, offset, search) : [];
// Remove password hashes from response
const safeUsers = users.map(({ passwordHash, ...user }) => user);
res.json({
users: safeUsers,
total: users.length,
limit,
offset
});
} catch (error) {
console.error("Error fetching users:", error);
res.status(500).json({ message: "Failed to fetch users" });
}
});
// Get user by ID (admin only)
app.get('/api/admin/users/:id', isAdmin, async (req, res) => {
try {
const user = await storage.getUser(req.params.id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
// Remove password hash from response
const { passwordHash, ...safeUser } = user;
res.json(safeUser);
} catch (error) {
console.error("Error fetching user:", error);
res.status(500).json({ message: "Failed to fetch user" });
}
});
// Update user admin status (super admin only)
app.patch('/api/admin/users/:id/admin', isAuthenticated, async (req: any, res) => {
try {
const currentUserId = req.user.claims.sub;
const currentUserId = `replit_${req.user.claims.sub}`;
const currentUser = await storage.getUser(currentUserId);
if (!currentUser?.isSuperAdmin) {
@@ -952,13 +996,71 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(404).json({ message: "User not found" });
}
res.json(updatedUser);
// Remove password hash from response
const { passwordHash, ...safeUser } = updatedUser;
res.json(safeUser);
} catch (error) {
console.error("Error updating user admin status:", error);
res.status(500).json({ message: "Failed to update user" });
}
});
// Update user active status (admin only)
app.patch('/api/admin/users/:id/status', isAdmin, async (req, res) => {
try {
const userId = req.params.id;
const { isActive } = req.body;
if (typeof isActive !== 'boolean') {
return res.status(400).json({ message: "isActive must be a boolean" });
}
const updatedUser = await storage.updateUser(userId, { isActive });
if (!updatedUser) {
return res.status(404).json({ message: "User not found" });
}
// Remove password hash from response
const { passwordHash, ...safeUser } = updatedUser;
res.json(safeUser);
} catch (error) {
console.error("Error updating user status:", error);
res.status(500).json({ message: "Failed to update user status" });
}
});
// Get admin statistics (admin only)
app.get('/api/admin/stats', isAdmin, async (req, res) => {
try {
const videoCount = await storage.getVideoCount();
const userCount = await storage.getUserCount ? await storage.getUserCount() : 0;
// Get recent videos
const recentVideos = await storage.getVideos(5, 0);
// Basic stats - in production you might want more sophisticated analytics
const stats = {
videos: {
total: videoCount,
recent: recentVideos.length
},
users: {
total: userCount,
// Could add more user analytics here
},
platform: {
uptime: process.uptime(),
nodeVersion: process.version
}
};
res.json(stats);
} catch (error) {
console.error("Error fetching admin stats:", error);
res.status(500).json({ message: "Failed to fetch statistics" });
}
});
const httpServer = createServer(app);
return httpServer;
}