diff --git a/packages/nuxt/.eslintrc.js b/packages/nuxt/.eslintrc.js index f06b5a5..5be73c0 100644 --- a/packages/nuxt/.eslintrc.js +++ b/packages/nuxt/.eslintrc.js @@ -16,5 +16,6 @@ module.exports = { rules: { 'require-await': 0, 'vue/no-v-html': 0, + 'import/order': 0, }, } diff --git a/packages/nuxt/assets/global.ts b/packages/nuxt/assets/global.ts index 6a5fcb2..bc68ad2 100644 --- a/packages/nuxt/assets/global.ts +++ b/packages/nuxt/assets/global.ts @@ -1,3 +1,46 @@ import type { ITheme } from '~/server/theme' export const THEME: ITheme = JSON.parse(process.env.THEME!) + +export const api = async ( + url: string, + opts: RequestInit & { + params?: Record + } = {} +) => { + if (!url.startsWith('/api')) { + return { + error: { + message: 'malformed API route', + }, + } + } + + const baseURL = new URL( + typeof location !== 'undefined' + ? location.origin + : `http://localhost:${process.env.PORT}` + ) + baseURL.pathname = url + Object.entries(opts.params || {}).map(([k, v]) => { + if (v) { + baseURL.searchParams.set(k, v) + } + }) + + const r = await fetch(baseURL.href, opts) + + try { + /** + * Does not rely on `r.ok`, but rather, errorhandler + */ + return r.json() + } catch (_) {} + + return { + error: { + statusCode: r.status, + message: r, + }, + } +} diff --git a/packages/nuxt/components/PostQuery.vue b/packages/nuxt/components/PostQuery.vue index 56a69e9..6a145c4 100644 --- a/packages/nuxt/components/PostQuery.vue +++ b/packages/nuxt/components/PostQuery.vue @@ -4,19 +4,15 @@ @@ -86,7 +73,7 @@ export default class PostQuery extends Vue { ...this.$route.query, page: page.toString(), }, - }) + }).href } } diff --git a/packages/nuxt/layouts/default.vue b/packages/nuxt/layouts/default.vue index 92b04e0..c8bb7e4 100644 --- a/packages/nuxt/layouts/default.vue +++ b/packages/nuxt/layouts/default.vue @@ -14,11 +14,7 @@
- +
🔎
@@ -34,9 +30,20 @@ import { THEME } from '~/assets/global' @Component export default class DefaultLayout extends Vue { theme = THEME + q = this.$route.query.q || '' doSearch() { - alert('not implemented') + let path = this.$route.path + if (['/blog', '/tag'].includes(path)) { + path = '/blog' + } + + this.$router.push({ + path, + query: { + q: this.q, + }, + }) } } diff --git a/packages/nuxt/layouts/error.vue b/packages/nuxt/layouts/error.vue index 6624ebe..04bb87e 100644 --- a/packages/nuxt/layouts/error.vue +++ b/packages/nuxt/layouts/error.vue @@ -1,8 +1,12 @@ diff --git a/packages/nuxt/nuxt.config.ts b/packages/nuxt/nuxt.config.ts index bf8f65e..9ef877d 100644 --- a/packages/nuxt/nuxt.config.ts +++ b/packages/nuxt/nuxt.config.ts @@ -4,6 +4,7 @@ import { getTheme } from './server/theme' const config = async (): Promise => { const theme = getTheme() + const PORT = process.env.PORT || '3000' return { // Global page headers (https://go.nuxtjs.dev/config-head) @@ -56,18 +57,13 @@ const config = async (): Promise => { ], // Modules (https://go.nuxtjs.dev/config-modules) - modules: [ - // https://go.nuxtjs.dev/axios - '@nuxtjs/axios', - ], - - // Axios module configuration (https://go.nuxtjs.dev/config-axios) - axios: {}, + modules: [], // Build Configuration (https://go.nuxtjs.dev/config-build) build: {}, env: { THEME: JSON.stringify(theme), + PORT, }, serverMiddleware: [{ path: '/api', handler: '~/server/index.ts' }], } diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 5bfff41..b3152f2 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -14,15 +14,16 @@ }, "dependencies": { "@nuxt/typescript-runtime": "^2.0.0", - "@nuxtjs/axios": "^5.12.2", "@patarapolw/scope-css": "^1.3.0", "@types/express": "^4.17.9", "@types/js-yaml": "^3.12.5", + "@types/morgan": "^1.9.2", "better-sqlite3": "^7.1.1", "cheerio": "^1.0.0-rc.3", "core-js": "^3.6.5", "express": "^4.17.1", "js-yaml": "^3.14.1", + "morgan": "^1.10.0", "nuxt": "^2.14.6", "showdown": "^1.9.1", "zod": "^1.11.11" @@ -66,4 +67,4 @@ "pnpm": "please-use-yarn" }, "engineStrict": true -} +} \ No newline at end of file diff --git a/packages/nuxt/pages/_tag.vue b/packages/nuxt/pages/_tag.vue index bd06e80..5a9dd6a 100644 --- a/packages/nuxt/pages/_tag.vue +++ b/packages/nuxt/pages/_tag.vue @@ -6,10 +6,15 @@ + + diff --git a/packages/nuxt/scripts/make-html.ts b/packages/nuxt/scripts/make-html.ts index 4f3ad52..f09b75d 100644 --- a/packages/nuxt/scripts/make-html.ts +++ b/packages/nuxt/scripts/make-html.ts @@ -73,7 +73,7 @@ export class MakeHTML { .update(filename) .digest() .toString('hex') - .substr(8) + .substr(0, 8) const $div = this.$(`
`) $div.html(await this.getHTML(filename)) @@ -87,10 +87,10 @@ export class MakeHTML { return { $: $div, - html() { - return $div.html() || '' + html: () => { + return this.$('
').append($div).html() || '' }, - clean() { + clean: () => { return jieba.cutForSearch($div.text() || '').join(' ') }, } diff --git a/packages/nuxt/server/db.ts b/packages/nuxt/server/db.ts index 5d1f6b8..16d448d 100644 --- a/packages/nuxt/server/db.ts +++ b/packages/nuxt/server/db.ts @@ -132,7 +132,6 @@ export class Database { [image], tag, category, - excerptHtml excerpt, contentHtml content FROM files f JOIN q ON q.path = f.path @@ -155,7 +154,6 @@ export class Database { image?: string tag?: string[] category?: string - excerpt: string content: string } } @@ -167,7 +165,10 @@ export class Database { limit: number } ) { - return this._search(q, opts, 'RANK') + return this._search(q, { + ...opts, + orderedBy: 'RANK', + }) } public by( @@ -177,11 +178,11 @@ export class Database { limit: number } ) { - return this._search( - q, - opts, - 'priority DESC, [date] DESC, createdAt DESC, updatedAt DESC' - ) + return this._search(q, { + ...opts, + orderedBy: 'priority DESC, [date] DESC, createdAt DESC, updatedAt DESC', + where: '[date] IS NOT NULL', + }) } private _search( @@ -189,13 +190,14 @@ export class Database { opts: { offset: number limit: number - }, - orderedBy: string + orderedBy: string + where?: string + } ) { const fromSegment = /* sql */ ` FROM files f JOIN q ON q.path = f.path - ${q ? `WHERE q = @q` : ''} + WHERE ${q ? `q = @q` : 'TRUE'} ${opts.where ? `AND ${opts.where}` : ''} ` const count = @@ -220,7 +222,7 @@ export class Database { category, excerptHtml excerpt ${fromSegment} - ORDER BY ${orderedBy} + ORDER BY ${opts.orderedBy} LIMIT ${opts.limit} OFFSET ${opts.offset} ` ) diff --git a/packages/nuxt/server/index.ts b/packages/nuxt/server/index.ts index 9b1dc03..5be1ad6 100644 --- a/packages/nuxt/server/index.ts +++ b/packages/nuxt/server/index.ts @@ -1,19 +1,22 @@ import express from 'express' +import morgan from 'morgan' import * as z from 'zod' import { Database } from './db' const app = express() +app.use(morgan('dev')) + const db = new Database({ readonly: true }) app.get('/', (req, res) => { - const { id } = z + const { path } = z .object({ - id: z.string(), + path: z.string(), }) .parse(req.query) - const r = db.get(id) + const r = db.get(path) if (!r) { res.sendStatus(404) return @@ -55,4 +58,10 @@ app.get('/q', (req, res) => { ) }) +app.use((err: Error, _: express.Request, res: express.Response) => { + // eslint-disable-next-line no-console + console.error(err.stack) + res.status(500).send(err) +}) + export default app diff --git a/packages/nuxt/tsconfig.json b/packages/nuxt/tsconfig.json index 1270666..d307d71 100644 --- a/packages/nuxt/tsconfig.json +++ b/packages/nuxt/tsconfig.json @@ -15,7 +15,7 @@ "~/*": ["./*"], "@/*": ["./*"] }, - "types": ["@types/node", "@nuxt/types", "@nuxtjs/axios"] + "types": ["@types/node", "@nuxt/types"] }, "exclude": ["node_modules", ".nuxt", "dist"] } diff --git a/packages/nuxt/yarn.lock b/packages/nuxt/yarn.lock index f80aa68..f0294d5 100644 --- a/packages/nuxt/yarn.lock +++ b/packages/nuxt/yarn.lock @@ -1242,17 +1242,6 @@ webpack-node-externals "^2.5.2" webpackbar "^4.0.0" -"@nuxtjs/axios@^5.12.2": - version "5.12.4" - resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.12.4.tgz#596ed88441aaedc5e74349f34fff2a462b02d01c" - integrity sha512-NKDIkrhtteOwC163Qlglw7YyjWHfLecpC/wXax4Z2tVVDlYnvSBa1KGoH9AnDY30lfvi0gjBKWCcTnUY8PaWDA== - dependencies: - "@nuxtjs/proxy" "^2.1.0" - axios "^0.21.0" - axios-retry "^3.1.9" - consola "^2.15.0" - defu "^3.2.2" - "@nuxtjs/eslint-config-typescript@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@nuxtjs/eslint-config-typescript/-/eslint-config-typescript-3.0.0.tgz#4c307ba1d7038d3d4a6aa40ebd4e0bd80a9af512" @@ -1284,13 +1273,6 @@ consola "^2.11.3" eslint-loader "^4.0.2" -"@nuxtjs/proxy@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@nuxtjs/proxy/-/proxy-2.1.0.tgz#fa7715a11d237fa1273503c4e9e137dd1bf5575b" - integrity sha512-/qtoeqXgZ4Mg6LRg/gDUZQrFpOlOdHrol/vQYMnKu3aN3bP90UfOUB3QSDghUUK7OISAJ0xp8Ld78aHyCTcKCQ== - dependencies: - http-proxy-middleware "^1.0.6" - "@nuxtjs/youch@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@nuxtjs/youch/-/youch-4.2.3.tgz#36f8b22df5a0efaa81373109851e1d857aca6bed" @@ -1476,13 +1458,6 @@ "@types/relateurl" "*" "@types/uglify-js" "*" -"@types/http-proxy@^1.17.4": - version "1.17.4" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.4.tgz#e7c92e3dbe3e13aa799440ff42e6d3a17a9d045b" - integrity sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q== - dependencies: - "@types/node" "*" - "@types/integer@*": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/integer/-/integer-1.0.1.tgz#025d87e30d97f539fcc6087372af7d3672ffbbe6" @@ -1525,6 +1500,13 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/morgan@^1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.2.tgz#450f958a4d3fb0694a3ba012b09c8106f9a2885e" + integrity sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ== + dependencies: + "@types/node" "*" + "@types/node-sass@^4.11.1": version "4.11.1" resolved "https://registry.yarnpkg.com/@types/node-sass/-/node-sass-4.11.1.tgz#bda27c5181cbf7c090c3058e119633dfb2b6504c" @@ -2306,20 +2288,6 @@ autoprefixer@^9.6.1: postcss "^7.0.32" postcss-value-parser "^4.1.0" -axios-retry@^3.1.9: - version "3.1.9" - resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.1.9.tgz#6c30fc9aeb4519aebaec758b90ef56fa03fe72e8" - integrity sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA== - dependencies: - is-retry-allowed "^1.1.0" - -axios@^0.21.0: - version "0.21.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.0.tgz#26df088803a2350dff2c27f96fef99fe49442aca" - integrity sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw== - dependencies: - follow-redirects "^1.10.0" - babel-eslint@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -2372,6 +2340,13 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + better-sqlite3@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-7.1.1.tgz#107457a8b770cfb16be646e347c17b42bc204dd3" @@ -3584,6 +3559,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -4217,11 +4197,6 @@ etag@^1.8.1, etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" @@ -4547,11 +4522,6 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@^1.0.0, follow-redirects@^1.10.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" - integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== - for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5076,26 +5046,6 @@ http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-proxy-middleware@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.0.6.tgz#0618557722f450375d3796d701a8ac5407b3b94e" - integrity sha512-NyL6ZB6cVni7pl+/IT2W0ni5ME00xR0sN27AQZZrpKn1b+qRh+mLbBxIq9Cq1oGfmTc7BUq4HB77mxwCaxAYNg== - dependencies: - "@types/http-proxy" "^1.17.4" - http-proxy "^1.18.1" - is-glob "^4.0.1" - lodash "^4.17.20" - micromatch "^4.0.2" - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -5484,11 +5434,6 @@ is-resolvable@^1.0.0: resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-ssh@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" @@ -6139,6 +6084,17 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" +morgan@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -8014,11 +7970,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - reserved-words@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1"