Add admin dashboard and Replit authentication integration
Integrates Replit authentication using OpenID Connect, adds an admin dashboard route with video management and thumbnail upload capabilities, and updates dependencies. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 890577b1-c154-40a4-a177-a0c6d55320c3 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/890577b1-c154-40a4-a177-a0c6d55320c3/1jMBtLj
This commit is contained in:
@@ -16,6 +16,8 @@ import fs from "fs";
|
||||
import session from "express-session";
|
||||
import sharp from "sharp";
|
||||
import fetch from "node-fetch";
|
||||
import { setupAuth, isAuthenticated, isAdmin } from "./replitAuth";
|
||||
import { ObjectStorageService, ObjectNotFoundError } from "./objectStorage";
|
||||
|
||||
// Extend express session
|
||||
declare module "express-session" {
|
||||
@@ -56,6 +58,9 @@ const authenticate = (req: Request, res: Response, next: any) => {
|
||||
};
|
||||
|
||||
export async function registerRoutes(app: Express): Promise<Server> {
|
||||
// Setup Replit Auth first
|
||||
await setupAuth(app);
|
||||
|
||||
// Add compression middleware for better performance
|
||||
app.use(compression({
|
||||
level: 6,
|
||||
@@ -850,6 +855,110 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
});
|
||||
|
||||
// ===== ADMIN ROUTES =====
|
||||
|
||||
// Auth route to get current user
|
||||
app.get('/api/auth/user', isAuthenticated, async (req: any, res) => {
|
||||
try {
|
||||
const userId = req.user.claims.sub;
|
||||
const user = await storage.getUser(userId);
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
console.error("Error fetching user:", error);
|
||||
res.status(500).json({ message: "Failed to fetch user" });
|
||||
}
|
||||
});
|
||||
|
||||
// Admin video management
|
||||
app.get('/api/admin/videos', 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;
|
||||
|
||||
const videos = await storage.getVideos(limit, offset, search);
|
||||
const total = await storage.getVideoCount(search);
|
||||
|
||||
res.json({ videos, total, limit, offset });
|
||||
} catch (error) {
|
||||
console.error("Error fetching admin videos:", error);
|
||||
res.status(500).json({ message: "Failed to fetch videos" });
|
||||
}
|
||||
});
|
||||
|
||||
app.patch('/api/admin/videos/:id', isAdmin, async (req, res) => {
|
||||
try {
|
||||
const videoId = req.params.id;
|
||||
const updateData = updateVideoSchema.parse(req.body);
|
||||
|
||||
const updatedVideo = await storage.updateVideo(videoId, updateData);
|
||||
if (!updatedVideo) {
|
||||
return res.status(404).json({ message: "Video not found" });
|
||||
}
|
||||
|
||||
res.json(updatedVideo);
|
||||
} catch (error) {
|
||||
console.error("Error updating video:", error);
|
||||
if (error instanceof z.ZodError) {
|
||||
res.status(400).json({ message: "Invalid video data", errors: error.errors });
|
||||
} else {
|
||||
res.status(500).json({ message: "Failed to update video" });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Thumbnail upload
|
||||
app.post('/api/admin/thumbnails/upload', isAdmin, async (req, res) => {
|
||||
try {
|
||||
const objectStorageService = new ObjectStorageService();
|
||||
const uploadURL = await objectStorageService.getThumbnailUploadURL();
|
||||
res.json({ uploadURL });
|
||||
} catch (error) {
|
||||
console.error("Error getting thumbnail upload URL:", error);
|
||||
res.status(500).json({ message: "Failed to get upload URL" });
|
||||
}
|
||||
});
|
||||
|
||||
// Serve uploaded objects
|
||||
app.get("/objects/:objectPath(*)", async (req, res) => {
|
||||
const objectStorageService = new ObjectStorageService();
|
||||
try {
|
||||
const objectFile = await objectStorageService.getObjectFile(req.path);
|
||||
objectStorageService.downloadObject(objectFile, res);
|
||||
} catch (error) {
|
||||
console.error("Error serving object:", error);
|
||||
if (error instanceof ObjectNotFoundError) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
// User management (super admin only)
|
||||
app.patch('/api/admin/users/:id/admin', isAuthenticated, async (req: any, res) => {
|
||||
try {
|
||||
const currentUserId = req.user.claims.sub;
|
||||
const currentUser = await storage.getUser(currentUserId);
|
||||
|
||||
if (!currentUser?.isSuperAdmin) {
|
||||
return res.status(403).json({ message: "Super admin access required" });
|
||||
}
|
||||
|
||||
const userId = req.params.id;
|
||||
const { isAdmin } = req.body;
|
||||
|
||||
const updatedUser = await storage.updateUser(userId, { isAdmin });
|
||||
if (!updatedUser) {
|
||||
return res.status(404).json({ message: "User not found" });
|
||||
}
|
||||
|
||||
res.json(updatedUser);
|
||||
} catch (error) {
|
||||
console.error("Error updating user admin status:", error);
|
||||
res.status(500).json({ message: "Failed to update user" });
|
||||
}
|
||||
});
|
||||
|
||||
const httpServer = createServer(app);
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user