mirror of
				https://github.com/r-freeman/portfolio.git
				synced 2025-10-31 22:31:12 +00:00 
			
		
		
		
	Add og meta tags
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Build And Publish / BuildAndPublish (push) Successful in 2m45s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Build And Publish / BuildAndPublish (push) Successful in 2m45s
				
			This commit is contained in:
		
							parent
							
								
									18161a50e8
								
							
						
					
					
						commit
						412927d663
					
				| @ -4,15 +4,22 @@ import Image from 'next/image' | |||||||
| import {Container} from '@/components/common/Container' | import {Container} from '@/components/common/Container' | ||||||
| import {MailIcon} from '@/components/icons/MailIcon' | import {MailIcon} from '@/components/icons/MailIcon' | ||||||
| import {GitHubIcon, LinkedInIcon} from '@/components/icons/SocialIcons' | import {GitHubIcon, LinkedInIcon} from '@/components/icons/SocialIcons' | ||||||
|  | import {metadata as _metadata} from '@/lib/generateMetadata' | ||||||
| import clsx from 'clsx' | import clsx from 'clsx' | ||||||
| import me from '@/public/images/me.jpg' | import me from '@/public/images/me.jpg' | ||||||
| import awsCCPBadge from '@/public/images/aws-certified-cloud-practitioner-badge.png' | import awsCCPBadge from '@/public/images/aws-certified-cloud-practitioner-badge.png' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | const meta = { | ||||||
|     title: 'About - Ryan Freeman', |     title: 'About', | ||||||
|     description: 'I’m Ryan. I live in Dublin, Ireland where I work as a software engineer.' |     heading: 'I\'m Ryan. I live in Dublin, Ireland where I work as a software engineer.', | ||||||
|  |     description: 'I\'ve always had an affinity for technology, and loved making things for as long as I can remember. ' + | ||||||
|  |         'My first computer was an Amstrad CPC 464 way back in the 90s, which is ancient by modern standards. ' + | ||||||
|  |         'My passion for tinkering continued through my teens and into adulthood where I eventually found my way into software engineering.', | ||||||
|  |     type: 'website' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({...meta, heading: meta.heading}) | ||||||
|  | 
 | ||||||
| function SocialLink({ | function SocialLink({ | ||||||
|                         className, |                         className, | ||||||
|                         href, |                         href, | ||||||
| @ -55,14 +62,11 @@ export default async function About() { | |||||||
|                 </div> |                 </div> | ||||||
|                 <div className="lg:order-first lg:row-span-2"> |                 <div className="lg:order-first lg:row-span-2"> | ||||||
|                     <h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl bg-clip-text dark:text-transparent bg-gradient-to-r from-blue-400 to-emerald-400"> |                     <h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl bg-clip-text dark:text-transparent bg-gradient-to-r from-blue-400 to-emerald-400"> | ||||||
|                         I’m Ryan. I live in Dublin, Ireland where I work as a software engineer. |                         {meta.heading} | ||||||
|                     </h1> |                     </h1> | ||||||
|                     <div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400"> |                     <div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400"> | ||||||
|                         <p> |                         <p> | ||||||
|                             I've always had an affinity for technology, and loved making things for as long as I can |                             {meta.description} | ||||||
|                             remember. My first computer was an Amstrad CPC 464 way back in the 90s, which is ancient by modern |  | ||||||
|                             standards. My passion for tinkering continued through my teens and into adulthood where I |  | ||||||
|                             eventually found my way into software engineering. |  | ||||||
|                         </p> |                         </p> | ||||||
|                         <p> |                         <p> | ||||||
|                             In terms of my experience to date, I have a strong foundation in both front-end and back-end |                             In terms of my experience to date, I have a strong foundation in both front-end and back-end | ||||||
|  | |||||||
| @ -29,11 +29,11 @@ export async function GET(request: Request) { | |||||||
|                     width: '100%', |                     width: '100%', | ||||||
|                     height: '100%', |                     height: '100%', | ||||||
|                     display: 'flex', |                     display: 'flex', | ||||||
|                     textAlign: 'center', |                     textAlign: 'left', | ||||||
|                     alignItems: 'center', |                     alignItems: 'center', | ||||||
|                     justifyContent: 'center', |                     justifyContent: 'center', | ||||||
|                     lineHeight: '1', |                     lineHeight: '1.1', | ||||||
|                     padding: '0 128px' |                     padding: '0 64px' | ||||||
|                 }} |                 }} | ||||||
|             > |             > | ||||||
|                 <div |                 <div | ||||||
|  | |||||||
| @ -6,7 +6,10 @@ import {Footer} from '@/components/common/Footer' | |||||||
| import '@/styles/tailwind.css' | import '@/styles/tailwind.css' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const metadata = { | ||||||
|     title: 'Ryan Freeman - Full-stack software engineer from Dublin, Ireland.', |     title: { | ||||||
|  |         default: 'Ryan Freeman - Full-stack software engineer based in Dublin, Ireland.', | ||||||
|  |         template: '%s - Ryan Freeman' | ||||||
|  |     }, | ||||||
|     description: 'Full-stack software engineer who enjoys building cloud-native applications.', |     description: 'Full-stack software engineer who enjoys building cloud-native applications.', | ||||||
|     metadataBase: new URL('https://ryanfreeman.dev'), |     metadataBase: new URL('https://ryanfreeman.dev'), | ||||||
|     alternates: { |     alternates: { | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								app/page.tsx
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								app/page.tsx
									
									
									
									
									
								
							| @ -8,6 +8,18 @@ import {GitHubIcon, LinkedInIcon} from '@/components/icons/SocialIcons' | |||||||
| import {getAllArticles} from '@/lib/getAllArticles' | import {getAllArticles} from '@/lib/getAllArticles' | ||||||
| import {formatDate} from '@/lib/formatDate' | import {formatDate} from '@/lib/formatDate' | ||||||
| import type {Article} from '@/types' | import type {Article} from '@/types' | ||||||
|  | import {metadata as _metadata} from '@/lib/generateMetadata' | ||||||
|  | 
 | ||||||
|  | const meta = { | ||||||
|  |     title: 'Ryan Freeman - Full-stack software engineer based in Dublin, Ireland.', | ||||||
|  |     heading: 'Full-stack software engineer who enjoys building cloud-native applications.', | ||||||
|  |     description: 'Hi. I\'m Ryan, a software engineer based in Dublin, Ireland. I\'m currently working in the aviation industry for Aer Lingus. ' + | ||||||
|  |         'I am passionate about personal growth and progressing in my career. ' + | ||||||
|  |         'This is my personal website where you can learn more about me, read articles I\'ve written and see projects I\'ve worked on.', | ||||||
|  |     type: 'website' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const metadata = _metadata({...meta, heading: meta.heading}) | ||||||
| 
 | 
 | ||||||
| function Article(article: Article) { | function Article(article: Article) { | ||||||
|     return ( |     return ( | ||||||
| @ -41,13 +53,10 @@ export default async function Home() { | |||||||
|             <Container className="mt-9"> |             <Container className="mt-9"> | ||||||
|                 <div className="max-w-2xl"> |                 <div className="max-w-2xl"> | ||||||
|                     <h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl bg-clip-text dark:text-transparent bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500"> |                     <h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl bg-clip-text dark:text-transparent bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500"> | ||||||
|                         Full-stack software engineer who enjoys building cloud-native applications. |                         {meta.heading} | ||||||
|                     </h1> |                     </h1> | ||||||
|                     <p className="mt-6 text-base text-zinc-600 dark:text-zinc-400"> |                     <p className="mt-6 text-base text-zinc-600 dark:text-zinc-400"> | ||||||
|                         Hi. I'm Ryan, a software engineer based in Dublin, Ireland. I'm currently working in the |                         {meta.description} | ||||||
|                         aviation industry for Aer Lingus. I am passionate about personal growth and progressing in my career. This |  | ||||||
|                         is my personal website where you can learn more about me, read articles I've written and see projects |  | ||||||
|                         I've worked on. |  | ||||||
|                     </p> |                     </p> | ||||||
|                     <div className="mt-6 flex gap-6"> |                     <div className="mt-6 flex gap-6"> | ||||||
|                         <SocialLink |                         <SocialLink | ||||||
|  | |||||||
| @ -4,13 +4,18 @@ import {SimpleLayout} from '@/components/layouts/SimpleLayout' | |||||||
| import {Card} from '@/components/ui/Card' | import {Card} from '@/components/ui/Card' | ||||||
| import {getPinnedRepos} from '@/lib/github' | import {getPinnedRepos} from '@/lib/github' | ||||||
| import {numberFormat} from '@/lib/numberFormat' | import {numberFormat} from '@/lib/numberFormat' | ||||||
| import React from 'react'; | import {metadata as _metadata} from '@/lib/generateMetadata' | ||||||
|  | import React from 'react' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | const meta = { | ||||||
|     title: 'Projects - Ryan Freeman', |     title: 'Projects', | ||||||
|     description: 'Here\'s a selection of academic and personal projects that I have worked on. Many of them are open-source, so if you see something that piques your interest, check out the code and contribute if you have ideas for how it can be improved.' |     heading: 'Things I\'ve made and projects I\'ve worked on.', | ||||||
|  |     description: 'Here\'s a selection of academic and personal projects that I have worked on. Many of them are open-source, so if you see something that piques your interest, check out the code and contribute if you have ideas for how it can be improved.', | ||||||
|  |     type: 'website' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({...meta, heading: meta.heading}) | ||||||
|  | 
 | ||||||
| export const revalidate = 0 | export const revalidate = 0 | ||||||
| 
 | 
 | ||||||
| export default async function Projects() { | export default async function Projects() { | ||||||
| @ -18,8 +23,8 @@ export default async function Projects() { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <SimpleLayout |         <SimpleLayout | ||||||
|             heading="Things I've made and projects I've worked on." |             heading={meta.heading} | ||||||
|             description={metadata.description} |             description={meta.description} | ||||||
|             gradient="bg-gradient-to-r from-sky-400 to-blue-500"> |             gradient="bg-gradient-to-r from-sky-400 to-blue-500"> | ||||||
|             <ul |             <ul | ||||||
|                 role="list" |                 role="list" | ||||||
|  | |||||||
| @ -1,12 +1,17 @@ | |||||||
| import {SimpleLayout} from '@/components/layouts/SimpleLayout' | import {SimpleLayout} from '@/components/layouts/SimpleLayout' | ||||||
| import {Card} from '@/components/ui/Card' | import {Card} from '@/components/ui/Card' | ||||||
| import React from 'react'; | import React from 'react' | ||||||
|  | import {metadata as _metadata} from '@/lib/generateMetadata' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | const meta = { | ||||||
|     title: 'Reading - Ryan Freeman', |     title: 'Reading', | ||||||
|     description: 'Take a look at my curated reading list.' |     heading: 'Books I\'m reading at the moment', | ||||||
|  |     description: 'Take a look at my curated reading list.', | ||||||
|  |     type: 'website' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({...meta, heading: meta.heading}) | ||||||
|  | 
 | ||||||
| type Book = { | type Book = { | ||||||
|     title: string |     title: string | ||||||
|     author: string |     author: string | ||||||
| @ -69,8 +74,8 @@ export default async function Reading() { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <SimpleLayout |         <SimpleLayout | ||||||
|             heading="Books I'm reading at the moment" |             heading={meta.heading} | ||||||
|             description={metadata.description} |             description={meta.description} | ||||||
|             gradient="bg-gradient-to-r from-sky-400 to-blue-500"> |             gradient="bg-gradient-to-r from-sky-400 to-blue-500"> | ||||||
|             <ul |             <ul | ||||||
|                 role="list" |                 role="list" | ||||||
|  | |||||||
| @ -9,31 +9,17 @@ import {ShieldIcon} from '@/components/icons/ShieldIcon' | |||||||
| import {EmailIcon} from '@/components/icons/EmailIcon' | import {EmailIcon} from '@/components/icons/EmailIcon' | ||||||
| import {RocketIcon} from '@/components/icons/RocketIcon' | import {RocketIcon} from '@/components/icons/RocketIcon' | ||||||
| import {ShoppingBagIcon} from '@/components/icons/ShoppingBagIcon' | import {ShoppingBagIcon} from '@/components/icons/ShoppingBagIcon' | ||||||
|  | import {metadata as _metadata} from '@/lib/generateMetadata' | ||||||
| 
 | 
 | ||||||
| const meta = { | const meta = { | ||||||
|     title: 'Services - Ryan Freeman', |     title: 'Services', | ||||||
|     heading: 'I offer a wide range of digital services to elevate and transform your business', |     heading: 'I offer a wide range of digital services to elevate and transform your business', | ||||||
|     description: 'Whether you need a WordPress website, React app, AWS support or odd coding jobs, I\'m here to help. ' + |     description: 'Whether you need a WordPress website, React app, AWS support or odd coding jobs, I\'m here to help. ' + | ||||||
|         'As an experienced software engineer, I produce high-quality software that will deliver immediate value for you and your customers.', |         'As an experienced software engineer, I produce high-quality software that will deliver immediate value for you and your customers.', | ||||||
|  |     type: 'website' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const metadata = _metadata({...meta, heading: meta.heading}) | ||||||
|     ...meta, |  | ||||||
|     openGraph: { |  | ||||||
|         title: meta.title, |  | ||||||
|         description: meta.description, |  | ||||||
|         images: [ |  | ||||||
|             { |  | ||||||
|                 url: `/api/og-image?text=${meta.heading}`, |  | ||||||
|                 width: 1200, |  | ||||||
|                 height: 600, |  | ||||||
|                 alt: meta.heading, |  | ||||||
|                 type: 'image/png' |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         type: 'website' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| type Services = { | type Services = { | ||||||
|     title: string |     title: string | ||||||
| @ -99,8 +85,8 @@ export default function Services() { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <SimpleLayout |         <SimpleLayout | ||||||
|             heading={metadata.heading} |             heading={meta.heading} | ||||||
|             description={metadata.description} |             description={meta.description} | ||||||
|             gradient="bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400"> |             gradient="bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-400"> | ||||||
|             <ul |             <ul | ||||||
|                 role="list" |                 role="list" | ||||||
|  | |||||||
| @ -2,12 +2,17 @@ import React, {ReactNode} from 'react' | |||||||
| import {SimpleLayout} from '@/components/layouts/SimpleLayout' | import {SimpleLayout} from '@/components/layouts/SimpleLayout' | ||||||
| import {Card} from '@/components/ui/Card' | import {Card} from '@/components/ui/Card' | ||||||
| import {Section} from '@/components/ui/Section' | import {Section} from '@/components/ui/Section' | ||||||
|  | import {metadata as _metadata} from '@/lib/generateMetadata' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | const meta = { | ||||||
|     title: 'Uses - Ryan Freeman', |     title: 'Uses', | ||||||
|     description: 'Software I use, equipment that makes my job easier, and other things I recommend.' |     heading: 'Software I use, equipment that makes my job easier, and other things I recommend.', | ||||||
|  |     description: 'I get asked a lot about the things I use to build software and stay productive. Here’s a big list of all of my favourite gear.', | ||||||
|  |     type: 'website' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({...meta, heading: meta.heading}) | ||||||
|  | 
 | ||||||
| function ToolsSection({children, title}: { children: ReactNode, title: string }) { | function ToolsSection({children, title}: { children: ReactNode, title: string }) { | ||||||
|     return ( |     return ( | ||||||
|         <Section title={title}> |         <Section title={title}> | ||||||
| @ -32,8 +37,8 @@ function Tool({title, href, children}: { title: string, href: string, children: | |||||||
| export default function Uses() { | export default function Uses() { | ||||||
|     return ( |     return ( | ||||||
|         <SimpleLayout |         <SimpleLayout | ||||||
|             heading="Software I use, equipment that makes my job easier, and other things I recommend." |             heading={meta.heading} | ||||||
|             description="I get asked a lot about the things I use to build software and stay productive. Here’s a big list of all of my favourite gear." |             description={meta.description} | ||||||
|             gradient="bg-gradient-to-r from-orange-400 to-rose-400"> |             gradient="bg-gradient-to-r from-orange-400 to-rose-400"> | ||||||
|             <div className="space-y-20"> |             <div className="space-y-20"> | ||||||
|                 <ToolsSection title="Workstation"> |                 <ToolsSection title="Workstation"> | ||||||
|  | |||||||
| @ -1,18 +1,21 @@ | |||||||
| import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | ||||||
| import {createSlug} from '../../../lib/createSlug' | import {createSlug} from '../../../lib/createSlug' | ||||||
|  | import {metadata as _metadata} from '../../../lib/generateMetadata' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const meta = { | ||||||
|     authors: 'Ryan Freeman', |     authors: 'Ryan Freeman', | ||||||
|     title: 'A personal journey in software engineering', |     title: 'A personal journey in software engineering', | ||||||
|     date: '2022-12-04', |     date: '2022-12-04', | ||||||
|     description: 'Hello there! If you\'re reading this, you\'ve likely stumbled upon my website — welcome! My name is Ryan Freeman, and I\'m a full-stack developer with a passion for creating intuitive and dynamic web applications.' |     description: 'Hello there! If you\'re reading this, you\'ve likely stumbled upon my website — welcome! My name is Ryan Freeman, and I\'m a full-stack developer with a passion for creating intuitive and dynamic web applications.' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({title: meta.title, heading: meta.title, description: meta.description, type: 'article'}) | ||||||
|  | 
 | ||||||
| export default (props) => <ArticleLayout | export default (props) => <ArticleLayout | ||||||
|     title={metadata.title} |     title={meta.title} | ||||||
|     date={metadata.date} |     date={meta.date} | ||||||
|     description={metadata.description} |     description={meta.description} | ||||||
|     slug={createSlug(metadata.title)} |     slug={createSlug(meta.title)} | ||||||
|     {...props} /> |     {...props} /> | ||||||
| 
 | 
 | ||||||
| Hello there! | Hello there! | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | |||||||
| import {createSlug} from '../../../lib/createSlug' | import {createSlug} from '../../../lib/createSlug' | ||||||
| import Image from 'next/image' | import Image from 'next/image' | ||||||
| import ogMetaTags from './open-graph-meta-tags.png' | import ogMetaTags from './open-graph-meta-tags.png' | ||||||
|  | import {metadata as _metadata} from '../../../lib/generateMetadata' | ||||||
| 
 | 
 | ||||||
| export const meta = { | export const meta = { | ||||||
|     authors: 'Ryan Freeman', |     authors: 'Ryan Freeman', | ||||||
| @ -10,29 +11,13 @@ export const meta = { | |||||||
|     description: 'In this post I\'ll talk about how I created dynamic, eye-catching Open Graph images with Next.js for this website.' |     description: 'In this post I\'ll talk about how I created dynamic, eye-catching Open Graph images with Next.js for this website.' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const metadata = _metadata({title: meta.title, heading: meta.title, description: meta.description, type: 'article'}) | ||||||
|     ...meta, |  | ||||||
|     openGraph: { |  | ||||||
|         title: meta.title, |  | ||||||
|         description: meta.description, |  | ||||||
|         images: [ |  | ||||||
|             { |  | ||||||
|                 url: `/api/og-image?text=${meta.title}`, |  | ||||||
|                 width: 1200, |  | ||||||
|                 height: 600, |  | ||||||
|                 alt: meta.title, |  | ||||||
|                 type: 'image/png' |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         type: 'website' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export default (props) => <ArticleLayout | export default (props) => <ArticleLayout | ||||||
|     title={metadata.title} |     title={meta.title} | ||||||
|     date={metadata.date} |     date={meta.date} | ||||||
|     description={metadata.description} |     description={meta.description} | ||||||
|     slug={createSlug(metadata.title)} |     slug={createSlug(meta.title)} | ||||||
|     {...props} /> |     {...props} /> | ||||||
| 
 | 
 | ||||||
| In this post I\'ll talk about how I created dynamic, eye-catching Open Graph images with Next.js for this website. | In this post I\'ll talk about how I created dynamic, eye-catching Open Graph images with Next.js for this website. | ||||||
| @ -138,7 +123,7 @@ By exporting the `metadata` object, I was able to specify the Open Graph meta ta | |||||||
| 
 | 
 | ||||||
| <Image src={ogMetaTags} alt=""/> | <Image src={ogMetaTags} alt=""/> | ||||||
| 
 | 
 | ||||||
| Lastly, I can verify everything is working correctly by loading the page in the browser. | Lastly, I can verify everything is working correctly by loading the Services page in the browser and examining the Open Graph meta tags. | ||||||
| 
 | 
 | ||||||
| ## Conclusion | ## Conclusion | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,21 +1,24 @@ | |||||||
| import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | ||||||
| import {createSlug} from '../../../lib/createSlug' | import {createSlug} from '../../../lib/createSlug' | ||||||
|  | import {metadata as _metadata} from '../../../lib/generateMetadata' | ||||||
| import raspberryPi from './vishnu-mohanan-rZKdS0wI8Ks-unsplash.jpg' | import raspberryPi from './vishnu-mohanan-rZKdS0wI8Ks-unsplash.jpg' | ||||||
| import casaOS from './casaos.png' | import casaOS from './casaos.png' | ||||||
| import Image from 'next/image' | import Image from 'next/image' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const meta = { | ||||||
|     authors: 'Ryan Freeman', |     authors: 'Ryan Freeman', | ||||||
|     title: 'Migrating from Vercel to Raspberry Pi 5', |     title: 'Migrating from Vercel to Raspberry Pi 5', | ||||||
|     date: '2024-08-23', |     date: '2024-08-23', | ||||||
|     description: 'Recently, I decided to migrate this website from Vercel to a self-hosted environment using a Raspberry Pi 5. This transition was driven by several motivations, such as lowering costs and having greater control over the software and hardware that I run.' |     description: 'Recently, I decided to migrate this website from Vercel to a self-hosted environment using a Raspberry Pi 5. This transition was driven by several motivations, such as lowering costs and having greater control over the software and hardware that I run.' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({title: meta.title, heading: meta.title, description: meta.description, type: 'article'}) | ||||||
|  | 
 | ||||||
| export default (props) => <ArticleLayout | export default (props) => <ArticleLayout | ||||||
|     title={metadata.title} |     title={meta.title} | ||||||
|     date={metadata.date} |     date={meta.date} | ||||||
|     description={metadata.description} |     description={meta.description} | ||||||
|     slug={createSlug(metadata.title)} |     slug={createSlug(meta.title)} | ||||||
|     {...props} /> |     {...props} /> | ||||||
| 
 | 
 | ||||||
| <Image src={raspberryPi} alt=""/> | <Image src={raspberryPi} alt=""/> | ||||||
|  | |||||||
| @ -4,31 +4,17 @@ import {Card} from '@/components/ui/Card' | |||||||
| import {Views} from '@/components/ui/Views' | import {Views} from '@/components/ui/Views' | ||||||
| import {formatDate} from '@/lib/formatDate' | import {formatDate} from '@/lib/formatDate' | ||||||
| import {getAllArticles} from '@/lib/getAllArticles' | import {getAllArticles} from '@/lib/getAllArticles' | ||||||
|  | import {metadata as _metadata} from '@/lib/generateMetadata' | ||||||
| import type {Article} from '@/types' | import type {Article} from '@/types' | ||||||
| 
 | 
 | ||||||
| const meta = { | const meta = { | ||||||
|     title: 'Writing - Ryan Freeman', |     title: 'Writing', | ||||||
|     heading: 'Writing on software engineering, and everything in between.', |     heading: 'Writing on software engineering, and everything in between.', | ||||||
|     description: 'All of my long-form thoughts on software engineering, and more, displayed in chronological order.' |     description: 'All of my long-form thoughts on software engineering, and more, displayed in chronological order.', | ||||||
|  |     type: 'website' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const metadata = _metadata({...meta, heading: meta.heading}) | ||||||
|     ...meta, |  | ||||||
|     openGraph: { |  | ||||||
|         title: meta.title, |  | ||||||
|         description: meta.description, |  | ||||||
|         images: [ |  | ||||||
|             { |  | ||||||
|                 url: `/api/og-image?text=${meta.heading}`, |  | ||||||
|                 width: 1200, |  | ||||||
|                 height: 600, |  | ||||||
|                 alt: meta.heading, |  | ||||||
|                 type: 'image/png' |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         type: 'website' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| function Article({article}: { article: Article }) { | function Article({article}: { article: Article }) { | ||||||
|     return ( |     return ( | ||||||
| @ -57,8 +43,8 @@ export default async function Writing() { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <SimpleLayout |         <SimpleLayout | ||||||
|             heading={metadata.heading} |             heading={meta.heading} | ||||||
|             description={metadata.description} |             description={meta.description} | ||||||
|             gradient="bg-gradient-to-r from-pink-500 to-violet-500" |             gradient="bg-gradient-to-r from-pink-500 to-violet-500" | ||||||
|         > |         > | ||||||
|             <div className="mx-auto grid max-w-xl grid-cols-1 gap-y-20 lg:max-w-none"> |             <div className="mx-auto grid max-w-xl grid-cols-1 gap-y-20 lg:max-w-none"> | ||||||
|  | |||||||
| @ -1,23 +1,26 @@ | |||||||
| import Image from 'next/image' | import Image from 'next/image' | ||||||
| import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | ||||||
| import {createSlug} from '../../../lib/createSlug' | import {createSlug} from '../../../lib/createSlug' | ||||||
|  | import {metadata as _metadata} from '../../../lib/generateMetadata' | ||||||
| import locks from './mariia-shalabaieva-zVC8A0CrTZ0-unsplash.jpg' | import locks from './mariia-shalabaieva-zVC8A0CrTZ0-unsplash.jpg' | ||||||
| import namecheap from './namecheap-change-nameservers.png' | import namecheap from './namecheap-change-nameservers.png' | ||||||
| import nginx from './nginx-proxy-manager.png' | import nginx from './nginx-proxy-manager.png' | ||||||
| import starshipTroopers from './starship-troopers.gif' | import starshipTroopers from './starship-troopers.gif' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const meta = { | ||||||
|     authors: 'Ryan Freeman', |     authors: 'Ryan Freeman', | ||||||
|     title: 'Secure your websites with Let\'s Encrypt, NPM and Cloudflare', |     title: 'Secure your websites with Let\'s Encrypt, NPM and Cloudflare', | ||||||
|     date: '2024-09-21', |     date: '2024-09-21', | ||||||
|     description: 'SSL/TLS is the encryption standard or protocol which encrypts the session between a website (server) and the browser (client). This protects us from potential man-in-the-middle attacks whereby an attacker could eavesdrop on the session and see all the traffic and data exchanged in the clear.' |     description: 'SSL/TLS is the encryption standard or protocol which encrypts the session between a website (server) and the browser (client). This protects us from potential man-in-the-middle attacks whereby an attacker could eavesdrop on the session and see all the traffic and data exchanged in the clear.' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({title: meta.title, heading: meta.title, description: meta.description, type: 'article'}) | ||||||
|  | 
 | ||||||
| export default (props) => <ArticleLayout | export default (props) => <ArticleLayout | ||||||
|     title={metadata.title} |     title={meta.title} | ||||||
|     date={metadata.date} |     date={meta.date} | ||||||
|     description={metadata.description} |     description={meta.description} | ||||||
|     slug={createSlug(metadata.title)} |     slug={createSlug(meta.title)} | ||||||
|     {...props} /> |     {...props} /> | ||||||
| 
 | 
 | ||||||
| <Image src={locks} alt=""/> | <Image src={locks} alt=""/> | ||||||
|  | |||||||
| @ -1,20 +1,23 @@ | |||||||
| import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | import {ArticleLayout} from '../../../components/layouts/ArticleLayout' | ||||||
| import {createSlug} from '../../../lib/createSlug' | import {createSlug} from '../../../lib/createSlug' | ||||||
|  | import {metadata as _metadata} from '../../../lib/generateMetadata' | ||||||
| import steamLibrary from './024a06f7b503c5ae8f9442ac521e06b9c9bc21e8.png' | import steamLibrary from './024a06f7b503c5ae8f9442ac521e06b9c9bc21e8.png' | ||||||
| import Image from 'next/image' | import Image from 'next/image' | ||||||
| 
 | 
 | ||||||
| export const metadata = { | export const meta = { | ||||||
|     authors: 'Ryan Freeman', |     authors: 'Ryan Freeman', | ||||||
|     title: 'Using Tailscale to bypass Steam Families restrictions', |     title: 'Using Tailscale to bypass Steam Families restrictions', | ||||||
|     date: '2024-09-14', |     date: '2024-09-14', | ||||||
|     description: 'This past week, Steam launched Steam Families, which allows Steam account owners to share their game library with up to six members of their family.' |     description: 'This past week, Steam launched Steam Families, which allows Steam account owners to share their game library with up to six members of their family.' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const metadata = _metadata({title: meta.title, heading: meta.title, description: meta.description, type: 'article'}) | ||||||
|  | 
 | ||||||
| export default (props) => <ArticleLayout | export default (props) => <ArticleLayout | ||||||
|     title={metadata.title} |     title={meta.title} | ||||||
|     date={metadata.date} |     date={meta.date} | ||||||
|     description={metadata.description} |     description={meta.description} | ||||||
|     slug={createSlug(metadata.title)} |     slug={createSlug(meta.title)} | ||||||
|     {...props} /> |     {...props} /> | ||||||
| 
 | 
 | ||||||
| <Image src={steamLibrary} alt=""/> | <Image src={steamLibrary} alt=""/> | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								lib/generateMetadata.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/generateMetadata.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | type Meta = { | ||||||
|  |     title: string | ||||||
|  |     heading: string | ||||||
|  |     description: string | ||||||
|  |     type: string | ||||||
|  |     [name: string]: string | Object | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const metadata = (meta: Meta) => { | ||||||
|  |     return { | ||||||
|  |         ...meta, | ||||||
|  |         openGraph: { | ||||||
|  |             title: meta.title, | ||||||
|  |             description: meta.description, | ||||||
|  |             images: [ | ||||||
|  |                 { | ||||||
|  |                     url: `/api/og-image?text=${meta.heading}`, | ||||||
|  |                     width: 1200, | ||||||
|  |                     height: 600, | ||||||
|  |                     alt: meta.heading, | ||||||
|  |                     type: 'image/png' | ||||||
|  |                 } | ||||||
|  |             ], | ||||||
|  |             type: meta.type | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -2,12 +2,12 @@ import glob from 'fast-glob' | |||||||
| import * as path from 'path' | import * as path from 'path' | ||||||
| 
 | 
 | ||||||
| async function importArticle(articleFilename: string) { | async function importArticle(articleFilename: string) { | ||||||
|     let {metadata, default: component} = await import( |     let {meta, default: component} = await import( | ||||||
|         `/app/writing/${articleFilename}` |         `/app/writing/${articleFilename}` | ||||||
|         ) |         ) | ||||||
|     return { |     return { | ||||||
|         slug: articleFilename.replace(/(\/page)?\.mdx$/, ''), |         slug: articleFilename.replace(/(\/page)?\.mdx$/, ''), | ||||||
|         ...metadata, |         ...meta, | ||||||
|         component, |         component, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,17 +23,3 @@ export type Repo = { | |||||||
|         color: string |         color: string | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export type Metric = { |  | ||||||
|     title: string |  | ||||||
|     value: number | string |  | ||||||
|     group: string |  | ||||||
|     href: string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export type MetricGroup = [ |  | ||||||
|     { |  | ||||||
|         groupName: string, |  | ||||||
|         groupItems: Metric[] |  | ||||||
|     } |  | ||||||
| ] |  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user