Start creating blog posts

This commit is contained in:
Bad Manners 2024-09-13 22:36:39 -03:00
parent 9ff1986adc
commit 4a3ee88f77
No known key found for this signature in database
GPG key ID: 8C88292CCB075609
35 changed files with 1015 additions and 157 deletions

View file

@ -148,91 +148,101 @@ const copyrightedCharacters = z
/** A record of the format `{ en: string, tok?: string, ... }`. */
const langRecord = z.object({ [DEFAULT_LANG]: z.string() }).and(z.record(lang, z.string()));
/** Common attributes for published content (stories + games). */
const publishedContent = z.object({
// Required parameters
title: z.string(),
authors: userList,
// Required parameters, but optional for drafts (isDraft === true)
pubDate: z
.date()
.transform((date: Date) => new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0))
.optional(),
description: z.string().trim(),
tags: z.array(z.string()),
// Optional parameters
isDraft: z.boolean().default(false),
isAgeRestricted: z.boolean().default(true),
relatedStories: z.array(reference("stories")).default([]),
relatedGames: z.array(reference("games")).default([]),
lang: lang,
series: reference("series").optional(),
posts: z
.object({
eka: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)(?:www\.)?aryion\.com\/g4\/view\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
furaffinity: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)(?:www\.)?furaffinity\.net\/view\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
weasyl: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)(?:www\.)?weasyl\.com\/~(?<user>[a-zA-Z][a-zA-Z0-9_-]+)\/submissions\/(?<postId>[1-9]\d*(?:\/[a-zA-Z0-9_-]+)?)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
inkbunny: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)(?:www\.)?inkbunny\.net\/s\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
sofurry: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)www\.sofurry\.com\/view\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
sofurrybeta: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)sofurrybeta\.com\/s\/(?<postId>[a-zA-Z0-9]+)\/?$/,
)(link, ctx);
return { link, postId };
}),
itch: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)(?<user>[a-z-]+)\.itch\.io\/(?<postId>[a-z0-9_-]+)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
twitter: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)(?:www\.)?(?:twitter\.com|x\.com)\/(?<user>[a-zA-Z0-9_-]+)\/status\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
bluesky: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)bsky\.app\/profile\/(?<user>(?:[a-zA-Z0-9_-]+\.)+[a-z]+)\/post\/(?<postId>[a-z0-9]+)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
mastodon: z.string().transform((link, ctx) => {
const { instance, user, postId } = parseRegex<{ instance: string; user: string; postId: string }>(
/^(?:https?:\/\/)(?<instance>(?:[a-zA-Z0-9_-]+\.)+[a-z]+)\/@(?<user>[a-zA-Z][a-zA-Z0-9_-]+)\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, instance, user, postId };
}),
})
.partial()
.default({}),
blacklistedMastodonComments: z.array(z.string()).default([]),
});
const publishedContent = z
.object({
// Required parameters
title: z.string(),
authors: userList,
// Required parameters, but optional for drafts (isDraft === true)
pubDate: z
.date()
.transform((date: Date) => new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0))
.optional(),
description: z.string().trim(),
tags: z.array(z.string()),
// Optional parameters
isDraft: z.boolean().default(false),
isAgeRestricted: z.boolean().default(true),
relatedStories: z.array(reference("stories")).default([]),
relatedGames: z.array(reference("games")).default([]),
relatedBlogPosts: z.array(reference("blog")).default([]),
lang: lang,
series: reference("series").optional(),
posts: z
.object({
eka: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)(?:www\.)?aryion\.com\/g4\/view\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
furaffinity: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)(?:www\.)?furaffinity\.net\/view\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
weasyl: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)(?:www\.)?weasyl\.com\/~(?<user>[a-zA-Z][a-zA-Z0-9_-]+)\/submissions\/(?<postId>[1-9]\d*(?:\/[a-zA-Z0-9_-]+)?)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
inkbunny: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)(?:www\.)?inkbunny\.net\/s\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
sofurry: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)www\.sofurry\.com\/view\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, postId };
}),
sofurrybeta: z.string().transform((link, ctx) => {
const { postId } = parseRegex<{ postId: string }>(
/^(?:https?:\/\/)sofurrybeta\.com\/s\/(?<postId>[a-zA-Z0-9]+)\/?$/,
)(link, ctx);
return { link, postId };
}),
itch: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)(?<user>[a-z-]+)\.itch\.io\/(?<postId>[a-z0-9_-]+)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
twitter: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)(?:www\.)?(?:twitter\.com|x\.com)\/(?<user>[a-zA-Z0-9_-]+)\/status\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
bluesky: z.string().transform((link, ctx) => {
const { user, postId } = parseRegex<{ user: string; postId: string }>(
/^(?:https?:\/\/)bsky\.app\/profile\/(?<user>(?:[a-zA-Z0-9_-]+\.)+[a-z]+)\/post\/(?<postId>[a-z0-9]+)\/?$/,
)(link, ctx);
return { link, user, postId };
}),
mastodon: z.string().transform((link, ctx) => {
const { instance, user, postId } = parseRegex<{ instance: string; user: string; postId: string }>(
/^(?:https?:\/\/)(?<instance>(?:[a-zA-Z0-9_-]+\.)+[a-z]+)\/@(?<user>[a-zA-Z][a-zA-Z0-9_-]+)\/(?<postId>[1-9]\d*)\/?$/,
)(link, ctx);
return { link, instance, user, postId };
}),
})
.partial()
.default({}),
blacklistedMastodonComments: z.array(z.string()).default([]),
})
.refine(
({ posts, blacklistedMastodonComments }) => !blacklistedMastodonComments.length || posts.mastodon,
`Cannot include "blacklistedMastodonComments" without a Mastodon post`,
)
.refine(({ isDraft, pubDate }) => isDraft || pubDate, `Missing "pubDate" for published content`)
.refine(({ isDraft, description }) => isDraft || description, `Missing "description" for published content`)
.refine(({ isDraft, pubDate }) => isDraft || pubDate, `Missing "pubDate" for published content`)
.refine(({ isDraft, tags }) => isDraft || tags.length, `Missing "tags" for published content`);
// Types
@ -267,19 +277,12 @@ const storiesCollection = defineCollection({
summary: z.string().trim().optional(),
})
.and(publishedContent)
.refine(({ isDraft, description }) => isDraft || description, `Missing "description" for published story`)
.refine(({ isDraft, thumbnail }) => isDraft || thumbnail, `Missing "thumbnail" for published story`)
.refine(
({ isDraft, contentWarning }) => isDraft || contentWarning,
`Missing "contentWarning" for published story`,
)
.refine(({ isDraft, wordCount }) => isDraft || wordCount, `Missing "wordCount" for published story`)
.refine(({ isDraft, pubDate }) => isDraft || pubDate, `Missing "pubDate" for published story`)
.refine(({ isDraft, thumbnail }) => isDraft || thumbnail, `Missing "thumbnail" for published story`)
.refine(({ isDraft, tags }) => isDraft || tags.length, `Missing "tags" for published story`)
.refine(
({ posts, blacklistedMastodonComments }) => !blacklistedMastodonComments.length || posts.mastodon,
`Cannot include "blacklistedMastodonComments" without a Mastodon post`,
),
.refine(({ isDraft, wordCount }) => isDraft || wordCount, `Missing "wordCount" for published story`),
});
const gamesCollection = defineCollection({
@ -299,16 +302,9 @@ const gamesCollection = defineCollection({
copyrightedCharacters: copyrightedCharacters,
})
.and(publishedContent)
.refine(({ isDraft, description }) => isDraft || description, `Missing "description" for published game`)
.refine(({ isDraft, contentWarning }) => isDraft || contentWarning, `Missing "contentWarning" for published game`)
.refine(({ isDraft, platforms }) => isDraft || platforms.length, `Missing "platforms" for published game`)
.refine(({ isDraft, pubDate }) => isDraft || pubDate, `Missing "pubDate" for published game`)
.refine(({ isDraft, thumbnail }) => isDraft || thumbnail, `Missing "thumbnail" for published game`)
.refine(({ isDraft, tags }) => isDraft || tags.length, `Missing "tags" for published game`)
.refine(
({ posts, blacklistedMastodonComments }) => !blacklistedMastodonComments.length || posts.mastodon,
`Cannot include "blacklistedMastodonComments" without a Mastodon post`,
),
.refine(({ isDraft, contentWarning }) => isDraft || contentWarning, `Missing "contentWarning" for published game`)
.refine(({ isDraft, platforms }) => isDraft || platforms.length, `Missing "platforms" for published game`),
});
const blogCollection = defineCollection({
@ -316,24 +312,14 @@ const blogCollection = defineCollection({
schema: ({ image }) =>
z
.object({
// Required parameters, but optional for drafts (isDraft === true)
thumbnail: image().optional(),
// Optional parameters
summary: z.string().trim().optional(),
thumbnail: image().optional(),
thumbnailWidth: z.number().int().optional(),
thumbnailHeight: z.number().int().optional(),
prev: reference("blog").nullish(),
next: reference("blog").nullish(),
})
.and(publishedContent)
.refine(({ isDraft, description }) => isDraft || description, `Missing "description" for published story`)
.refine(({ isDraft, pubDate }) => isDraft || pubDate, `Missing "pubDate" for published story`)
.refine(({ isDraft, thumbnail }) => isDraft || thumbnail, `Missing "thumbnail" for published story`)
.refine(({ isDraft, tags }) => isDraft || tags.length, `Missing "tags" for published story`)
.refine(
({ posts, blacklistedMastodonComments }) => !blacklistedMastodonComments.length || posts.mastodon,
`Cannot include "blacklistedMastodonComments" without a Mastodon post`,
),
.and(publishedContent),
});
// Data collections