Skip to content

Commit

Permalink
new api for cover, added meta tags (#108)
Browse files Browse the repository at this point in the history
* new api for cover, added meta tags

* fixed redundant props

* handled authenticated routes
  • Loading branch information
e-for-eshaan authored Dec 13, 2023
1 parent 92dd296 commit 24bd78c
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 57 deletions.
Binary file added public/assets/images/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 7 additions & 3 deletions src/api-helpers/vercel-cover-generator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import {
import { getInterFonts } from '@/api-helpers/fonts';
import { COVER_WIDTH } from '@/constants/general';
import LogoSvg from '@/assets/unwrapped-logo.svg';
import { arrayBufferToBuffer } from './general';

export const createCoverUsingVercel = async (
username: string,
images: string[]
): Promise<ImageResponse> => {
const interFonts = await getInterFonts('browser');
): Promise<Buffer> => {
const interFonts = await getInterFonts('node');
try {
const generatedImage = new ImageResponse(
(
Expand Down Expand Up @@ -96,7 +97,10 @@ export const createCoverUsingVercel = async (
}
);

return generatedImage;
const imageArrayBuffer = await generatedImage.arrayBuffer();
const imageBuffer = arrayBufferToBuffer(imageArrayBuffer);

return imageBuffer;
} catch (error) {
console.error(error);
throw new Error(`Image creation failed for cover`);
Expand Down
4 changes: 3 additions & 1 deletion src/contexts/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export const AppStateProvider = ({ children }: AppStateProviderInterface) => {
useSession({
required: true,
onUnauthenticated() {
router.pathname !== '/' && router.push('/');
router.pathname !== '/' &&
!router.pathname.startsWith('/view') &&
router.push('/');
}
});

Expand Down
34 changes: 34 additions & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,40 @@ export default function App({
return (
<>
<Head>
{/* Favicon */}
<link rel="icon" href="/favicon.ico" />

<title>Unwrapped by Middleware</title>

<meta
name="keywords"
content="developer,unwrap,app,recap,yearly,review,annual,github,spotify,wrapped,year-in-review,year in review,middleware,software,engineering,annual review,appraisal,new year,new year's eve,december,github unwrapped,githubunwrapped,github-unwrapped,software engineers,jayant bhawal,eshaan yadav,samad yar khan,shivam singh,dhruv agarwal,varun narula,adnan hashmi,cadence,management,dora metrics,open source,open-source,contributions,bottlenecks,pull requests,prs,issues,commits,lines of code,loc,languages,repositories,repos,stars,forks,github insights"
/>

<meta property="og:title" content={`Unwrapped by Middleware`} />
<meta
property="og:description"
content="A yearly recap of your GitHub, like Spotify Wrapped. If you are a developer, you will love it! ❤️"
/>
<meta property="og:image" content="/assets/images/og-image.png" />
<meta property="og:url" content={process.env.NEXT_PUBLIC_APP_URL} />
<meta property="og:type" content="website" />

{/* Other meta tags */}
<meta
name="description"
content="A yearly recap of your GitHub, like Spotify Wrapped. If you are a developer, you will love it! ❤️"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

{/* Twitter meta tags */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Unwrapped by Middleware" />
<meta
name="twitter:description"
content="A yearly recap of your GitHub, like Spotify Wrapped. If you are a developer, you will love it! ❤️"
/>
<meta name="twitter:image" content="/assets/images/og-image.png" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
Expand Down
34 changes: 0 additions & 34 deletions src/pages/api/get_cover.ts

This file was deleted.

70 changes: 70 additions & 0 deletions src/pages/api/get_cover/[...params].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { fetchSavedCards } from '@/api-helpers/persistance';
import { createCoverUsingVercel } from '@/api-helpers/vercel-cover-generator';
import { CardTypes } from '@/types/cards';
import {
bcryptGen,
extractFilenameWithoutExtension
} from '@/utils/stringHelpers';
import { NextApiRequest, NextApiResponse } from 'next';

const fetchAndDownloadImageBuffer = async (
req: NextApiRequest,
res: NextApiResponse
) => {
const [username, hash] = req.query.params as string[];

if (!username || !hash) {
return res.status(400).json({
message: 'Invalid parameters, must pass valid username and hash.'
});
}

const generatedHash = bcryptGen(username);

if (generatedHash !== hash) {
return res.status(400).json({
message: 'Invalid parameters, must pass valid hash.'
});
}
const imageData = await fetchSavedCards(username);

const images = imageData
.sort((a, b) => {
const indexA = priority.indexOf(
extractFilenameWithoutExtension(a.fileName).toUpperCase() as CardTypes
);
const indexB = priority.indexOf(
extractFilenameWithoutExtension(b.fileName).toUpperCase() as CardTypes
);

return indexA - indexB;
})
.slice(0, 3)
.map((image) => {
const file = extractFilenameWithoutExtension(image.fileName);
const uniqueHash = bcryptGen(username + file);
const domain = process.env.NEXTAUTH_URL;
return `${domain}/shared/${username}/${file}/${uniqueHash}`;
});

const getCoverImage = await createCoverUsingVercel(username, images);
res.setHeader('Content-Type', 'image/jpeg');
res.setHeader('Content-Length', getCoverImage.length);
res.send(getCoverImage);
};

export default fetchAndDownloadImageBuffer;

const priority = [
CardTypes.YOUR_CONTRIBUTIONS,
CardTypes.PR_REVIEWED_VS_AUTHORED,
CardTypes.IT_TAKES_A_VILLAGE,
CardTypes.TOP_REVIEWERS,
CardTypes.CONTRIBUTION_STREAK,
CardTypes.GUARDIAN_OF_PROD,
CardTypes.ZEN_OR_NINJA,
CardTypes.DAY_NIGHT_CYCLE,
CardTypes.OSS_CONTRIBUTION,
CardTypes.PRODUCTION_BREAKING,
CardTypes.UNWRAPPED_INTRO
];
83 changes: 64 additions & 19 deletions src/pages/view/[username]/[...hash].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Confetti from 'react-confetti';
import toast from 'react-hot-toast';
import { UpdatedImageFile } from '@/types/images';
import { useRouter } from 'next/router';
import Head from 'next/head';

export default function StatsUnwrapped() {
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -61,25 +62,69 @@ export default function StatsUnwrapped() {
);
}
return (
<div className="items-center justify-center p-4 min-h-screen w-full flex flex-col gap-10 text-center">
<div>
<h2 className="text-2xl">
Unwrap{' '}
<span className="font-bold text-[#bc9aef]">{userName}&apos;s</span>{' '}
GitHub journey of 2023! 🎉
</h2>
</div>
{images?.length && <Confetti numberOfPieces={400} recycle={false} />}
{images?.length && (
<div className="flex flex-col items-center gap-4 w-full ">
<SwiperCarousel
hideShareButtons
userName={userName}
images={images}
singleImageSharingCallback={downloadImage}
/>
<>
<Head>
{/* Favicon */}
<link rel="icon" href="/favicon.ico" />

<title>{userName}&apos;s Unwrapped 2023</title>

{/* Open Graph meta tags */}
<link
rel={`${process.env.NEXT_PUBLIC_APP_URL}/api/get_cover/${userName}/${hash}`}
/>

<meta property="og:title" content={`${userName}'s Unwrapped 2023`} />
<meta
property="og:description"
content={`${userName}'s 2023 on GitHub Unwrapped, by Middleware. It's like Spotify Wrapped, but for developers. You'll love it.`}
/>
<meta
property="og:image"
content={`${process.env.NEXT_PUBLIC_APP_URL}/api/get_cover/${userName}/${hash}`}
/>
<meta property="og:url" content={process.env.NEXT_PUBLIC_APP_URL} />
<meta property="og:type" content="website" />

{/* Other meta tags */}
<meta
name="description"
content={`${userName}'s 2023 on GitHub Unwrapped, by Middleware. It's like Spotify Wrapped, but for developers. You'll love it.`}
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

{/* Twitter meta tags */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Unwrapped by Middleware" />
<meta
name="twitter:description"
content={`${userName}'s 2023 on GitHub Unwrapped, by Middleware. It's like Spotify Wrapped, but for developers. You'll love it.`}
/>
<meta
name="twitter:image"
content={`${process.env.NEXT_PUBLIC_APP_URL}/api/get_cover/${userName}/${hash}`}
/>
</Head>
<div className="items-center justify-center p-4 min-h-screen w-full flex flex-col gap-10 text-center">
<div>
<h2 className="text-2xl">
Unwrap{' '}
<span className="font-bold text-[#bc9aef]">{userName}&apos;s</span>{' '}
GitHub journey of 2023! 🎉
</h2>
</div>
)}
</div>
{images?.length && <Confetti numberOfPieces={400} recycle={false} />}
{images?.length && (
<div className="flex flex-col items-center gap-4 w-full ">
<SwiperCarousel
hideShareButtons
userName={userName}
images={images}
singleImageSharingCallback={downloadImage}
/>
</div>
)}
</div>
</>
);
}

0 comments on commit 24bd78c

Please sign in to comment.