Skip to content

Commit d11202d

Browse files
committed
everything
1 parent 2709a6f commit d11202d

File tree

7 files changed

+109
-49
lines changed

7 files changed

+109
-49
lines changed

app/api/admin/verify/route.tsx

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ const adminSupabase = supabaseServiceKey ?
99
createClient(supabaseUrl, supabaseServiceKey) :
1010
null;
1111

12-
// List of authorized admin user IDs and usernames
13-
const AUTHORIZED_ADMINS = [
14-
'f8adc96a-496f-412b-af15-20bd3cd66b3c', // Original admin ID
15-
'Sparths', // Admin username
16-
'sparths', // Lowercase version
17-
];
12+
// Get admin list from environment variables
13+
const getAuthorizedAdmins = (): string[] => {
14+
const adminList = process.env.AUTHORIZED_ADMINS;
15+
if (!adminList) {
16+
console.error('AUTHORIZED_ADMINS environment variable not set');
17+
return [];
18+
}
19+
return adminList.split(',').map(admin => admin.trim());
20+
};
1821

1922
// Input sanitization
2023
const sanitizeInput = (input: string): string => {
@@ -36,9 +39,17 @@ export async function POST(request: Request) {
3639
}
3740

3841
const sanitizedUserId = sanitizeInput(userId);
42+
const authorizedAdmins = getAuthorizedAdmins();
43+
44+
if (authorizedAdmins.length === 0) {
45+
return NextResponse.json(
46+
{ error: "Admin configuration error" },
47+
{ status: 500 }
48+
);
49+
}
3950

4051
// First check if user ID is directly in the authorized list
41-
if (AUTHORIZED_ADMINS.includes(sanitizedUserId)) {
52+
if (authorizedAdmins.includes(sanitizedUserId)) {
4253
console.log("User found in direct admin list:", sanitizedUserId);
4354

4455
// Create admin session token for subsequent requests
@@ -55,7 +66,7 @@ export async function POST(request: Request) {
5566
success: true,
5667
isAdmin: true,
5768
userId: sanitizedUserId,
58-
adminToken: adminToken // Return this token for subsequent admin API calls
69+
adminToken: adminToken
5970
});
6071
}
6172

@@ -89,10 +100,10 @@ export async function POST(request: Request) {
89100
console.log("Found user in database:", user);
90101

91102
// Check if user ID, username, or display_name is in admin list
92-
const isAdmin = AUTHORIZED_ADMINS.includes(user.id) ||
93-
AUTHORIZED_ADMINS.includes(user.username) ||
94-
AUTHORIZED_ADMINS.includes(user.username.toLowerCase()) ||
95-
AUTHORIZED_ADMINS.includes(user.display_name);
103+
const isAdmin = authorizedAdmins.includes(user.id) ||
104+
authorizedAdmins.includes(user.username) ||
105+
authorizedAdmins.includes(user.username.toLowerCase()) ||
106+
authorizedAdmins.includes(user.display_name);
96107

97108
if (isAdmin) {
98109
console.log("User verified as admin");
@@ -111,7 +122,7 @@ export async function POST(request: Request) {
111122
success: true,
112123
isAdmin: true,
113124
userId: sanitizedUserId,
114-
adminToken: adminToken // Return this token for subsequent admin API calls
125+
adminToken: adminToken
115126
});
116127
} else {
117128
console.log("User not in admin list:", {
@@ -163,9 +174,10 @@ export async function GET(request: Request) {
163174
}
164175

165176
const sanitizedUserId = sanitizeInput(userId);
177+
const authorizedAdmins = getAuthorizedAdmins();
166178

167179
// Quick check against admin list
168-
const isAdmin = AUTHORIZED_ADMINS.includes(sanitizedUserId);
180+
const isAdmin = authorizedAdmins.includes(sanitizedUserId);
169181

170182
// If not found directly, check database
171183
if (!isAdmin && adminSupabase) {
@@ -177,10 +189,10 @@ export async function GET(request: Request) {
177189
.single();
178190

179191
if (!error && user) {
180-
const dbIsAdmin = AUTHORIZED_ADMINS.includes(user.id) ||
181-
AUTHORIZED_ADMINS.includes(user.username) ||
182-
AUTHORIZED_ADMINS.includes(user.username.toLowerCase()) ||
183-
AUTHORIZED_ADMINS.includes(user.display_name);
192+
const dbIsAdmin = authorizedAdmins.includes(user.id) ||
193+
authorizedAdmins.includes(user.username) ||
194+
authorizedAdmins.includes(user.username.toLowerCase()) ||
195+
authorizedAdmins.includes(user.display_name);
184196

185197
return NextResponse.json({
186198
isAdmin: dbIsAdmin,

app/request/requestpage.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,27 +88,30 @@ const handleFinalSubmit = async () => {
8888
message: "Submitting your request...",
8989
});
9090

91-
console.log("Submitting project with user ID:", user.id); // Add this for debugging
91+
console.log("Submitting project with user:", user); // Better logging
9292

9393
const response = await fetch("/api/project-requests", {
9494
method: "POST",
95-
headers: { "Content-Type": "application/json" },
95+
headers: {
96+
"Content-Type": "application/json",
97+
// Add authentication header if needed
98+
...(user.token && { "Authorization": `Bearer ${user.token}` })
99+
},
96100
body: JSON.stringify({
97101
title: projectRequest.title,
98102
githubLink: projectRequest.githubLink,
99103
description: projectRequest.description,
100104
reason: projectRequest.reason,
101-
userId: user.id, // Make sure this is correct
105+
userId: user.id,
102106
}),
103107
});
104108

109+
const responseData = await response.json();
110+
105111
if (!response.ok) {
106-
const errorData = await response.json();
107-
throw new Error(errorData.error || "Failed to submit request");
112+
throw new Error(responseData.error || "Failed to submit request");
108113
}
109114

110-
111-
112115
setSubmissionStatus({
113116
status: "success",
114117
message: "Thank you for your submission! Your project request has been received and will be reviewed by our team. You can check the status in your profile."
@@ -126,11 +129,11 @@ const handleFinalSubmit = async () => {
126129
router.push('/profile?tab=my-requests');
127130
}, 3000);
128131
} catch (error) {
132+
console.error("Error submitting project request:", error);
129133
setSubmissionStatus({
130134
status: "error",
131-
message: "Failed to submit project request. Please try again."
135+
message: error instanceof Error ? error.message : "Failed to submit project request. Please try again."
132136
});
133-
console.error("Error submitting project request:", error);
134137
}
135138
};
136139

components/Navbar.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ import {
2929
DropdownMenuTrigger,
3030
} from "@/components/ui/dropdown-menu";
3131

32-
// Define owner ID - replace with your actual owner user ID
33-
const OWNER_ID = "Sparths";
32+
3433

3534
interface NavLinkProps {
3635
icon: React.ElementType;
@@ -70,15 +69,25 @@ const Navbar = () => {
7069
const [activeNav, setActiveNav] = useState("/");
7170
const [showAuthDialog, setShowAuthDialog] = useState(false);
7271

72+
73+
7374
const { user, isAuthenticated, logout } = useAuth();
7475

76+
77+
const getOwnerIds = (): string[] => {
78+
// You can make this configurable via props or context if needed
79+
return process.env.NEXT_PUBLIC_AUTHORIZED_ADMINS?.split(',').map(admin => admin.trim()) || [];
80+
};
7581
// Check if user is the owner
76-
const isOwner = user && (user.id === OWNER_ID || user.username === OWNER_ID);
82+
const ownerIds = getOwnerIds();
83+
const isOwner = user && (ownerIds.includes(user.id) || ownerIds.includes(user.username));
7784

7885
useEffect(() => {
7986
setActiveNav(pathname);
8087
}, [pathname]);
8188

89+
90+
8291
return (
8392
<nav className="fixed top-0 w-full bg-slate-900/90 backdrop-blur-lg border-b border-slate-700 z-50">
8493
<div className="max-w-6xl mx-auto px-4">

lib/env-check.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// lib/env-check.ts
2+
export function validateEnvironment() {
3+
const requiredEnvVars = [
4+
'NEXT_PUBLIC_SUPABASE_URL',
5+
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
6+
'SUPABASE_SERVICE_ROLE_KEY',
7+
'AUTHORIZED_ADMINS'
8+
];
9+
10+
const missing = requiredEnvVars.filter(varName => !process.env[varName]);
11+
12+
if (missing.length > 0) {
13+
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
14+
}
15+
}
16+
17+
// For client-side environment validation
18+
export function validateClientEnvironment() {
19+
if (typeof window !== 'undefined') {
20+
const requiredClientVars = [
21+
'NEXT_PUBLIC_SUPABASE_URL',
22+
'NEXT_PUBLIC_SUPABASE_ANON_KEY'
23+
];
24+
25+
const missing = requiredClientVars.filter(varName => !process.env[varName]);
26+
27+
if (missing.length > 0) {
28+
console.error(`Missing required client environment variables: ${missing.join(', ')}`);
29+
return false;
30+
}
31+
}
32+
return true;
33+
}

lib/rate-limiter.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ interface RateLimitConfig {
1212
// Rate limiting store (in production, use Redis or a database)
1313
const rateLimitStore = new Map<string, RateLimitRecord>();
1414

15-
// More reasonable rate limiting configurations
15+
// Much higher rate limiting configurations
1616
const RATE_LIMIT_CONFIGS: Record<string, RateLimitConfig> = {
17-
'users_create': { maxRequests: 5, windowMs: 15 * 60 * 1000 }, // 5 registrations per 15 minutes
18-
'users_login': { maxRequests: 10, windowMs: 15 * 60 * 1000 }, // 10 login attempts per 15 minutes
19-
'users_update': { maxRequests: 10, windowMs: 5 * 60 * 1000 }, // 10 updates per 5 minutes
20-
'users_get': { maxRequests: 100, windowMs: 5 * 60 * 1000 }, // 100 gets per 5 minutes
21-
'project_requests': { maxRequests: 5, windowMs: 60 * 60 * 1000 }, // 5 requests per hour
22-
'comments': { maxRequests: 30, windowMs: 5 * 60 * 1000 }, // 30 comments per 5 minutes
23-
'ratings': { maxRequests: 50, windowMs: 60 * 60 * 1000 }, // 50 ratings per hour
24-
'default': { maxRequests: 200, windowMs: 15 * 60 * 1000 } // Default rate limit
17+
'users_create': { maxRequests: 50, windowMs: 15 * 60 * 1000 }, // 50 registrations per 15 minutes
18+
'users_login': { maxRequests: 100, windowMs: 15 * 60 * 1000 }, // 100 login attempts per 15 minutes
19+
'users_update': { maxRequests: 100, windowMs: 5 * 60 * 1000 }, // 100 updates per 5 minutes
20+
'users_get': { maxRequests: 1000, windowMs: 5 * 60 * 1000 }, // 1000 gets per 5 minutes
21+
'project_requests': { maxRequests: 50, windowMs: 60 * 60 * 1000 }, // 50 requests per hour
22+
'comments': { maxRequests: 500, windowMs: 5 * 60 * 1000 }, // 500 comments per 5 minutes
23+
'ratings': { maxRequests: 500, windowMs: 60 * 60 * 1000 }, // 500 ratings per hour
24+
'default': { maxRequests: 2000, windowMs: 15 * 60 * 1000 } // 2000 requests per 15 minutes
2525
};
2626

2727
function getClientIP(request: Request): string {

lib/supabase.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,14 @@ export type Database = {
300300
}
301301
}
302302

303-
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
304-
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
303+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
304+
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
305305

306306
if (!supabaseUrl || !supabaseKey) {
307-
throw new Error('Missing Supabase environment variables');
307+
console.error('Missing Supabase environment variables:');
308+
console.error('NEXT_PUBLIC_SUPABASE_URL:', !!supabaseUrl);
309+
console.error('NEXT_PUBLIC_SUPABASE_ANON_KEY:', !!supabaseKey);
310+
throw new Error('Missing required Supabase environment variables. Please check your .env.local file.');
308311
}
309312

310313
const supabase = createClient<Database>(supabaseUrl, supabaseKey, {

middleware.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import type { NextRequest } from 'next/server';
55
// Rate limiting store (in production, use Redis or a database)
66
const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
77

8-
// Rate limiting configuration - More reasonable limits
8+
// Much higher rate limiting configuration
99
const RATE_LIMIT_CONFIG = {
10-
'/api/auth': { maxRequests: 10, windowMs: 15 * 60 * 1000 }, // 10 requests per 15 minutes
11-
'/api/project-requests': { maxRequests: 5, windowMs: 60 * 60 * 1000 }, // 5 requests per hour
12-
'/api/comments': { maxRequests: 20, windowMs: 5 * 60 * 1000 }, // 20 requests per 5 minutes
13-
'/api/ratings': { maxRequests: 30, windowMs: 60 * 60 * 1000 }, // 30 requests per hour
14-
'/api/users': { maxRequests: 15, windowMs: 15 * 60 * 1000 }, // 15 requests per 15 minutes
15-
'default': { maxRequests: 100, windowMs: 15 * 60 * 1000 } // Default rate limit
10+
'/api/auth': { maxRequests: 100, windowMs: 15 * 60 * 1000 }, // 100 requests per 15 minutes
11+
'/api/project-requests': { maxRequests: 50, windowMs: 60 * 60 * 1000 }, // 50 requests per hour
12+
'/api/comments': { maxRequests: 500, windowMs: 5 * 60 * 1000 }, // 500 requests per 5 minutes
13+
'/api/ratings': { maxRequests: 500, windowMs: 60 * 60 * 1000 }, // 500 requests per hour
14+
'/api/users': { maxRequests: 150, windowMs: 15 * 60 * 1000 }, // 150 requests per 15 minutes
15+
'default': { maxRequests: 2000, windowMs: 15 * 60 * 1000 } // 2000 requests per 15 minutes
1616
};
1717

1818
function getClientIP(request: NextRequest): string {

0 commit comments

Comments
 (0)