Skip to content

Commit ee80fd3

Browse files
committed
add admin link to navbar, update component model with previewUrl, and integrate Cloudinary for image uploads
1 parent 5f753dd commit ee80fd3

File tree

10 files changed

+526
-190
lines changed

10 files changed

+526
-190
lines changed

app/api/get-all-pending-components/route.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export async function GET(req: NextRequest, res: NextResponse) {
2222
// Show all pending components
2323
await connectDb();
2424
const pendingComponents = await Component.find({ status: "pending" })
25-
.select("_id code title tags description author status installationGuide usageGuide props");
25+
console.log(pendingComponents);
2626

2727
const count = pendingComponents.length;
2828

app/api/gift-component/route.ts

+85-28
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,116 @@
11
import { NextRequest, NextResponse } from "next/server";
2-
import Component from "@/models/components.model";
2+
import Component from "@/models/components.model";
33
import { connectDb } from "@/config/dbConfig/dbConfig";
44
import rateLimit from "@/config/ratelimit/ratelimit";
5+
import { v2 as cloudinary } from 'cloudinary';
6+
import { uploadImage } from "@/config/uploadImage/upload";
57

6-
type component = {
8+
type Component = {
79
code: string;
810
title: string;
911
tags: string[];
1012
description: string;
1113
author: string;
14+
previewUrl: string;
1215
status: string;
1316
installationGuide: string | null;
1417
usageGuide: string | null;
1518
props: string | null;
1619
}
1720

21+
cloudinary.config({
22+
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
23+
api_key: process.env.CLOUDINARY_API_KEY,
24+
api_secret: process.env.CLOUDINARY_API_SECRET,
25+
secure: true,
26+
});
1827

19-
export async function POST(req:NextRequest,res:NextResponse){
28+
export async function POST(req: NextRequest) {
2029
try {
21-
22-
if(rateLimit(req) === false){
23-
return NextResponse.json({message:"Too many request try after some time"},{status:429});
30+
if (rateLimit(req) === false) {
31+
return NextResponse.json({ message: "Too many requests, try after some time" }, { status: 429 });
2432
}
2533

26-
const reqBody = await req.json();
27-
if(!reqBody.code || !reqBody.title || !reqBody.tags || !reqBody.description || !reqBody.author){
28-
return NextResponse.json({message:"all fields are required"},{status:400});
34+
// Parse FormData instead of JSON
35+
const formData = await req.formData();
36+
37+
// Extract fields from FormData
38+
const code = formData.get('code') as string;
39+
const title = formData.get('title') as string;
40+
const tags = formData.get('tags') as string;
41+
const description = formData.get('description') as string;
42+
const author = formData.get('author') as string;
43+
const installationGuide = formData.get('installationGuide') as string;
44+
const usageGuide = formData.get('usageGuide') as string;
45+
const props = formData.get('props') as string;
46+
const previewImage = formData.get('previewImage') as File | null;
47+
48+
// Validation
49+
if (!code || !title || !tags || !description || !author) {
50+
return NextResponse.json({ message: "All fields are required" }, { status: 400 });
2951
}
3052

31-
//check if code is not malicous
32-
if(reqBody.code.length > 60 && reqBody.installationGuide.length <= 0 && reqBody.usageGuide.length <= 0){
33-
return NextResponse.json({message:"code is long so installion guide and usage guide is required"},{status:400});
53+
// Check if code is not malicious
54+
if (code.length > 60 && (!installationGuide || !usageGuide)) {
55+
return NextResponse.json({
56+
message: "Code is long so installation guide and usage guide are required"
57+
}, { status: 400 });
3458
}
3559

36-
const newComponent:component = {
37-
code:reqBody.code,
38-
title:reqBody.title,
39-
tags:reqBody.tags,
40-
description:reqBody.description,
41-
author:reqBody.author,
42-
status:"pending",
43-
installationGuide:reqBody.installationGuide || null,
44-
usageGuide:reqBody.usageGuide || null,
45-
props:reqBody.props || null
60+
let avatarUrl = '';
61+
62+
// Handle image upload if present
63+
if (previewImage) {
64+
// Validate file type
65+
if (!previewImage.type.startsWith('image/')) {
66+
return NextResponse.json({
67+
message: "Invalid file type. Only images are allowed"
68+
}, { status: 400 });
69+
}
70+
71+
// Convert File to Buffer
72+
const bytes = await previewImage.arrayBuffer();
73+
const buffer = Buffer.from(bytes);
74+
75+
// Upload to Cloudinary
76+
try {
77+
avatarUrl = await uploadImage(buffer) as string;
78+
console.log("Uploaded avatar URL:", avatarUrl);
79+
} catch (error) {
80+
console.error("Image upload failed:", error);
81+
return NextResponse.json({
82+
message: "Failed to upload image"
83+
}, { status: 500 });
84+
}
4685
}
86+
87+
const newComponent: Component = {
88+
code,
89+
title,
90+
tags: tags.split(',').map(tag => tag.trim()), // Convert comma-separated string to array
91+
description,
92+
author,
93+
previewUrl: avatarUrl,
94+
status: "pending",
95+
installationGuide: installationGuide || null,
96+
usageGuide: usageGuide || null,
97+
props: props || null
98+
};
99+
100+
console.log("New component:", newComponent.previewUrl);
47101

48102
await connectDb();
49-
//save to db
50103
const savedComponent = await Component.create(newComponent);
51104

52-
return NextResponse.json({message:"code is under review . it will take less than 24hr to review ",savedComponent},{status:201});
105+
return NextResponse.json({
106+
message: "Code is under review. It will take less than 24hr to review",
107+
savedComponent
108+
}, { status: 201 });
53109

54-
55-
} catch (error:any) {
56-
return NextResponse.json({message:error.message},{status:500});
57-
110+
} catch (error: any) {
111+
console.error("Error in POST handler:", error);
112+
return NextResponse.json({
113+
message: "Internal server error"
114+
}, { status: 500 });
58115
}
59116
}

0 commit comments

Comments
 (0)