Set up the basic structure and functionality for the DJ management system

Initializes project structure, adds core components, and configures essential dependencies.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 3a22ac80-cd1d-4441-9e36-f24fc2f4c3de
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3478f7c3-db8c-4fca-9165-3adbdf1b5829/e8da43e7-d99c-4328-9fdc-485bdeecffc1.jpg
This commit is contained in:
spliceboti
2025-07-09 23:54:32 +00:00
parent 2869d657c8
commit 89946fcef9
106 changed files with 20207 additions and 0 deletions
+566
View File
@@ -0,0 +1,566 @@
import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { setupAuth, isAuthenticated } from "./replitAuth";
import {
insertEventSchema,
insertEventTypeSchema,
insertScheduleTemplateSchema,
insertScheduleTemplateSlotSchema,
insertSocialLinkSchema,
insertAvailabilitySchema,
insertRemovalRequestSchema,
insertInvitationSchema,
updateUserSchema,
updateEventSchema,
updateEventTypeSchema,
updateScheduleTemplateSchema,
updateRemovalRequestSchema,
} from "@shared/schema";
import { z } from "zod";
import { fromZodError } from "zod-validation-error";
import crypto from "crypto";
export async function registerRoutes(app: Express): Promise<Server> {
// Auth middleware
await setupAuth(app);
// Helper function to check if user is admin
const isAdmin = async (req: any, res: any, next: any) => {
try {
const userId = req.user.claims.sub;
const user = await storage.getUser(userId);
if (!user || user.role !== "admin") {
return res.status(403).json({ message: "Admin access required" });
}
next();
} catch (error) {
res.status(500).json({ message: "Error checking admin status" });
}
};
// Auth routes
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" });
}
});
// User management routes
app.get('/api/users', isAuthenticated, isAdmin, async (req, res) => {
try {
const users = await storage.getAllDJs();
res.json(users);
} catch (error) {
console.error("Error fetching users:", error);
res.status(500).json({ message: "Failed to fetch users" });
}
});
app.patch('/api/users/:id', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const targetUserId = req.params.id;
const user = await storage.getUser(userId);
// Allow users to update their own profile or admins to update any profile
if (userId !== targetUserId && user?.role !== "admin") {
return res.status(403).json({ message: "Permission denied" });
}
const updates = updateUserSchema.parse(req.body);
const updatedUser = await storage.updateUser(targetUserId, updates);
res.json(updatedUser);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error updating user:", error);
res.status(500).json({ message: "Failed to update user" });
}
});
app.post('/api/users/:id/deactivate', isAuthenticated, isAdmin, async (req, res) => {
try {
await storage.deactivateUser(req.params.id);
res.json({ message: "User deactivated successfully" });
} catch (error) {
console.error("Error deactivating user:", error);
res.status(500).json({ message: "Failed to deactivate user" });
}
});
app.post('/api/users/:id/reactivate', isAuthenticated, isAdmin, async (req, res) => {
try {
await storage.reactivateUser(req.params.id);
res.json({ message: "User reactivated successfully" });
} catch (error) {
console.error("Error reactivating user:", error);
res.status(500).json({ message: "Failed to reactivate user" });
}
});
app.post('/api/users/:id/make-admin', isAuthenticated, isAdmin, async (req, res) => {
try {
await storage.makeAdmin(req.params.id);
res.json({ message: "User promoted to admin successfully" });
} catch (error) {
console.error("Error promoting user to admin:", error);
res.status(500).json({ message: "Failed to promote user to admin" });
}
});
app.post('/api/users/:id/remove-admin', isAuthenticated, isAdmin, async (req, res) => {
try {
await storage.removeAdmin(req.params.id);
res.json({ message: "Admin privileges removed successfully" });
} catch (error) {
console.error("Error removing admin privileges:", error);
res.status(500).json({ message: "Failed to remove admin privileges" });
}
});
// Event routes
app.get('/api/events', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const user = await storage.getUser(userId);
if (user?.role === "admin") {
const events = await storage.getAllEvents();
res.json(events);
} else {
const events = await storage.getEventsByDJ(userId);
res.json(events);
}
} catch (error) {
console.error("Error fetching events:", error);
res.status(500).json({ message: "Failed to fetch events" });
}
});
app.get('/api/events/upcoming', isAuthenticated, async (req, res) => {
try {
const limit = req.query.limit ? parseInt(req.query.limit as string) : 50;
const events = await storage.getUpcomingEvents(limit);
res.json(events);
} catch (error) {
console.error("Error fetching upcoming events:", error);
res.status(500).json({ message: "Failed to fetch upcoming events" });
}
});
app.post('/api/events', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const eventData = insertEventSchema.parse({ ...req.body, djId: userId });
const event = await storage.createEvent(eventData);
res.json(event);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating event:", error);
res.status(500).json({ message: "Failed to create event" });
}
});
app.get('/api/events/:id', isAuthenticated, async (req: any, res) => {
try {
const event = await storage.getEvent(parseInt(req.params.id));
if (!event) {
return res.status(404).json({ message: "Event not found" });
}
const userId = req.user.claims.sub;
const user = await storage.getUser(userId);
// Allow access to event if user is admin or the DJ assigned to the event
if (user?.role !== "admin" && event.djId !== userId) {
return res.status(403).json({ message: "Permission denied" });
}
res.json(event);
} catch (error) {
console.error("Error fetching event:", error);
res.status(500).json({ message: "Failed to fetch event" });
}
});
app.patch('/api/events/:id', isAuthenticated, async (req: any, res) => {
try {
const eventId = parseInt(req.params.id);
const event = await storage.getEvent(eventId);
if (!event) {
return res.status(404).json({ message: "Event not found" });
}
const userId = req.user.claims.sub;
const user = await storage.getUser(userId);
// Only allow DJ to edit their own self-added events or admin to edit any event
if (user?.role !== "admin" && (event.djId !== userId || event.isAssignedByAdmin)) {
return res.status(403).json({ message: "Permission denied" });
}
const updates = updateEventSchema.parse(req.body);
const updatedEvent = await storage.updateEvent(eventId, updates);
res.json(updatedEvent);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error updating event:", error);
res.status(500).json({ message: "Failed to update event" });
}
});
app.delete('/api/events/:id', isAuthenticated, async (req: any, res) => {
try {
const eventId = parseInt(req.params.id);
const event = await storage.getEvent(eventId);
if (!event) {
return res.status(404).json({ message: "Event not found" });
}
const userId = req.user.claims.sub;
const user = await storage.getUser(userId);
// Only allow DJ to delete their own self-added events or admin to delete any event
if (user?.role !== "admin" && (event.djId !== userId || event.isAssignedByAdmin)) {
return res.status(403).json({ message: "Permission denied" });
}
await storage.deleteEvent(eventId);
res.json({ message: "Event deleted successfully" });
} catch (error) {
console.error("Error deleting event:", error);
res.status(500).json({ message: "Failed to delete event" });
}
});
// Public API for WordPress integration
app.get('/api/public/upcoming-events', async (req, res) => {
try {
const limit = req.query.limit ? parseInt(req.query.limit as string) : 10;
const events = await storage.getUpcomingEvents(limit);
// Format events for WordPress widget
const formattedEvents = events.map(event => ({
id: event.id,
name: event.name,
date: event.date,
startTime: event.startTime,
endTime: event.endTime,
locationName: event.locationName,
locationAddress: event.locationAddress,
djId: event.djId,
eventTypeId: event.eventTypeId,
description: event.description,
}));
res.json(formattedEvents);
} catch (error) {
console.error("Error fetching public events:", error);
res.status(500).json({ message: "Failed to fetch events" });
}
});
// Event type routes
app.get('/api/event-types', isAuthenticated, async (req, res) => {
try {
const eventTypes = await storage.getAllEventTypes();
res.json(eventTypes);
} catch (error) {
console.error("Error fetching event types:", error);
res.status(500).json({ message: "Failed to fetch event types" });
}
});
app.post('/api/event-types', isAuthenticated, isAdmin, async (req, res) => {
try {
const eventTypeData = insertEventTypeSchema.parse(req.body);
const eventType = await storage.createEventType(eventTypeData);
res.json(eventType);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating event type:", error);
res.status(500).json({ message: "Failed to create event type" });
}
});
app.patch('/api/event-types/:id', isAuthenticated, isAdmin, async (req, res) => {
try {
const eventTypeId = parseInt(req.params.id);
const updates = updateEventTypeSchema.parse(req.body);
const eventType = await storage.updateEventType(eventTypeId, updates);
res.json(eventType);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error updating event type:", error);
res.status(500).json({ message: "Failed to update event type" });
}
});
app.delete('/api/event-types/:id', isAuthenticated, isAdmin, async (req, res) => {
try {
const eventTypeId = parseInt(req.params.id);
await storage.deleteEventType(eventTypeId);
res.json({ message: "Event type deleted successfully" });
} catch (error) {
console.error("Error deleting event type:", error);
res.status(500).json({ message: "Failed to delete event type" });
}
});
// Schedule template routes
app.get('/api/schedule-templates', isAuthenticated, isAdmin, async (req, res) => {
try {
const templates = await storage.getAllScheduleTemplates();
res.json(templates);
} catch (error) {
console.error("Error fetching schedule templates:", error);
res.status(500).json({ message: "Failed to fetch schedule templates" });
}
});
app.post('/api/schedule-templates', isAuthenticated, isAdmin, async (req, res) => {
try {
const templateData = insertScheduleTemplateSchema.parse(req.body);
const template = await storage.createScheduleTemplate(templateData);
res.json(template);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating schedule template:", error);
res.status(500).json({ message: "Failed to create schedule template" });
}
});
app.get('/api/schedule-templates/:id/slots', isAuthenticated, isAdmin, async (req, res) => {
try {
const templateId = parseInt(req.params.id);
const slots = await storage.getScheduleTemplateSlots(templateId);
res.json(slots);
} catch (error) {
console.error("Error fetching template slots:", error);
res.status(500).json({ message: "Failed to fetch template slots" });
}
});
app.post('/api/schedule-templates/:id/slots', isAuthenticated, isAdmin, async (req, res) => {
try {
const templateId = parseInt(req.params.id);
const slotData = insertScheduleTemplateSlotSchema.parse({ ...req.body, templateId });
const slot = await storage.createScheduleTemplateSlot(slotData);
res.json(slot);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating template slot:", error);
res.status(500).json({ message: "Failed to create template slot" });
}
});
// Social links routes
app.get('/api/social-links', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const links = await storage.getSocialLinksByDJ(userId);
res.json(links);
} catch (error) {
console.error("Error fetching social links:", error);
res.status(500).json({ message: "Failed to fetch social links" });
}
});
app.post('/api/social-links', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const linkData = insertSocialLinkSchema.parse({ ...req.body, djId: userId });
const link = await storage.createSocialLink(linkData);
res.json(link);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating social link:", error);
res.status(500).json({ message: "Failed to create social link" });
}
});
app.delete('/api/social-links/:id', isAuthenticated, async (req: any, res) => {
try {
await storage.deleteSocialLink(parseInt(req.params.id));
res.json({ message: "Social link deleted successfully" });
} catch (error) {
console.error("Error deleting social link:", error);
res.status(500).json({ message: "Failed to delete social link" });
}
});
// Availability routes
app.get('/api/availability', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const availability = await storage.getAvailabilityByDJ(userId);
res.json(availability);
} catch (error) {
console.error("Error fetching availability:", error);
res.status(500).json({ message: "Failed to fetch availability" });
}
});
app.post('/api/availability', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const availabilityData = insertAvailabilitySchema.parse({ ...req.body, djId: userId });
const availability = await storage.createAvailability(availabilityData);
res.json(availability);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating availability:", error);
res.status(500).json({ message: "Failed to create availability" });
}
});
app.delete('/api/availability/:id', isAuthenticated, async (req: any, res) => {
try {
await storage.deleteAvailability(parseInt(req.params.id));
res.json({ message: "Availability deleted successfully" });
} catch (error) {
console.error("Error deleting availability:", error);
res.status(500).json({ message: "Failed to delete availability" });
}
});
// Removal request routes
app.get('/api/removal-requests', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const user = await storage.getUser(userId);
if (user?.role === "admin") {
const requests = await storage.getPendingRemovalRequests();
res.json(requests);
} else {
return res.status(403).json({ message: "Admin access required" });
}
} catch (error) {
console.error("Error fetching removal requests:", error);
res.status(500).json({ message: "Failed to fetch removal requests" });
}
});
app.post('/api/removal-requests', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const requestData = insertRemovalRequestSchema.parse({ ...req.body, djId: userId });
const request = await storage.createRemovalRequest(requestData);
res.json(request);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating removal request:", error);
res.status(500).json({ message: "Failed to create removal request" });
}
});
app.post('/api/removal-requests/:id/approve', isAuthenticated, isAdmin, async (req: any, res) => {
try {
const requestId = parseInt(req.params.id);
const reviewedBy = req.user.claims.sub;
await storage.approveRemovalRequest(requestId, reviewedBy);
res.json({ message: "Removal request approved successfully" });
} catch (error) {
console.error("Error approving removal request:", error);
res.status(500).json({ message: "Failed to approve removal request" });
}
});
app.post('/api/removal-requests/:id/deny', isAuthenticated, isAdmin, async (req: any, res) => {
try {
const requestId = parseInt(req.params.id);
const reviewedBy = req.user.claims.sub;
await storage.denyRemovalRequest(requestId, reviewedBy);
res.json({ message: "Removal request denied successfully" });
} catch (error) {
console.error("Error denying removal request:", error);
res.status(500).json({ message: "Failed to deny removal request" });
}
});
// Invitation routes
app.post('/api/invitations', isAuthenticated, isAdmin, async (req: any, res) => {
try {
const createdBy = req.user.claims.sub;
const token = crypto.randomBytes(32).toString('hex');
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
const invitationData = insertInvitationSchema.parse({
...req.body,
token,
createdBy,
expiresAt
});
const invitation = await storage.createInvitation(invitationData);
res.json(invitation);
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({ message: fromZodError(error).toString() });
}
console.error("Error creating invitation:", error);
res.status(500).json({ message: "Failed to create invitation" });
}
});
app.get('/api/invitations', isAuthenticated, isAdmin, async (req, res) => {
try {
const invitations = await storage.getActiveInvitations();
res.json(invitations);
} catch (error) {
console.error("Error fetching invitations:", error);
res.status(500).json({ message: "Failed to fetch invitations" });
}
});
// Statistics routes
app.get('/api/stats/dashboard', isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const stats = await storage.getDashboardStats(userId);
res.json(stats);
} catch (error) {
console.error("Error fetching dashboard stats:", error);
res.status(500).json({ message: "Failed to fetch dashboard stats" });
}
});
app.get('/api/stats/admin', isAuthenticated, isAdmin, async (req, res) => {
try {
const stats = await storage.getAdminStats();
res.json(stats);
} catch (error) {
console.error("Error fetching admin stats:", error);
res.status(500).json({ message: "Failed to fetch admin stats" });
}
});
const httpServer = createServer(app);
return httpServer;
}