initial commit
This commit is contained in:
commit
c70fc72952
143 changed files with 13594 additions and 0 deletions
33
src/pages/[permalink].astro
Normal file
33
src/pages/[permalink].astro
Normal file
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
import Layout from '@layouts/Layout.astro';
|
||||
import BlockRenderer from '@components/BlockRenderer.astro';
|
||||
import { fetchGlobals } from '@utils/data/globals.js';
|
||||
import { fetchPages } from '@utils/data/pages';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pages = await fetchPages();
|
||||
return pages.map((page) => ({
|
||||
params: { permalink: page.permalink },
|
||||
props: { page },
|
||||
}));
|
||||
}
|
||||
|
||||
const globals = await fetchGlobals();
|
||||
const { page } = Astro.props;
|
||||
const currentUrl = Astro.url.pathname;
|
||||
---
|
||||
|
||||
<Layout
|
||||
globals={globals}
|
||||
pageTitle={page.title}
|
||||
description={page.description}
|
||||
ogImage={page.open_graph_image}
|
||||
updated={page.updated}
|
||||
currentUrl={currentUrl}
|
||||
>
|
||||
{page.blocks.map((block) => (
|
||||
<BlockRenderer block={block} />
|
||||
))}
|
||||
</Layout>
|
64
src/pages/feeds/syndication.xml.js
Normal file
64
src/pages/feeds/syndication.xml.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
import fetchSyndication from '../../utils/data/syndication.js';
|
||||
import { fetchGlobals } from '../../utils/data/globals.js';
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const entries = await fetchSyndication();
|
||||
|
||||
if (!entries.length) return new Response('No feed entries found.', { status: 404 });
|
||||
|
||||
const title = globals.site_name || 'Syndicated content / Cory Dransfeldt';
|
||||
const permalink = '/feeds/syndication.xml';
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<atom:link href="${globals.url}${permalink}" rel="self" type="application/rss+xml" />
|
||||
<title><![CDATA[${title}]]></title>
|
||||
<description><![CDATA[${globals.site_description || ''}]]></description>
|
||||
<link>${globals.url}${permalink}</link>
|
||||
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
|
||||
<image>
|
||||
<title><![CDATA[${title}]]></title>
|
||||
<link>${globals.url}${permalink}</link>
|
||||
<url>${globals.cdn_url}${globals.avatar}?class=w200</url>
|
||||
<width>144</width>
|
||||
<height>144</height>
|
||||
</image>
|
||||
${entries
|
||||
.slice(0, 20)
|
||||
.map(
|
||||
(entry) => `
|
||||
<item>
|
||||
<title><![CDATA[${entry.syndication.title}]]></title>
|
||||
<link>${encodeAmp(entry.syndication.url)}</link>
|
||||
<pubDate>${new Date(entry.syndication.date).toUTCString()}</pubDate>
|
||||
<guid isPermaLink="false">${encodeAmp(entry.syndication.url)}</guid>
|
||||
<description><![CDATA[${escapeHTML(entry.syndication.description)}]]></description>
|
||||
</item>`
|
||||
)
|
||||
.join('')}
|
||||
</channel>
|
||||
</rss>`;
|
||||
|
||||
return new Response(xml, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/rss+xml',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function encodeAmp(url) {
|
||||
return url.replace(/&/g, '&');
|
||||
}
|
||||
|
||||
function escapeHTML(str) {
|
||||
if (!str) return '';
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
29
src/pages/index.astro
Normal file
29
src/pages/index.astro
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Intro from '../components/Intro.astro';
|
||||
import { fetchGlobals } from '../utils/data/globals';
|
||||
import RecentActivity from '../components/RecentActivity.astro';
|
||||
import RecentPosts from '../components/RecentPosts.astro';
|
||||
|
||||
const globals = await fetchGlobals();
|
||||
const schema = 'blog';
|
||||
const pageTitle = globals.site_name;
|
||||
const description = 'This is a blog post description';
|
||||
const ogImage = globals.cdn_url + globals.avatar;
|
||||
const fullUrl = globals.url + '/blog/my-post';
|
||||
const themeColor = globals.theme_color;
|
||||
---
|
||||
<Layout
|
||||
globals={globals}
|
||||
pageTitle={pageTitle}
|
||||
description={description}
|
||||
ogImage={ogImage}
|
||||
fullUrl={fullUrl}
|
||||
themeColor={themeColor}
|
||||
schema={schema}
|
||||
globals={globals}
|
||||
>
|
||||
<Intro intro={globals.intro} />
|
||||
<RecentActivity />
|
||||
<RecentPosts />
|
||||
</Layout>
|
59
src/pages/posts/[...page].astro
Normal file
59
src/pages/posts/[...page].astro
Normal file
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import { IconStar } from '@tabler/icons-react';
|
||||
import { fetchGlobals } from "@data/globals.js";
|
||||
import { fetchAllPosts } from "@data/posts.js";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Paginator from '@components/nav/Paginator.astro';
|
||||
import { md } from '@utils/helpers.js';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
const globals = await fetchGlobals();
|
||||
const posts = await fetchAllPosts();
|
||||
const { page } = Astro.props;
|
||||
const currentUrl = Astro.url.pathname;
|
||||
|
||||
const currentPage = Astro.params.page ? parseInt(Astro.params.page, 10) : 1;
|
||||
const pageSize = 15;
|
||||
const totalPosts = posts.length;
|
||||
const totalPages = Math.ceil(totalPosts / pageSize);
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
const paginatedPosts = posts.slice(start, end);
|
||||
|
||||
const pagination = {
|
||||
currentPage,
|
||||
totalPages,
|
||||
hasPrevious: currentPage > 1,
|
||||
hasNext: currentPage < totalPages,
|
||||
previousPage: currentPage > 1 ? `/posts/${currentPage - 1}` : null,
|
||||
nextPage: currentPage < totalPages ? `/posts/${currentPage + 1}` : null,
|
||||
pages: Array.from({ length: totalPages }, (_, i) => ({
|
||||
number: i + 1,
|
||||
href: `/posts/${i + 1}`,
|
||||
})),
|
||||
};
|
||||
---
|
||||
|
||||
<Layout
|
||||
globals={globals}
|
||||
pageTitle="All posts"
|
||||
currentUrl={currentUrl}
|
||||
>
|
||||
{paginatedPosts.map((post) => (
|
||||
<article>
|
||||
<div class="post-meta">
|
||||
{post.featured && <IconStar size={16} />}
|
||||
<time datetime={post.date}>
|
||||
{DateTime.fromISO(post.date).toLocaleString(DateTime.DATE_FULL)}
|
||||
</time>
|
||||
</div>
|
||||
<h3>
|
||||
<a href={post.slug}>{post.title}</a>
|
||||
</h3>
|
||||
<p set:html={md(post.description)}></p>
|
||||
</article>
|
||||
))}
|
||||
|
||||
<Paginator pagination={pagination} appVersion="1.0.0" />
|
||||
</Layout>
|
102
src/pages/posts/[year]/[title].astro
Normal file
102
src/pages/posts/[year]/[title].astro
Normal file
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
import { IconStar } from "@tabler/icons-react";
|
||||
import { fetchAllPosts } from "@data/posts.js";
|
||||
import { fetchGlobals } from "@data/globals.js";
|
||||
import { md } from '@utils/helpers.js';
|
||||
import OldPost from "@components/blocks/banners/OldPost.astro";
|
||||
import BlockRenderer from "@components/BlockRenderer.astro";
|
||||
import AssociatedMedia from "@components/blocks/AssociatedMedia.astro";
|
||||
import MastodonPost from "@components/blocks/MastodonPost.astro";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Coffee from "@components/blocks/banners/Coffee.astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await fetchAllPosts();
|
||||
|
||||
return posts.map((post) => {
|
||||
const match = post.url.match(/^\/posts\/(\d{4})\/(.+)$/);
|
||||
if (!match) throw new Error(`Invalid post URL: ${post.url}`);
|
||||
|
||||
const [, year, title] = match;
|
||||
|
||||
return {
|
||||
params: { year, title },
|
||||
props: { post },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
const { year, title } = Astro.params;
|
||||
const globals = await fetchGlobals();
|
||||
const currentUrl = Astro.url.pathname;
|
||||
const htmlContent = md(post.content);
|
||||
---
|
||||
|
||||
<Layout
|
||||
globals={globals}
|
||||
pageTitle={post.title}
|
||||
description={post.description}
|
||||
ogImage={post.open_graph_image}
|
||||
updated={post.updated}
|
||||
currentUrl={currentUrl}
|
||||
>
|
||||
<article class="standalone">
|
||||
<div class="post-meta">
|
||||
{post.featured && <IconStar size={16} />}
|
||||
<time datetime={post.date}>
|
||||
{
|
||||
new Date(post.date).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
</time>
|
||||
</div>
|
||||
<h3>{post.title}</h3>
|
||||
<div>
|
||||
{post.old_post && <OldPost />}
|
||||
{
|
||||
post.image && (
|
||||
<img
|
||||
srcset={`
|
||||
${globals.cdn_url}${post.image}?class=w200&type=webp 200w,
|
||||
${globals.cdn_url}${post.image}?class=w400&type=webp 400w,
|
||||
${globals.cdn_url}${post.image}?class=w800&type=webp 800w,
|
||||
${globals.cdn_url}${post.image}?class=w1600&type=webp 1600w
|
||||
`}
|
||||
sizes="(max-width: 450px) 200px,
|
||||
(max-width: 850px) 400px,
|
||||
(max-width: 1000px) 800px,
|
||||
1200px"
|
||||
src={`${globals.cdn_url}${post.image}?class=w200`}
|
||||
alt={post.image_alt?.replace(/['"]/g, "")}
|
||||
class="image-banner"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
width="200"
|
||||
height="auto"
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div set:html={htmlContent} />
|
||||
{
|
||||
post.blocks &&
|
||||
post.blocks.map((block) => <BlockRenderer block={block} />)
|
||||
}
|
||||
<!-- {post.mastodon_url && <MastodonPost url={post.mastodon_url} />} -->
|
||||
<AssociatedMedia
|
||||
artists={post.artists}
|
||||
books={post.books}
|
||||
genres={post.genres}
|
||||
movies={post.movies}
|
||||
posts={post.posts}
|
||||
shows={post.shows}
|
||||
/>
|
||||
<Coffee />
|
||||
</div>
|
||||
</article>
|
||||
</Layout>
|
26
src/pages/robots.txt.js
Normal file
26
src/pages/robots.txt.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { fetchAllRobots } from '../utils//data/robots.js';
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const robots = await fetchAllRobots();
|
||||
|
||||
const sitemap = `Sitemap: https://coryd.dev/sitemap.xml\n\n`;
|
||||
const allowAll = `User-agent: *\nDisallow:\n\n`;
|
||||
const disallowedBots = robots
|
||||
.map((userAgent) => `User-agent: ${userAgent}`)
|
||||
.join('\n');
|
||||
const disallowAll = `\nDisallow: /`;
|
||||
|
||||
const robotsTxt = `${sitemap}${allowAll}${disallowedBots}${disallowAll}`;
|
||||
|
||||
return new Response(robotsTxt, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error generating robots.txt:', error);
|
||||
return new Response('Error generating robots.txt', { status: 500 });
|
||||
}
|
||||
}
|
Reference in a new issue