feat: multi-authors support

This commit is contained in:
grassblock 2025-05-23 22:03:54 +08:00
parent af55568839
commit 458dc2dafe
9 changed files with 54 additions and 8 deletions

View file

@ -1,4 +1,5 @@
---
const { title } = Astro.props;
import {siteConfig} from "../config";
const { title, email = siteConfig.defaultAuthor.email } = Astro.props;
---
<a href={`mailto:test@email.com?subject=RE:${title}&body=Hi,\n\nI would like to reply to your post "${title}".`}>&#x21A9; Reply via Email</a>
<a href={`mailto:${email}?subject=RE:${title}&body=Hi,\n\nI would like to reply to your post "${title}".`}>&#x21A9; Reply via Email</a>

View file

@ -1,9 +1,17 @@
---
const { title, description, ogImage = "" } = Astro.props
interface Props {
title: string;
description: string;
author?: string;
ogImage?: string;
}
const { title, author,description, ogImage = "" } = Astro.props
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
---
<meta name="title" content={title} />
{author && <meta name="author" content={author} />}
<meta name="description" content={description} />
{/* Open Graph / Facebook */}
<meta name="og:title" content={title} />

View file

@ -3,6 +3,11 @@ export const siteConfig = {
title: '/var/log/mercury',
description: 'A blog about software development, technology, and life.',
homepageOgImage: '',
defaultAuthor: {
id: 'd6e4661d', // (optional) an id in the authors.yaml, will override the setting below (if id exists)
name: 'GrassBlock1',
email: 'hi@mercury.info',
},
// features
noClientJavaScript: false, // disable client-side javascript, this will:
// 1. disable all built-in client-side javascript from rendering
@ -10,7 +15,6 @@ export const siteConfig = {
// 3. the comments will be globally disabled
// 4. the night mode & back to top will not use Javascript to function
// 5. the neko will be force-disabled
authorDefaultEmail: '',
// site components
navBarItems: [
// additional items in the navbar

View file

@ -3,6 +3,7 @@ import {posts} from './posts/_schemas';
import {pages} from "./pages/_schemas";
import {file} from 'astro/loaders';
import { z } from 'astro:content';
import {siteConfig} from "../config.ts";
const blogCollection = defineCollection({
type: 'content',
@ -21,8 +22,21 @@ const blogRollData = defineCollection({
})
});
const authorsData = defineCollection({
loader: file("src/data/authors.yaml"),
schema: z.object({
name: z.string().default(siteConfig.defaultAuthor.name),
email: z.string().email().default(siteConfig.defaultAuthor.email),
social: z.object({
twitter: z.string().optional(),
fediverse: z.string().optional(),
}).optional(),
})
});
export const collections = {
'posts': blogCollection,
'pages': pageCollection,
'links': blogRollData,
'authors': authorsData,
};

View file

@ -6,4 +6,5 @@ export const posts = ({ image }) => z.object({
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
cover: image().optional(),
author: z.string().optional(),
});

View file

@ -2,6 +2,7 @@
title: 'The Art of Minimalism'
description: 'Thoughts on minimalism in design and code'
pubDate: '2025-06-05'
author: 'Wheatley'
---
Minimalism isn't just about having less, it's about making room for what matters.

10
src/data/authors.yaml Normal file
View file

@ -0,0 +1,10 @@
Wheatley: # the key name (id) of the author, which is used in the front matter
name: "Wheatley" # the display name of the author
email: "hello@example.org" # the email address of the author
social: # the social media accounts of the author, if any (there is no reference for this yet except for the twitter handle)
twitter: "@wheatley"
fediverse: "@"
Glados: # you can have more if you like
name: "Glados"
email: "contact@example.org"

View file

@ -12,6 +12,7 @@ import Logo from '../assets/mercury.svg'
interface Props {
title: string;
description: string;
author?: string;
path?: string;
ogImage?: string;
}
@ -29,7 +30,7 @@ const navBarItems = siteConfig.navBarItems
const customFooter = siteConfig.customFooter
const nekoType = siteConfig.neko?.type
const { title = pageTitle, description = siteConfig.description, ogImage = "" } = Astro.props;
const { title = pageTitle, author = siteConfig.defaultAuthor.name,description = siteConfig.description, ogImage = "" } = Astro.props;
---
<!doctype html>
@ -39,7 +40,7 @@ const { title = pageTitle, description = siteConfig.description, ogImage = "" }
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<Meta title={pageTitle} description={description} ogImage={ogImage} />
<Meta title={pageTitle} author={author} description={description} ogImage={ogImage} />
<title>{pageTitle}</title>
</head>
<body>

View file

@ -1,6 +1,6 @@
---
import Layout from '../../layouts/Layout.astro';
import { getCollection } from 'astro:content';
import { getCollection, getEntry } from 'astro:content';
import Comments from "../../components/Comments.astro";
import {getImage} from "astro:assets";
import { unified } from "unified";
@ -21,6 +21,11 @@ const { entry } = Astro.props;
const { Content } = await entry.render();
const noscript = siteConfig.noClientJavaScript
const slug = Astro.params.slug;
const authorId = entry.data.author || siteConfig.defaultAuthor.id;
// Get author data
const authorData = await getEntry('authors', authorId);
const authorInfo = authorData ? authorData.data : siteConfig.defaultAuthor;
// get featured image and use it as og:image
// use the custom cover image if it exists, otherwise use the featured image file in the same directory
@ -39,6 +44,7 @@ const cover = customFeaturedImage || matchedImage_src?.src || `/post/${slug}/fea
title={entry.data.title}
description={entry.data.description}
ogImage={cover}
author={authorInfo.name}
>
<h1 class="title">{entry.data.title}</h1>
<span class="date">{new Date(entry.data.pubDate).toISOString().split('T')[0]}</span>
@ -47,7 +53,7 @@ const cover = customFeaturedImage || matchedImage_src?.src || `/post/${slug}/fea
</div>
<div style="margin-top: 2rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
<ReplyViaEmail title={entry.data.title} />
<ReplyViaEmail title={entry.data.title} email={authorInfo.email} />
<br>
<a href="/blog">&larr; Back to posts</a>
{noscript && <h2>Comments</h2> <Comments path={`post/${slug}`} />}