-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
414 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
'use client' | ||
|
||
import { useState, useEffect } from 'react' | ||
import Image from 'next/image' | ||
import { Button } from "@/components/ui/button" | ||
|
||
interface Submission { | ||
_id: string | ||
firstName: string | ||
lastName: string | ||
companyName: string | ||
email: string | ||
address: string | ||
phone: string | ||
originalImage: string | ||
generatedImage: string | ||
createdAt: string | ||
} | ||
|
||
export default function KubeConBoothPage() { | ||
const [submissions, setSubmissions] = useState<Submission[]>([]) | ||
const [isLoading, setIsLoading] = useState(true) | ||
const [error, setError] = useState<string | null>(null) | ||
|
||
useEffect(() => { | ||
fetchSubmissions() | ||
}, []) | ||
|
||
const fetchSubmissions = async () => { | ||
try { | ||
setIsLoading(true) | ||
setError(null) | ||
console.log('Fetching submissions...') | ||
const response = await fetch('/api/admin/submissions') | ||
if (!response.ok) { | ||
throw new Error(`Failed to fetch submissions: ${response.statusText}`) | ||
} | ||
const data = await response.json() | ||
console.log('Submissions fetched successfully:', data.length) | ||
setSubmissions(data) | ||
} catch (err) { | ||
console.error('Error fetching submissions:', err) | ||
setError('An error occurred while fetching submissions. Please try again.') | ||
} finally { | ||
setIsLoading(false) | ||
} | ||
} | ||
|
||
const handleDownload = (base64Image: string, fileName: string) => { | ||
const link = document.createElement('a') | ||
link.href = `data:image/jpeg;base64,${base64Image}` | ||
link.download = fileName | ||
document.body.appendChild(link) | ||
link.click() | ||
document.body.removeChild(link) | ||
} | ||
|
||
if (isLoading) { | ||
return <div className="text-center p-8">Loading submissions...</div> | ||
} | ||
|
||
if (error) { | ||
return ( | ||
<div className="text-center p-8"> | ||
<p className="text-red-500 mb-4">{error}</p> | ||
<Button onClick={fetchSubmissions}>Retry</Button> | ||
</div> | ||
) | ||
} | ||
|
||
if (submissions.length === 0) { | ||
return <div className="text-center p-8">No submissions found.</div> | ||
} | ||
|
||
return ( | ||
<div className="container mx-auto px-4 py-8"> | ||
<h1 className="text-2xl font-bold mb-6">KubeCon Booth Submissions</h1> | ||
<div className="overflow-x-auto"> | ||
<table className="min-w-full bg-white"> | ||
<thead className="bg-gray-100"> | ||
<tr> | ||
<th className="py-2 px-4 border-b text-left">Name</th> | ||
<th className="py-2 px-4 border-b text-left">Company</th> | ||
<th className="py-2 px-4 border-b text-left">Email</th> | ||
<th className="py-2 px-4 border-b text-left">Address</th> | ||
<th className="py-2 px-4 border-b text-left">Phone</th> | ||
<th className="py-2 px-4 border-b text-left">Original Image</th> | ||
<th className="py-2 px-4 border-b text-left">Generated Image</th> | ||
<th className="py-2 px-4 border-b text-left">Created At</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{submissions.map((submission) => ( | ||
<tr key={submission._id}> | ||
<td className="py-2 px-4 border-b">{`${submission.firstName} ${submission.lastName}`}</td> | ||
<td className="py-2 px-4 border-b">{submission.companyName}</td> | ||
<td className="py-2 px-4 border-b">{submission.email}</td> | ||
<td className="py-2 px-4 border-b">{submission.address}</td> | ||
<td className="py-2 px-4 border-b">{submission.phone}</td> | ||
<td className="py-2 px-4 border-b"> | ||
<div className="relative w-20 h-20"> | ||
<Image | ||
src={`data:image/jpeg;base64,${submission.originalImage}`} | ||
alt="Original Image" | ||
fill | ||
className="object-cover" | ||
/> | ||
</div> | ||
<Button | ||
onClick={() => handleDownload(submission.originalImage, `original_${submission._id}.jpg`)} | ||
className="mt-2 text-xs" | ||
> | ||
Download | ||
</Button> | ||
</td> | ||
<td className="py-2 px-4 border-b"> | ||
<div className="relative w-20 h-20"> | ||
<Image | ||
src={`data:image/jpeg;base64,${submission.generatedImage}`} | ||
alt="Generated Image" | ||
fill | ||
className="object-cover" | ||
/> | ||
</div> | ||
<Button | ||
onClick={() => handleDownload(submission.generatedImage, `generated_${submission._id}.jpg`)} | ||
className="mt-2 text-xs" | ||
> | ||
Download | ||
</Button> | ||
</td> | ||
<td className="py-2 px-4 border-b">{new Date(submission.createdAt).toLocaleString()}</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { NextResponse } from 'next/server' | ||
import { MongoClient } from 'mongodb' | ||
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' | ||
import { Readable } from 'stream' | ||
|
||
const s3Client = new S3Client({ | ||
region: process.env.AWS_REGION || 'us-east-1', | ||
credentials: { | ||
accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', | ||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '', | ||
}, | ||
}) | ||
|
||
const BUCKET_NAME = process.env.AWS_S3_BUCKET_NAME || 'kubecon-booth-1080b684c6ad44c9b835761fa92dece8' | ||
|
||
async function streamToBuffer(stream: Readable): Promise<Buffer> { | ||
const chunks: Buffer[] = [] | ||
|
||
for await (const chunk of stream) { | ||
chunks.push(Buffer.from(chunk)) | ||
} | ||
|
||
return Buffer.concat(chunks) | ||
} | ||
|
||
async function getImageFromS3(key: string): Promise<string> { | ||
try { | ||
console.log(`Fetching image from S3: ${key}`) | ||
const command = new GetObjectCommand({ | ||
Bucket: BUCKET_NAME, | ||
Key: key, | ||
}) | ||
|
||
const response = await s3Client.send(command) | ||
if (!response.Body) { | ||
throw new Error('No image data received from S3') | ||
} | ||
|
||
const stream = response.Body as Readable | ||
const buffer = await streamToBuffer(stream) | ||
console.log(`Successfully fetched and processed image: ${key}`) | ||
return buffer.toString('base64') | ||
} catch (error) { | ||
console.error(`Error fetching image from S3: ${key}`, error) | ||
throw error | ||
} | ||
} | ||
|
||
export async function GET() { | ||
try { | ||
console.log('Connecting to MongoDB...') | ||
const client = await MongoClient.connect(process.env.MONGODB_URI || '') | ||
const db = client.db('kubecon-booth') | ||
console.log('Connected to MongoDB, fetching submissions...') | ||
const submissions = await db.collection('submissions').find({}).toArray() | ||
console.log(`Found ${submissions.length} submissions`) | ||
|
||
const processedSubmissions = await Promise.all(submissions.map(async (submission) => { | ||
try { | ||
const originalImage = await getImageFromS3(submission.originalImagePath) | ||
const generatedImage = await getImageFromS3(submission.generatedImagePath) | ||
|
||
return { | ||
...submission, | ||
originalImage, | ||
generatedImage, | ||
} | ||
} catch (error) { | ||
console.error(`Error processing submission ${submission._id}:`, error) | ||
return { | ||
...submission, | ||
originalImage: null, | ||
generatedImage: null, | ||
error: 'Failed to fetch images', | ||
} | ||
} | ||
})) | ||
|
||
await client.close() | ||
console.log('Successfully processed all submissions') | ||
|
||
return NextResponse.json(processedSubmissions) | ||
} catch (error) { | ||
console.error('Error fetching submissions:', error) | ||
return NextResponse.json({ error: 'Internal Server Error', details: error }, { status: 500 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.