remove pug
This commit is contained in:
1 parent
e397c9ae1e
commit
68840c9c60
49 files changed
+1421
-806
No files matched your search
+151
-11
@@ -1,6 +1,8 @@
|
||||
import sqlite3 from 'better-sqlite3'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { builtPath } from './dir'
|
||||
|
||||
const zMatter = z.object({
|
||||
date: z.date().optional(),
|
||||
createdAt: z.date().optional(),
|
||||
@@ -31,8 +33,8 @@ export type IPost = z.infer<typeof zPost>
|
||||
export class Database {
|
||||
db: sqlite3.Database
|
||||
|
||||
constructor(public filepath: string, opts?: sqlite3.Options) {
|
||||
this.db = sqlite3(filepath, opts)
|
||||
constructor(opts?: sqlite3.Options) {
|
||||
this.db = sqlite3(builtPath('data.db'), opts)
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
@@ -40,8 +42,8 @@ export class Database {
|
||||
CREATE TABLE IF NOT EXISTS files (
|
||||
[path] TEXT PRIMARY KEY,
|
||||
[date] INT, -- epoch milli
|
||||
[createdAt] INT, -- epoch milli
|
||||
[updatedAt] INT, -- epoch milli
|
||||
createdAt INT, -- epoch milli
|
||||
updatedAt INT, -- epoch milli
|
||||
priority INT,
|
||||
-- q.title
|
||||
[image] TEXT,
|
||||
@@ -83,20 +85,158 @@ export class Database {
|
||||
this.db.transaction(() => {
|
||||
// eslint-disable-next-line array-callback-return
|
||||
ps.map((p) => {
|
||||
p = zPost.parse(p)
|
||||
const {
|
||||
path,
|
||||
date,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
priority,
|
||||
image,
|
||||
excerptHtml,
|
||||
contentHtml,
|
||||
title,
|
||||
tag,
|
||||
category,
|
||||
content,
|
||||
} = zPost.parse(p)
|
||||
|
||||
fStmt.run({
|
||||
...p,
|
||||
date: p.date ? p.date.getTime() : undefined,
|
||||
createdAt: p.createdAt ? p.createdAt.getTime() : undefined,
|
||||
updatedAt: p.updatedAt ? p.updatedAt.getTime() : undefined,
|
||||
path,
|
||||
priority,
|
||||
image,
|
||||
excerptHtml,
|
||||
contentHtml,
|
||||
date: date ? date.getTime() : null,
|
||||
createdAt: createdAt ? createdAt.getTime() : null,
|
||||
updatedAt: updatedAt ? updatedAt.getTime() : null,
|
||||
})
|
||||
|
||||
qStmt.run({
|
||||
...p,
|
||||
tag: p.tag ? p.tag.join(' ') : undefined,
|
||||
path,
|
||||
title,
|
||||
category,
|
||||
content,
|
||||
tag: tag ? tag.join(' ') : undefined,
|
||||
})
|
||||
})
|
||||
})()
|
||||
}
|
||||
|
||||
public get(id: string) {
|
||||
const r = this.db
|
||||
.prepare(
|
||||
/* sql */ `
|
||||
SELECT
|
||||
[date],
|
||||
title,
|
||||
[image],
|
||||
tag,
|
||||
category,
|
||||
excerptHtml excerpt,
|
||||
contentHtml content
|
||||
FROM files f
|
||||
JOIN q ON q.path = f.path
|
||||
WHERE f.path = @id
|
||||
`
|
||||
)
|
||||
.get({ id })
|
||||
|
||||
if (!r) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
...r,
|
||||
date: r.date ? new Date(r.date) : undefined,
|
||||
tag: r.tag ? r.tag.split(/ /g) : undefined,
|
||||
} as {
|
||||
date?: Date
|
||||
title: string
|
||||
image?: string
|
||||
tag?: string[]
|
||||
category?: string
|
||||
excerpt: string
|
||||
content: string
|
||||
}
|
||||
}
|
||||
|
||||
public q(
|
||||
q: string,
|
||||
opts: {
|
||||
offset: number
|
||||
limit: number
|
||||
}
|
||||
) {
|
||||
return this._search(q, opts, 'RANK')
|
||||
}
|
||||
|
||||
public by(
|
||||
q: string,
|
||||
opts: {
|
||||
offset: number
|
||||
limit: number
|
||||
}
|
||||
) {
|
||||
return this._search(
|
||||
q,
|
||||
opts,
|
||||
'priority DESC, [date] DESC, createdAt DESC, updatedAt DESC'
|
||||
)
|
||||
}
|
||||
|
||||
private _search(
|
||||
q: string,
|
||||
opts: {
|
||||
offset: number
|
||||
limit: number
|
||||
},
|
||||
orderedBy: string
|
||||
) {
|
||||
const fromSegment = /* sql */ `
|
||||
FROM files f
|
||||
JOIN q ON q.path = f.path
|
||||
${q ? `WHERE q = @q` : ''}
|
||||
`
|
||||
|
||||
const count =
|
||||
this.db
|
||||
.prepare(
|
||||
/* sql */ `
|
||||
SELECT COUNT(f.path) [count]
|
||||
${fromSegment}
|
||||
`
|
||||
)
|
||||
.get({ q })?.count || 0
|
||||
|
||||
const result = this.db
|
||||
.prepare(
|
||||
/* sql */ `
|
||||
SELECT
|
||||
f.path [path],
|
||||
[date],
|
||||
title,
|
||||
[image],
|
||||
tag,
|
||||
category,
|
||||
excerptHtml excerpt
|
||||
${fromSegment}
|
||||
ORDER BY ${orderedBy}
|
||||
LIMIT ${opts.limit} OFFSET ${opts.offset}
|
||||
`
|
||||
)
|
||||
.all({ q }) as {
|
||||
path: string
|
||||
date?: Date
|
||||
title: string
|
||||
image?: string
|
||||
tag?: string[]
|
||||
category?: string
|
||||
excerpt: string
|
||||
}[]
|
||||
|
||||
return {
|
||||
result,
|
||||
count,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import path from 'path'
|
||||
|
||||
export const builtPath = (...ps: string[]): string =>
|
||||
path.join(__dirname, '../built', ...ps)
|
||||
|
||||
export const dstMediaPath = (...ps: string[]): string =>
|
||||
path.join(__dirname, '../static/media', ...ps)
|
||||
@@ -0,0 +1,58 @@
|
||||
import express from 'express'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { Database } from './db'
|
||||
|
||||
const app = express()
|
||||
const db = new Database({ readonly: true })
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
const { id } = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
})
|
||||
.parse(req.query)
|
||||
|
||||
const r = db.get(id)
|
||||
if (!r) {
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
|
||||
res.json(r)
|
||||
})
|
||||
|
||||
app.get('/q', (req, res) => {
|
||||
const { q, tag, page: _page = '1' } = z
|
||||
.object({
|
||||
q: z.string().optional(),
|
||||
tag: z.string().optional(),
|
||||
page: z
|
||||
.string()
|
||||
.refine((s) => /^\d+$/.test(s))
|
||||
.optional(),
|
||||
})
|
||||
.parse(req.query)
|
||||
|
||||
const perPage = 5
|
||||
const offset = (parseInt(_page) - 1) * perPage
|
||||
|
||||
if (q) {
|
||||
res.json(
|
||||
db.q(`${q} ${tag ? `tag:${tag}` : ''}`, {
|
||||
offset,
|
||||
limit: perPage,
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.json(
|
||||
db.by(tag ? `tag:${tag}` : '', {
|
||||
offset,
|
||||
limit: perPage,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
export default app
|
||||
Reference in new issue
Block a user