Skip to content

Commit fe16400

Browse files
Merge pull request #33 from devweekends/apply-form-validation
reafactor: multiple changes and fixes
2 parents 193a80a + f531421 commit fe16400

19 files changed

Lines changed: 640 additions & 184 deletions

File tree

app/about/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export default function AboutPage() {
8282
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
8383
{[
8484
{ value: "20K+", label: "Community Members" },
85-
{ value: "800+", label: "Engineers Trained" },
85+
{ value: "1600+", label: "Engineers Trained" },
8686
{ value: "200+", label: "Sessions Delivered" },
8787
{ value: "100%", label: "Free Forever" },
8888
].map((stat, i) => (

app/api/dsoc/applications/[id]/route.ts

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,47 @@ import connectDB from '@/lib/db';
33
import { DSOCApplication } from '@/models/DSOCApplication';
44
import { DSOCProject } from '@/models/DSOCProject';
55
import { DSOCMentee } from '@/models/DSOCMentee';
6+
import jwt from 'jsonwebtoken';
7+
8+
async function getMentorFromToken(request: NextRequest) {
9+
const token = request.cookies.get('dsoc-mentor-token')?.value;
10+
if (!token) return null;
11+
12+
try {
13+
const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as { id: string; role: string };
14+
if (decoded.role !== 'dsoc-mentor') return null;
15+
return decoded.id;
16+
} catch {
17+
return null;
18+
}
19+
}
20+
21+
async function getAdminFromToken(request: NextRequest) {
22+
const token = request.cookies.get('admin-token')?.value;
23+
if (!token) return null;
24+
25+
try {
26+
const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as { id?: string };
27+
return decoded.id || null;
28+
} catch {
29+
return null;
30+
}
31+
}
32+
33+
function hasMentorAccess(
34+
mentorId: string,
35+
project: { mentors?: Array<{ _id?: string } | string> } | null
36+
) {
37+
if (!project || !Array.isArray(project.mentors)) return false;
38+
39+
return project.mentors.some((mentor) => {
40+
if (!mentor) return false;
41+
if (typeof mentor === 'string') return mentor === mentorId;
42+
if (mentor._id) return mentor._id.toString() === mentorId;
43+
const asAny = mentor as { toString?: () => string };
44+
return asAny.toString?.() === mentorId;
45+
});
46+
}
647

748
// GET single application
849
export async function GET(
@@ -12,6 +53,15 @@ export async function GET(
1253
try {
1354
await connectDB();
1455
const { id } = await params;
56+
57+
const mentorId = await getMentorFromToken(request);
58+
const adminId = await getAdminFromToken(request);
59+
if (!mentorId && !adminId) {
60+
return NextResponse.json(
61+
{ success: false, error: 'Unauthorized' },
62+
{ status: 401 }
63+
);
64+
}
1565

1666
const application = await DSOCApplication.findById(id)
1767
.populate('project', 'title organization status mentors')
@@ -24,6 +74,16 @@ export async function GET(
2474
{ status: 404 }
2575
);
2676
}
77+
78+
if (mentorId) {
79+
const project = application.project as { mentors?: Array<{ _id?: string } | string> } | null;
80+
if (!hasMentorAccess(mentorId, project)) {
81+
return NextResponse.json(
82+
{ success: false, error: 'Forbidden' },
83+
{ status: 403 }
84+
);
85+
}
86+
}
2787

2888
return NextResponse.json({
2989
success: true,
@@ -46,8 +106,16 @@ export async function PUT(
46106
try {
47107
await connectDB();
48108
const { id } = await params;
49-
50-
// TODO: Add mentor/admin authentication check
109+
110+
const mentorId = await getMentorFromToken(request);
111+
const adminId = await getAdminFromToken(request);
112+
if (!mentorId && !adminId) {
113+
return NextResponse.json(
114+
{ success: false, error: 'Unauthorized' },
115+
{ status: 401 }
116+
);
117+
}
118+
51119
const body = await request.json();
52120
const { status, mentorNotes, adminNotes, score } = body;
53121

@@ -59,6 +127,19 @@ export async function PUT(
59127
{ status: 404 }
60128
);
61129
}
130+
131+
if (mentorId) {
132+
const project = await DSOCProject.findById(application.project)
133+
.select('mentors')
134+
.lean();
135+
136+
if (!project || !hasMentorAccess(mentorId, project)) {
137+
return NextResponse.json(
138+
{ success: false, error: 'Forbidden' },
139+
{ status: 403 }
140+
);
141+
}
142+
}
62143

63144
// Update application
64145
if (status) application.status = status;

app/api/dsoc/applications/route.ts

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ async function getMenteeFromToken(request: NextRequest) {
1818
}
1919
}
2020

21+
// Helper to get mentor from token
22+
async function getMentorFromToken(request: NextRequest) {
23+
const token = request.cookies.get('dsoc-mentor-token')?.value;
24+
if (!token) return null;
25+
26+
try {
27+
const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as { id: string; role: string };
28+
if (decoded.role !== 'dsoc-mentor') return null;
29+
return decoded.id;
30+
} catch {
31+
return null;
32+
}
33+
}
34+
2135
// GET all applications (with filters)
2236
export async function GET(request: NextRequest) {
2337
try {
@@ -26,15 +40,41 @@ export async function GET(request: NextRequest) {
2640
const searchParams = request.nextUrl.searchParams;
2741
const projectId = searchParams.get('project');
2842
const status = searchParams.get('status');
29-
const menteeId = await getMenteeFromToken(request);
43+
const mentorOnly = searchParams.get('mentor') === 'true';
44+
const menteeOnly = searchParams.get('my') === 'true';
45+
const menteeId = menteeOnly ? await getMenteeFromToken(request) : null;
3046

3147
const query: any = {};
32-
33-
if (projectId) query.project = projectId;
34-
if (status) query.status = status;
35-
if (menteeId && searchParams.get('my') === 'true') {
36-
query.mentee = menteeId;
48+
49+
if (mentorOnly) {
50+
const mentorId = await getMentorFromToken(request);
51+
if (!mentorId) {
52+
return NextResponse.json(
53+
{ success: false, error: 'Unauthorized' },
54+
{ status: 401 }
55+
);
56+
}
57+
58+
const mentorProjects = await DSOCProject.find({ mentors: mentorId })
59+
.select('_id')
60+
.lean();
61+
62+
const mentorProjectIds = mentorProjects.map((project) => project._id.toString());
63+
64+
if (projectId) {
65+
if (!mentorProjectIds.includes(projectId)) {
66+
return NextResponse.json({ success: true, data: [] });
67+
}
68+
query.project = projectId;
69+
} else {
70+
query.project = { $in: mentorProjectIds };
71+
}
72+
} else {
73+
if (projectId) query.project = projectId;
74+
if (menteeId) query.mentee = menteeId;
3775
}
76+
77+
if (status) query.status = status;
3878

3979
const applications = await DSOCApplication.find(query)
4080
.populate('project', 'title organization status')

app/api/dsoc/mentor/me/route.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import connectDB from '@/lib/db';
3+
import { DSOCMentor } from '@/models/DSOCMentor';
4+
import jwt from 'jsonwebtoken';
5+
6+
export async function GET(request: NextRequest) {
7+
try {
8+
await connectDB();
9+
10+
const token = request.cookies.get('dsoc-mentor-token')?.value;
11+
if (!token) {
12+
return NextResponse.json({ success: false }, { status: 200 });
13+
}
14+
15+
const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as {
16+
id: string;
17+
role: string;
18+
};
19+
20+
if (decoded.role !== 'dsoc-mentor') {
21+
return NextResponse.json({ success: false }, { status: 200 });
22+
}
23+
24+
const mentor = await DSOCMentor.findById(decoded.id)
25+
.select('_id name email username isActive')
26+
.lean();
27+
28+
if (!mentor || !mentor.isActive) {
29+
return NextResponse.json({ success: false }, { status: 200 });
30+
}
31+
32+
return NextResponse.json({
33+
success: true,
34+
data: {
35+
id: mentor._id,
36+
name: mentor.name,
37+
email: mentor.email,
38+
username: mentor.username,
39+
},
40+
});
41+
} catch (error) {
42+
console.error('Error checking DSOC mentor session:', error);
43+
return NextResponse.json({ success: false }, { status: 200 });
44+
}
45+
}

app/api/dsoc/projects/route.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@ import { NextRequest, NextResponse } from 'next/server';
22
import connectDB from '@/lib/db';
33
import '@/models/DSOCMentor';
44
import { DSOCProject } from '@/models/DSOCProject';
5+
import jwt from 'jsonwebtoken';
6+
7+
async function getMentorFromToken(request: NextRequest) {
8+
const token = request.cookies.get('dsoc-mentor-token')?.value;
9+
if (!token) return null;
10+
11+
try {
12+
const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as { id: string; role: string };
13+
if (decoded.role !== 'dsoc-mentor') return null;
14+
return decoded.id;
15+
} catch {
16+
return null;
17+
}
18+
}
519

620
// GET all projects with filtering
721
export async function GET(request: NextRequest) {
@@ -16,10 +30,22 @@ export async function GET(request: NextRequest) {
1630
const search = searchParams.get('search');
1731
const limit = parseInt(searchParams.get('limit') || '50');
1832
const page = parseInt(searchParams.get('page') || '1');
33+
const mentorOnly = searchParams.get('mentor') === 'true';
1934

2035
// Build query
2136
const query: any = { isActive: true };
2237

38+
if (mentorOnly) {
39+
const mentorId = await getMentorFromToken(request);
40+
if (!mentorId) {
41+
return NextResponse.json(
42+
{ success: false, error: 'Unauthorized' },
43+
{ status: 401 }
44+
);
45+
}
46+
query.mentors = mentorId;
47+
}
48+
2349
if (status) query.status = status;
2450
if (difficulty) query.difficulty = difficulty;
2551
if (technology) query.technologies = { $in: [technology] };

0 commit comments

Comments
 (0)