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, };