use Next.js
This commit is contained in:
1 parent
cf94c66807
commit
20a00d5ab3
34 files changed
+7117
-1
No files matched your search
@@ -0,0 +1,110 @@
|
||||
/* eslint-disable import/no-mutable-exports */
|
||||
|
||||
import idxJson from '~/build/idx.json'
|
||||
import Loki, { Collection } from 'lokijs'
|
||||
import lunr from 'lunr'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { buildPath } from './dir'
|
||||
|
||||
const _zMatter = {
|
||||
priority: z.number().optional(),
|
||||
title: z.string(),
|
||||
image: z.string().optional(),
|
||||
tag: z.array(z.string()).optional(),
|
||||
category: z.array(z.string()).optional()
|
||||
}
|
||||
|
||||
export const zMatter = z.object({
|
||||
..._zMatter,
|
||||
date: z.date().optional(),
|
||||
createdAt: z.date().optional(),
|
||||
updatedAt: z.date().optional()
|
||||
})
|
||||
|
||||
export type IMatter = z.infer<typeof zMatter>
|
||||
|
||||
const zMatterSerialized = z.object({
|
||||
..._zMatter,
|
||||
date: z.number().optional(),
|
||||
createdAt: z.number().optional(),
|
||||
updatedAt: z.number().optional()
|
||||
})
|
||||
|
||||
export type IMatterSerialized = z.infer<typeof zMatterSerialized>
|
||||
|
||||
export const zPost = z
|
||||
.object({
|
||||
path: z.string(),
|
||||
content: z.string(),
|
||||
excerptHtml: z.string(),
|
||||
contentHtml: z.string()
|
||||
})
|
||||
.merge(zMatterSerialized)
|
||||
|
||||
export type IPost = z.infer<typeof zPost> & {
|
||||
path: string
|
||||
title: string
|
||||
content: string
|
||||
excerptHtml: string
|
||||
contentHtml: string
|
||||
}
|
||||
|
||||
export type IPostTeaser = {
|
||||
path: string
|
||||
date?: number
|
||||
title: string
|
||||
image?: string
|
||||
content: string
|
||||
excerptHtml: string
|
||||
}
|
||||
|
||||
export interface IPostSearch {
|
||||
path: string
|
||||
title: string
|
||||
tag?: string[]
|
||||
category?: string[]
|
||||
/**
|
||||
* Cleaned and jieba-separated markdown
|
||||
*/
|
||||
content: string
|
||||
}
|
||||
|
||||
export let db: Loki
|
||||
|
||||
export let dbPost: Collection<IPost>
|
||||
|
||||
export let idx: lunr.Index
|
||||
|
||||
export async function initDatabase () {
|
||||
if (db) {
|
||||
return
|
||||
}
|
||||
|
||||
idx = lunr.Index.load(idxJson)
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
db = new Loki(buildPath('data.loki'), {
|
||||
autoload: true,
|
||||
autoloadCallback: () => {
|
||||
dbPost = db.getCollection('post')
|
||||
|
||||
if (!dbPost) {
|
||||
dbPost = db.addCollection('post', {
|
||||
unique: ['path'],
|
||||
indices: [
|
||||
'date',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'priority',
|
||||
'tag',
|
||||
'category'
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import path from 'path'
|
||||
|
||||
export const buildPath = (...ps: string[]): string =>
|
||||
path.resolve(process.cwd(), 'build', ...ps)
|
||||
|
||||
export const dstMediaPath = (...ps: string[]): string =>
|
||||
path.resolve(process.cwd(), 'public/media', ...ps)
|
||||
@@ -0,0 +1,42 @@
|
||||
import { dbPost, idx, initDatabase } from './db'
|
||||
|
||||
export async function doSearch ({
|
||||
q,
|
||||
tag,
|
||||
page = 1
|
||||
}: {
|
||||
q?: string
|
||||
tag?: string
|
||||
page?: number
|
||||
}) {
|
||||
await initDatabase()
|
||||
|
||||
const perPage = 5
|
||||
const offset = (page - 1) * perPage
|
||||
|
||||
let cond = dbPost.chain().find({
|
||||
date: { $exists: true } as any
|
||||
})
|
||||
|
||||
if (q) {
|
||||
cond = cond.find({
|
||||
path: { $in: idx.search(q).map(({ ref }) => ref) }
|
||||
})
|
||||
}
|
||||
|
||||
if (tag) {
|
||||
cond = cond.find({
|
||||
tag: { $contains: tag }
|
||||
})
|
||||
}
|
||||
|
||||
cond = cond.simplesort('date', { desc: true })
|
||||
|
||||
const count = cond.branch().count()
|
||||
const result = cond.offset(offset).limit(perPage).data()
|
||||
|
||||
return {
|
||||
count,
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import yaml from 'js-yaml'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { buildPath } from './dir'
|
||||
|
||||
export const THEME_FILENAME = 'theme.yml'
|
||||
|
||||
export const zTheme = z
|
||||
.object({
|
||||
title: z.string(),
|
||||
awsm: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
keywords: z.array(z.string()).optional(),
|
||||
nav: z
|
||||
.array(
|
||||
z
|
||||
.object({
|
||||
name: z.string(),
|
||||
to: z.string(),
|
||||
href: z.undefined()
|
||||
})
|
||||
.or(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
href: z.string(),
|
||||
to: z.undefined()
|
||||
})
|
||||
)
|
||||
)
|
||||
.optional(),
|
||||
author: z
|
||||
.object({
|
||||
url: z.string().optional(),
|
||||
email: z.string().optional(),
|
||||
name: z.string()
|
||||
})
|
||||
.nonstrict(),
|
||||
analytics: z
|
||||
.object({
|
||||
plausible: z.string().optional()
|
||||
})
|
||||
.optional(),
|
||||
comment: z
|
||||
.object({
|
||||
remark42: z
|
||||
.object({
|
||||
host: z.string(),
|
||||
siteId: z.string()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
.nonstrict()
|
||||
|
||||
export type ITheme = z.infer<typeof zTheme>
|
||||
|
||||
export function getTheme () {
|
||||
return zTheme.parse(
|
||||
yaml.safeLoad(fs.readFileSync(buildPath(THEME_FILENAME), 'utf-8'))
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user