use Next.js

This commit is contained in:
polv committed 2020-12-17 23:15:28 +07:00
1 parent cf94c66807
commit 20a00d5ab3
34 files changed
+7117 -1

No files matched your search

+110
View File
@@ -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()
}
})
})
}
+7
View File
@@ -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)
+42
View File
@@ -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
}
}
+64
View File
@@ -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'))
)
}