159 lines
4.4 KiB
TypeScript
159 lines
4.4 KiB
TypeScript
|
import { imageMetadata } from '@/helpers/images';
|
||
|
import { defineCollection, z } from 'astro:content';
|
||
|
|
||
|
export const defaultCover = '/images/default-cover.jpg';
|
||
|
|
||
|
// Copied and modified from https://github.com/zce/velite/blob/main/src/schemas/slug.ts
|
||
|
// The slug is internally supported by Astro with 'content' type.
|
||
|
// We add the slug here for validating the YAML configuration.
|
||
|
const slug = () =>
|
||
|
z
|
||
|
.string()
|
||
|
.min(3)
|
||
|
.max(200)
|
||
|
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/i, 'Invalid slug');
|
||
|
|
||
|
const image = (fallbackImage: string) =>
|
||
|
z
|
||
|
.string()
|
||
|
.optional()
|
||
|
.default(fallbackImage)
|
||
|
.transform(async (arg) => await imageMetadata(arg));
|
||
|
|
||
|
// Categories Collection
|
||
|
const categoriesCollection = defineCollection({
|
||
|
type: 'data',
|
||
|
schema: z.object({
|
||
|
name: z.string().max(20),
|
||
|
slug: slug(),
|
||
|
cover: image(defaultCover),
|
||
|
description: z.string().max(999).optional(),
|
||
|
}),
|
||
|
});
|
||
|
|
||
|
// Friends Collection
|
||
|
const friendsCollection = defineCollection({
|
||
|
type: 'data',
|
||
|
schema: z.array(
|
||
|
z
|
||
|
.object({
|
||
|
website: z.string().max(40),
|
||
|
description: z.string().optional(),
|
||
|
homepage: z.string().url(),
|
||
|
poster: z.string(),
|
||
|
favicon: z.string().optional(),
|
||
|
})
|
||
|
.transform((data) => {
|
||
|
if (data.favicon === undefined) {
|
||
|
data.favicon = `${data.homepage}/favicon.ico`;
|
||
|
}
|
||
|
return data;
|
||
|
}),
|
||
|
),
|
||
|
});
|
||
|
|
||
|
// Options Collection
|
||
|
const optionsCollection = defineCollection({
|
||
|
type: 'data',
|
||
|
schema: z.object({
|
||
|
title: z.string().max(40),
|
||
|
website: z.string().url(),
|
||
|
description: z.string().max(100),
|
||
|
keywords: z.array(z.string()),
|
||
|
author: z.object({ name: z.string(), email: z.string().email(), url: z.string().url() }),
|
||
|
navigation: z.array(z.object({ text: z.string(), link: z.string(), target: z.string().optional() })),
|
||
|
socials: z.array(
|
||
|
z.object({
|
||
|
name: z.string(),
|
||
|
icon: z.string(),
|
||
|
type: z.enum(['link', 'qrcode']),
|
||
|
title: z.string().optional(),
|
||
|
link: z.string().url(),
|
||
|
}),
|
||
|
),
|
||
|
settings: z.object({
|
||
|
initialYear: z.number().max(2024),
|
||
|
icpNo: z.string().optional(),
|
||
|
locale: z.string().optional().default('zh-CN'),
|
||
|
timeZone: z.string().optional().default('Asia/Shanghai'),
|
||
|
timeFormat: z.string().optional().default('yyyy-MM-dd HH:mm:ss'),
|
||
|
twitter: z.string(),
|
||
|
post: z.object({
|
||
|
sort: z.enum(['asc', 'desc']),
|
||
|
feature: z.array(z.string()).optional(),
|
||
|
category: z.array(z.string()).optional(),
|
||
|
}),
|
||
|
pagination: z.object({
|
||
|
posts: z.number().optional().default(5),
|
||
|
category: z.number().optional().default(7),
|
||
|
tags: z.number().optional().default(7),
|
||
|
search: z.number().optional().default(7),
|
||
|
}),
|
||
|
feed: z.object({
|
||
|
full: z.boolean().optional().default(true),
|
||
|
size: z.number().optional().default(20),
|
||
|
}),
|
||
|
sidebar: z.object({
|
||
|
search: z.boolean().default(false),
|
||
|
post: z.number().default(6),
|
||
|
comment: z.number().default(0),
|
||
|
tag: z.number().default(20),
|
||
|
}),
|
||
|
comments: z.object({
|
||
|
server: z.string().url().readonly(),
|
||
|
admins: z.array(z.number()),
|
||
|
}),
|
||
|
}),
|
||
|
}),
|
||
|
});
|
||
|
|
||
|
// Posts Collection
|
||
|
const postsCollection = defineCollection({
|
||
|
type: 'content',
|
||
|
schema: z.object({
|
||
|
title: z.string().max(99),
|
||
|
date: z.date(),
|
||
|
updated: z.date().optional(),
|
||
|
comments: z.boolean().optional().default(true),
|
||
|
tags: z.array(z.string()).optional().default([]),
|
||
|
category: z.string(),
|
||
|
summary: z.string().optional().default(''),
|
||
|
cover: image(defaultCover),
|
||
|
published: z.boolean().optional().default(true),
|
||
|
}),
|
||
|
});
|
||
|
|
||
|
// Pages Collection
|
||
|
const pagesCollection = defineCollection({
|
||
|
type: 'content',
|
||
|
schema: z.object({
|
||
|
title: z.string().max(99),
|
||
|
date: z.date(),
|
||
|
updated: z.date().optional(),
|
||
|
comments: z.boolean().optional().default(true),
|
||
|
cover: image(defaultCover),
|
||
|
published: z.boolean().optional().default(true),
|
||
|
friend: z.boolean().optional().default(false),
|
||
|
}),
|
||
|
});
|
||
|
|
||
|
// Tags Collection
|
||
|
const tagsCollection = defineCollection({
|
||
|
type: 'data',
|
||
|
schema: z.array(
|
||
|
z.object({
|
||
|
name: z.string().max(20),
|
||
|
slug: slug(),
|
||
|
}),
|
||
|
),
|
||
|
});
|
||
|
|
||
|
export const collections = {
|
||
|
categories: categoriesCollection,
|
||
|
friends: friendsCollection,
|
||
|
options: optionsCollection,
|
||
|
pages: pagesCollection,
|
||
|
posts: postsCollection,
|
||
|
tags: tagsCollection,
|
||
|
};
|