Skip to content

Commit

Permalink
Merge pull request #625 from alan-turing-institute/new-media-storage
Browse files Browse the repository at this point in the history
Using new api route for screenshots
  • Loading branch information
RichGriff authored Sep 28, 2024
2 parents 305090a + 60f2c7e commit dfaf0c6
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 74 deletions.
95 changes: 37 additions & 58 deletions next_frontend/app/api/screenshot/route.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,49 @@
import fs from 'fs';
import path from 'path';
import { BlobServiceClient } from "@azure/storage-blob";
import { NextRequest, NextResponse } from 'next/server';

type CaptureProps = {
base64image: string,
id: number,
token: string
}

export async function POST(request: NextRequest) {
const { base64, id } = await request.json()
const filename = `chart-screenshot-case-${id}.png`
const { base64image, id, token }: CaptureProps = await request.json();
const filename = `chart-screenshot-case-${id}.png`;

const base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
const buffer = Buffer.from(base64Data, 'base64')
// Convert base64 string to Blob if needed
const blob = base64ToBlob(base64image, 'image/png');

try {
const containerName = 'sample-container'
const account = process.env.NEXT_PUBLIC_STORAGESOURCENAME

const blobSasUrl = 'https://teamedia.blob.core.windows.net/?sv=2022-11-02&ss=bfqt&srt=co&sp=rwdlacupiytfx&se=2025-05-06T03:42:08Z&st=2024-05-05T19:42:08Z&spr=https&sig=eAyqjGI6Tz5jzZi%2FWrVr%2BGfMnTR%2Fnbe8HLbDYuoVnMY%3D'

const blobServiceClient = new BlobServiceClient(blobSasUrl)

// Get a reference to a container
const containerClient = blobServiceClient.getContainerClient(containerName);

const blockBlobClient = containerClient.getBlockBlobClient(filename);
await blockBlobClient.uploadData(buffer);

// Return the URL of the uploaded image
const imageUrl = `https://${account}.blob.core.windows.net/${containerName}/${filename}`;
return NextResponse.json({ imageUrl })

const formdata = new FormData();
formdata.append("media", blob, filename);

const requestOptions: RequestInit = {
method: "POST",
body: formdata,
redirect: "follow",
headers: {
Authorization: `Token ${token}`,
},
};

const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/cases/${id}/image`, requestOptions);
const { message, data } = await response.json();
return NextResponse.json({ message, data });
} catch (error) {
console.log(error)
return NextResponse.json({ error, message: 'Couldnt upload image' })
console.log(error);
return NextResponse.json({ error, message: "Couldn't upload image" });
}
}

// Utility function to convert base64 to Blob
function base64ToBlob(base64: string, mimeType: string) {
const byteString = atob(base64.split(",")[1]); // Decode base64
const arrayBuffer = new ArrayBuffer(byteString.length);
const uint8Array = new Uint8Array(arrayBuffer);

// try {
// // Remove header from base64 string
// const base64Data = base64Image.replace(/^data:image\/\w+;base64,/, '');

// // Create buffer from base64 string
// const buffer = Buffer.from(base64Data, 'base64');

// // Save to azure
// const imageUrl = await saveToStorage(buffer, filename)
// return imageUrl
// } catch (error) {
// console.error('Error saving image:', error);
// throw error;
// }

////Read files from the file system using Node.js fs module
// const templatesDir = path.resolve(process.cwd(), 'caseTemplates');
// const files = fs.readdirSync(templatesDir);

// // Filter JSON files
// const jsonFiles = files.filter(file => file.endsWith('.json'));

// // Read JSON content and parse it
// const newTemplates = jsonFiles.map(file => {
// const filePath = path.join(templatesDir, file);
// const content = fs.readFileSync(filePath, 'utf-8');
// return JSON.parse(content);
// });

// // Find default case
// let defaultCase = newTemplates.find(c => c.name === 'empty') || newTemplates[0];
for (let i = 0; i < byteString.length; i++) {
uint8Array[i] = byteString.charCodeAt(i);
}

// return NextResponse.json({ newTemplates, defaultCase })
return new Blob([uint8Array], { type: mimeType });
}
9 changes: 4 additions & 5 deletions next_frontend/components/cases/ActionButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ const ActionButtons = ({ showCreateGoal, actions, notify, notifyError }: ActionB

const newImage = JSON.stringify({
id: assuranceCase.id,
base64: base64image
base64image,
token
});

const requestOptions: RequestInit = {
Expand All @@ -128,15 +129,13 @@ const ActionButtons = ({ showCreateGoal, actions, notify, notifyError }: ActionB
};

const response = await fetch("/api/screenshot", requestOptions)
const { imageUrl, error, message } = await response.json()
const { error, message, data } = await response.json()

if(error) {
notifyError(message)
}

if(imageUrl) {
notify('Screenshot Saved!')
}
notify('Screenshot Saved!')
}
}

Expand Down
46 changes: 37 additions & 9 deletions next_frontend/components/cases/CaseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const CaseCard = ({ assuranceCase } : CaseCardProps) => {

const [open, setOpen] = useState(false)
const [loading, setLoading] = useState(false)
const [imgSrc, setImgSrc] = useState(`https://teamedia.blob.core.windows.net/sample-container/chart-screenshot-case-${assuranceCase.id}.png`);
const [imgSrc, setImgSrc] = useState('');
// const [imgSrc, setImgSrc] = useState(`https://teamedia.blob.core.windows.net/sample-container/chart-screenshot-case-${assuranceCase.id}.png`);
// const [imageExists, setImageExists] = useState(true)
// const [imageUrl, setImageUrl] = useState<string>('')

Expand Down Expand Up @@ -59,20 +60,47 @@ const CaseCard = ({ assuranceCase } : CaseCardProps) => {
}
}

const fetchScreenshot = async () => {
try {
const requestOptions: RequestInit = {
method: "GET",
headers: {
Authorization: `Token ${token}`,
},
redirect: "follow"
};

const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/cases/${id}/image`, requestOptions)

if(response.status == 404) {
setImgSrc('/images/assurance-case-medium.png')
return
}

const result = await response.json()
setImgSrc(result.image)
} catch (error) {
console.log('Failed to fetch image')
}
}

useEffect(() => {
fetchScreenshot()
})

return (
<div className='group relative min-h-[420px]'>
<Link href={`/case/${assuranceCase.id}`}>
<Card className='flex flex-col justify-start items-start group-hover:bg-indigo-500/5 transition-all h-full'>
<CardHeader className='flex-1 w-full'>
<div className='relative flex aspect-video rounded-md mb-4 overflow-hidden'>
<Image
src={imgSrc}
alt={`Assurance Case ${assuranceCase.name} screenshot`}
fill
onError={() => {
setImgSrc('/images/assurance-case-medium.png');
}}
/>
{imgSrc && (
<Image
src={imgSrc}
alt={`Assurance Case ${assuranceCase.name} screenshot`}
fill
/>
)}
</div>
<CardTitle>{name}</CardTitle>
<CardDescription className='text-slate-900 dark:text-white'>{description}</CardDescription>
Expand Down
4 changes: 2 additions & 2 deletions next_frontend/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ const nextConfig = {
remotePatterns: [
{
protocol: "https",
hostname: "teamedia.blob.core.windows.net",
hostname: "stagingteastorageaccount.blob.core.windows.net",
port: "",
pathname: "/sample-container/**",
pathname: "**/*",
},
],
},
Expand Down

0 comments on commit dfaf0c6

Please sign in to comment.