Skip to content

Commit 0f19b63

Browse files
committed
feat: Refactor postPOTD logic for atomic updates and improved error handling
- Implemented session-based transactions in postPotdChallenge to ensure atomicity. - Simplified user challenge solving logic with a single update operation. - Enhanced error handling with specific messages for missing fields and update failures. - Added streak calculation logic based on daily challenge completion. - Updated challenge's solvedUsers list atomically to prevent duplicates. feat: Introduce enhanced error pages with retry functionality - Created EnhancedErrorPages component to handle various error types. - Implemented animations for error display using framer-motion. - Added retry functionality for server and network errors. - Wrapped error pages with ErrorBoundary for better error management. fix: Implement error boundary for better error handling in the application - Created ErrorBoundary component to catch and display errors gracefully. - Added retry and home navigation options in the error fallback UI. feat: Implement rate limiting middleware to prevent excessive requests - Developed rateLimitMiddleware to limit requests per user/IP. - Added specific rate limiters for challenge updates, platform updates, and registrations. chore: Set up database indexes for performance and race condition prevention - Created optimal indexes for users and challenges collections to enhance query performance. - Ensured unique constraints to prevent duplicate entries. fix: Standardize error handling across the application - Developed a global error handler to manage different error types and responses. - Created custom error classes for better error categorization. - Implemented success response helpers for consistent API responses. chore: Manage application lifecycle for background services and cleanup - Implemented AppLifecycle class to manage leaderboard updates and cleanup tasks. - Ensured graceful shutdown of cron jobs and rate limiter during application cleanup.
1 parent 710a473 commit 0f19b63

22 files changed

+1739
-299
lines changed

client-test/src/App.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import AdminChallenges from './components/Admin/AdminChallenges.tsx';
2626
import RouteSEO from './components/RouteSEO';
2727
import ProblemSet from './components/ProblemSet';
2828
import CategoryProblems from './components/ProblemSet/CategoryProblems';
29+
import { ServerErrorRoute, NetworkErrorRoute, UnauthorizedRoute } from './components/ErrorRoutes';
30+
import { ForbiddenPage } from './components/EnhancedErrorPages';
2931

3032
function UserApp() {
3133
return (
@@ -49,6 +51,12 @@ function UserApp() {
4951
<Route path="/challenges/solution/:slug" element={<SolutionPage />} />
5052
<Route path="/problemset" element={<ProblemSet />} />
5153
<Route path="/problemset/category/:categoryName" element={<CategoryProblems />} />
54+
{/* Enhanced Error Pages */}
55+
<Route path="/server-error" element={<ServerErrorRoute />} />
56+
<Route path="/network-error" element={<NetworkErrorRoute />} />
57+
<Route path="/unauthorized" element={<UnauthorizedRoute />} />
58+
<Route path="/forbidden" element={<ForbiddenPage />} />
59+
<Route path="/404" element={<NotFoundPage />} />
5260
<Route path="*" element={<NotFoundPage />} />
5361
</Routes>
5462
<Footer />
@@ -85,6 +93,11 @@ function AdminApp() {
8593
<Route path="/codingclubadmin/users/profile/:username" element={<ProfilePage />} />
8694
<Route path="/codingclubadmin/addchallenge" element={<AddChallenge />} />
8795
<Route path="/codingclubadmin/leaderboard" element={<Leaderboard />} />
96+
{/* Enhanced Error Pages for Admin */}
97+
<Route path="/codingclubadmin/server-error" element={<ServerErrorRoute />} />
98+
<Route path="/codingclubadmin/network-error" element={<NetworkErrorRoute />} />
99+
<Route path="/codingclubadmin/unauthorized" element={<UnauthorizedRoute />} />
100+
<Route path="/codingclubadmin/forbidden" element={<ForbiddenPage />} />
88101
<Route path="/codingclubadmin/*" element={<NotFoundPage />} />
89102
</Routes>
90103
</div>
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
import { motion } from "framer-motion";
2+
import { ArrowLeft, Rocket, AlertTriangle, RefreshCw, Home } from "lucide-react";
3+
import { Link } from "react-router-dom";
4+
import React, { useEffect } from "react";
5+
import { ErrorBoundary } from "./ErrorBoundary";
6+
import { Button } from "./ui/button";
7+
8+
// Enhanced 404/Error Page Props
9+
interface ErrorPageProps {
10+
errorType?: 'notFound' | 'serverError' | 'networkError' | 'forbidden' | 'unauthorized';
11+
title?: string;
12+
message?: string;
13+
showRetry?: boolean;
14+
onRetry?: () => void;
15+
customIcon?: React.ReactNode;
16+
}
17+
18+
// Error configurations for different types
19+
const errorConfigs = {
20+
notFound: {
21+
icon: <Rocket className="w-16 h-16 md:w-24 md:h-24 text-yellow-500 dark:text-yellow-600" />,
22+
title: "Oops! Page Not Found",
23+
message: "It looks like you're lost in space! The page you're looking for doesn't exist, or the server might be taking a nap.",
24+
showRetry: false
25+
},
26+
serverError: {
27+
icon: <AlertTriangle className="w-16 h-16 md:w-24 md:h-24 text-red-500 dark:text-red-600" />,
28+
title: "Server Error",
29+
message: "Something went wrong on our servers. Our team has been notified and is working on a fix.",
30+
showRetry: true
31+
},
32+
networkError: {
33+
icon: <AlertTriangle className="w-16 h-16 md:w-24 md:h-24 text-orange-500 dark:text-orange-600" />,
34+
title: "Connection Problem",
35+
message: "Unable to connect to our servers. Please check your internet connection and try again.",
36+
showRetry: true
37+
},
38+
forbidden: {
39+
icon: <AlertTriangle className="w-16 h-16 md:w-24 md:h-24 text-red-500 dark:text-red-600" />,
40+
title: "Access Denied",
41+
message: "You don't have permission to access this resource. Please contact support if you think this is a mistake.",
42+
showRetry: false
43+
},
44+
unauthorized: {
45+
icon: <AlertTriangle className="w-16 h-16 md:w-24 md:h-24 text-blue-500 dark:text-blue-600" />,
46+
title: "Authentication Required",
47+
message: "Please log in to access this resource.",
48+
showRetry: false
49+
}
50+
};
51+
52+
// Enhanced Error Page Component
53+
const EnhancedErrorPage: React.FC<ErrorPageProps> = ({
54+
errorType = 'notFound',
55+
title,
56+
message,
57+
showRetry,
58+
onRetry,
59+
customIcon
60+
}) => {
61+
const config = errorConfigs[errorType];
62+
const displayTitle = title || config.title;
63+
const displayMessage = message || config.message;
64+
const displayShowRetry = showRetry !== undefined ? showRetry : config.showRetry;
65+
const displayIcon = customIcon || config.icon;
66+
67+
useEffect(() => {
68+
console.log(`ErrorPage component mounted - Type: ${errorType}`);
69+
}, [errorType]);
70+
71+
// Animation variants for the error code
72+
const numberVariants = {
73+
hidden: { y: 50, opacity: 0 },
74+
visible: {
75+
y: 0,
76+
opacity: 1,
77+
transition: {
78+
type: "spring",
79+
stiffness: 200,
80+
damping: 20,
81+
delay: 0.2,
82+
} as const,
83+
},
84+
};
85+
86+
// Animation variants for the icon
87+
const iconVariants = {
88+
float: {
89+
y: [-10, 10],
90+
rotate: errorType === 'notFound' ? [-5, 5] : [0, 0],
91+
transition: {
92+
repeat: Infinity,
93+
repeatType: "reverse" as const,
94+
duration: 2,
95+
},
96+
},
97+
};
98+
99+
// Animation variants for the message
100+
const messageVariants = {
101+
hidden: { opacity: 0, y: 20 },
102+
visible: {
103+
opacity: 1,
104+
y: 0,
105+
transition: {
106+
delay: 0.4,
107+
duration: 0.5,
108+
},
109+
},
110+
};
111+
112+
const handleRetry = () => {
113+
if (onRetry) {
114+
onRetry();
115+
} else {
116+
window.location.reload();
117+
}
118+
};
119+
120+
return (
121+
<div className="min-h-screen bg-gray-900 dark:bg-gray-100 flex items-center justify-center p-4">
122+
<div className="text-center space-y-8">
123+
{/* Animated Error Display */}
124+
<div className="flex justify-center items-center space-x-2">
125+
{errorType === 'notFound' ? (
126+
<>
127+
<motion.h1
128+
className="text-8xl md:text-9xl font-bold text-blue-500 dark:text-blue-600"
129+
variants={numberVariants}
130+
initial="hidden"
131+
animate="visible"
132+
>
133+
4
134+
</motion.h1>
135+
<motion.div
136+
className="relative"
137+
variants={iconVariants}
138+
animate="float"
139+
>
140+
{displayIcon}
141+
</motion.div>
142+
<motion.h1
143+
className="text-8xl md:text-9xl font-bold text-blue-500 dark:text-blue-600"
144+
variants={numberVariants}
145+
initial="hidden"
146+
animate="visible"
147+
>
148+
4
149+
</motion.h1>
150+
</>
151+
) : (
152+
<motion.div
153+
className="relative"
154+
variants={iconVariants}
155+
animate="float"
156+
>
157+
{displayIcon}
158+
</motion.div>
159+
)}
160+
</div>
161+
162+
{/* Animated Message */}
163+
<motion.div
164+
className="space-y-4"
165+
variants={messageVariants}
166+
initial="hidden"
167+
animate="visible"
168+
>
169+
<h2 className="text-2xl md:text-3xl font-semibold text-white dark:text-gray-900">
170+
{displayTitle}
171+
</h2>
172+
<p className="text-gray-400 dark:text-gray-600 max-w-md mx-auto">
173+
{displayMessage}
174+
</p>
175+
</motion.div>
176+
177+
{/* Action Buttons */}
178+
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center mt-8">
179+
<Link to="/">
180+
<Button className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700">
181+
<Home className="w-4 h-4" />
182+
Back to Home
183+
</Button>
184+
</Link>
185+
186+
{displayShowRetry && (
187+
<Button
188+
onClick={handleRetry}
189+
variant="outline"
190+
className="flex items-center gap-2"
191+
>
192+
<RefreshCw className="w-4 h-4" />
193+
Try Again
194+
</Button>
195+
)}
196+
197+
{errorType === 'unauthorized' && (
198+
<Link to="/login">
199+
<Button variant="outline" className="flex items-center gap-2">
200+
<ArrowLeft className="w-4 h-4" />
201+
Go to Login
202+
</Button>
203+
</Link>
204+
)}
205+
</div>
206+
</div>
207+
</div>
208+
);
209+
};
210+
211+
// Wrapped with Error Boundary
212+
const SafeErrorPage: React.FC<ErrorPageProps> = (props) => (
213+
<ErrorBoundary>
214+
<EnhancedErrorPage {...props} />
215+
</ErrorBoundary>
216+
);
217+
218+
// Export both the original 404 and the enhanced version
219+
export const NotFoundPage: React.FC = () => (
220+
<SafeErrorPage errorType="notFound" />
221+
);
222+
223+
export const ServerErrorPage: React.FC<{ onRetry?: () => void }> = ({ onRetry }) => (
224+
<SafeErrorPage errorType="serverError" onRetry={onRetry} />
225+
);
226+
227+
export const NetworkErrorPage: React.FC<{ onRetry?: () => void }> = ({ onRetry }) => (
228+
<SafeErrorPage errorType="networkError" onRetry={onRetry} />
229+
);
230+
231+
export const ForbiddenPage: React.FC = () => (
232+
<SafeErrorPage errorType="forbidden" />
233+
);
234+
235+
export const UnauthorizedPage: React.FC = () => (
236+
<SafeErrorPage errorType="unauthorized" />
237+
);
238+
239+
export default SafeErrorPage;

client-test/src/components/Error404.tsx

Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,8 @@ import { motion } from "framer-motion";
33
import { ArrowLeft, Rocket } from "lucide-react";
44
import { Link } from "react-router-dom"
55
import React, { useEffect } from "react";
6-
7-
// Error Boundary Component
8-
class ErrorBoundary extends React.Component<
9-
{ children: React.ReactNode },
10-
{ hasError: boolean }
11-
> {
12-
state = { hasError: false };
13-
14-
static getDerivedStateFromError() {
15-
return { hasError: true };
16-
}
17-
18-
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
19-
console.error("Error in NotFoundPage:", error, errorInfo);
20-
}
21-
22-
render() {
23-
if (this.state.hasError) {
24-
return (
25-
<div className="min-h-screen flex items-center justify-center bg-gray-900 text-white">
26-
<h1>Something went wrong. Please try again later.</h1>
27-
</div>
28-
);
29-
}
30-
return this.props.children;
31-
}
32-
}
33-
34-
// Button Component
35-
const Button = ({
36-
children,
37-
href,
38-
className = "",
39-
}: {
40-
children: React.ReactNode;
41-
href: string;
42-
className?: string;
43-
}) => {
44-
return (
45-
<Link to={href}>
46-
<motion.button
47-
className={`px-6 py-3 rounded-lg bg-blue-600 text-white font-semibold flex items-center gap-2 hover:bg-blue-700 transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-blue-500 ${className}`}
48-
whileHover={{ scale: 1.05 }}
49-
whileTap={{ scale: 0.95 }}
50-
>
51-
{children}
52-
</motion.button>
53-
</Link>
54-
);
55-
};
6+
import { ErrorBoundary } from "./ErrorBoundary";
7+
import { Button } from "./ui/button";
568

579
// 404 Page Component
5810
const NotFoundPage: React.FC = () => {
@@ -150,10 +102,12 @@ const NotFoundPage: React.FC = () => {
150102
</motion.div>
151103

152104
{/* Back to Home Button */}
153-
<Button href="/" className="mt-6 mx-auto">
154-
<ArrowLeft className="w-5 h-5" />
155-
Back to Home
156-
</Button>
105+
<Link to="/" className="mt-6 mx-auto inline-block">
106+
<Button className="flex items-center gap-2">
107+
<ArrowLeft className="w-5 h-5" />
108+
Back to Home
109+
</Button>
110+
</Link>
157111
</div>
158112
</div>
159113
</ErrorBoundary>

0 commit comments

Comments
 (0)