mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-23 04:34:15 -04:00
(search)(blog) add search for blog entries
This commit is contained in:
parent
bbfe946ac7
commit
e8f5f22ba7
@ -58,6 +58,7 @@
|
|||||||
"markdown-it-sup": "^2.0.0",
|
"markdown-it-sup": "^2.0.0",
|
||||||
"mastodon": "^1.2.2",
|
"mastodon": "^1.2.2",
|
||||||
"memorystore": "^1.6.7",
|
"memorystore": "^1.6.7",
|
||||||
|
"minisearch": "^7.1.1",
|
||||||
"nepali-calendar-js": "https://github.com/pixunil/nepali-calendar-js",
|
"nepali-calendar-js": "https://github.com/pixunil/nepali-calendar-js",
|
||||||
"node-fetch": "^2.6.12",
|
"node-fetch": "^2.6.12",
|
||||||
"nodemailer": "^6.7.8",
|
"nodemailer": "^6.7.8",
|
||||||
|
71
pages/search.vue
Normal file
71
pages/search.vue
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const search = ref('');
|
||||||
|
|
||||||
|
const results = useFetch('/api/search', {
|
||||||
|
query: computed(() => ({
|
||||||
|
text: search.value,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchInput = useTemplateRef('searchInput');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page>
|
||||||
|
<section class="sticky-top bg-white rounded">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<Icon v="search" />
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
ref="searchInput"
|
||||||
|
v-model="search"
|
||||||
|
type="search"
|
||||||
|
class="form-control border-primary"
|
||||||
|
:placeholder="$t('crud.filterLong')"
|
||||||
|
>
|
||||||
|
<button v-if="search" class="btn btn-outline-danger" @click="search = ''; searchInput?.focus()">
|
||||||
|
<Icon v="times" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<ul class="list-group">
|
||||||
|
<li v-for="result of results.data.value" :key="result.id" class="list-group-item">
|
||||||
|
<nuxt-link :to="result.url" class="d-flex text-dark">
|
||||||
|
<div class="col-auto pt-1 pe-3">
|
||||||
|
<Icon v="pen-nib" class="h3" />
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<img
|
||||||
|
v-if="result.hero"
|
||||||
|
:src="result.hero.src"
|
||||||
|
:class="['w-100', result.hero.class]"
|
||||||
|
:alt="result.hero.alt"
|
||||||
|
loading="lazy"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col ps-2">
|
||||||
|
<Spelling class="h4" :text="result.title" />
|
||||||
|
<ul class="list-inline mb-0 small">
|
||||||
|
<li class="list-inline-item small">
|
||||||
|
<Icon v="calendar" />
|
||||||
|
{{ result.date }}
|
||||||
|
</li>
|
||||||
|
<li v-for="author in result.authors" :key="author" class="list-inline-item">
|
||||||
|
<span v-if="author.startsWith('@')" class="badge bg-light text-dark border">
|
||||||
|
<Icon v="collective-logo.svg" class="invertible" />
|
||||||
|
{{ author }}
|
||||||
|
</span>
|
||||||
|
<span v-else class="badge bg-light text-dark border">
|
||||||
|
{{ author }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nuxt-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</Page>
|
||||||
|
</template>
|
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
@ -137,6 +137,9 @@ importers:
|
|||||||
memorystore:
|
memorystore:
|
||||||
specifier: ^1.6.7
|
specifier: ^1.6.7
|
||||||
version: 1.6.7
|
version: 1.6.7
|
||||||
|
minisearch:
|
||||||
|
specifier: ^7.1.1
|
||||||
|
version: 7.1.1
|
||||||
nepali-calendar-js:
|
nepali-calendar-js:
|
||||||
specifier: https://github.com/pixunil/nepali-calendar-js
|
specifier: https://github.com/pixunil/nepali-calendar-js
|
||||||
version: https://codeload.github.com/pixunil/nepali-calendar-js/tar.gz/a6e12cb1db54c508c49dc0c619cef0674e7c1cde
|
version: https://codeload.github.com/pixunil/nepali-calendar-js/tar.gz/a6e12cb1db54c508c49dc0c619cef0674e7c1cde
|
||||||
@ -6001,6 +6004,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||||
engines: {node: '>=16 || 14 >=14.17'}
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
|
|
||||||
|
minisearch@7.1.1:
|
||||||
|
resolution: {integrity: sha512-b3YZEYCEH4EdCAtYP7OlDyx7FdPwNzuNwLQ34SfJpM9dlbBZzeXndGavTrC+VCiRWomL21SWfMc6SCKO/U2ZNw==}
|
||||||
|
|
||||||
minizlib@2.1.2:
|
minizlib@2.1.2:
|
||||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -7619,16 +7625,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
|
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
tlds@1.255.0:
|
||||||
|
resolution: {integrity: sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
tldts-core@6.1.61:
|
tldts-core@6.1.61:
|
||||||
resolution: {integrity: sha512-In7VffkDWUPgwa+c9picLUxvb0RltVwTkSgMNFgvlGSWveCzGBemBqTsgJCL4EDFWZ6WH0fKTsot6yNhzy3ZzQ==}
|
resolution: {integrity: sha512-In7VffkDWUPgwa+c9picLUxvb0RltVwTkSgMNFgvlGSWveCzGBemBqTsgJCL4EDFWZ6WH0fKTsot6yNhzy3ZzQ==}
|
||||||
|
|
||||||
tldts-experimental@6.1.61:
|
tldts-experimental@6.1.61:
|
||||||
resolution: {integrity: sha512-1plwEyCpyYtVsZVtC169C5bStRlDk3cIniMHUeNmAJOjmQGx7SnLM8kS06PQAHx9PPY4Jm1VS6IXZzPC53XpbQ==}
|
resolution: {integrity: sha512-1plwEyCpyYtVsZVtC169C5bStRlDk3cIniMHUeNmAJOjmQGx7SnLM8kS06PQAHx9PPY4Jm1VS6IXZzPC53XpbQ==}
|
||||||
|
|
||||||
tlds@1.255.0:
|
|
||||||
resolution: {integrity: sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
to-fast-properties@2.0.0:
|
to-fast-properties@2.0.0:
|
||||||
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -15639,6 +15645,8 @@ snapshots:
|
|||||||
|
|
||||||
minipass@7.1.2: {}
|
minipass@7.1.2: {}
|
||||||
|
|
||||||
|
minisearch@7.1.1: {}
|
||||||
|
|
||||||
minizlib@2.1.2:
|
minizlib@2.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
minipass: 3.3.6
|
minipass: 3.3.6
|
||||||
@ -17593,14 +17601,14 @@ snapshots:
|
|||||||
|
|
||||||
tinyspy@3.0.2: {}
|
tinyspy@3.0.2: {}
|
||||||
|
|
||||||
|
tlds@1.255.0: {}
|
||||||
|
|
||||||
tldts-core@6.1.61: {}
|
tldts-core@6.1.61: {}
|
||||||
|
|
||||||
tldts-experimental@6.1.61:
|
tldts-experimental@6.1.61:
|
||||||
dependencies:
|
dependencies:
|
||||||
tldts-core: 6.1.61
|
tldts-core: 6.1.61
|
||||||
|
|
||||||
tlds@1.255.0: {}
|
|
||||||
|
|
||||||
to-fast-properties@2.0.0: {}
|
to-fast-properties@2.0.0: {}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
|
55
server/api/search.get.ts
Normal file
55
server/api/search.get.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import fs from 'node:fs/promises';
|
||||||
|
|
||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
import marked from 'marked';
|
||||||
|
import MiniSearch from 'minisearch';
|
||||||
|
|
||||||
|
import { getPosts, type PostMetadata } from '~/server/blog.ts';
|
||||||
|
import { loadSuml, loadSumlFromBase } from '~/server/loader.ts';
|
||||||
|
import { rootDir } from '~/server/paths.ts';
|
||||||
|
import parseMarkdown from '~/src/parseMarkdown.ts';
|
||||||
|
import { Translator } from '~/src/translator.ts';
|
||||||
|
|
||||||
|
const translations = loadSuml('translations');
|
||||||
|
const baseTranslations = loadSumlFromBase('locale/_base/translations');
|
||||||
|
|
||||||
|
const translator = new Translator(translations, baseTranslations, global.config);
|
||||||
|
|
||||||
|
interface SearchDocumentPost extends PostMetadata {
|
||||||
|
id: number;
|
||||||
|
url: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const index = new MiniSearch<SearchDocumentPost>({
|
||||||
|
fields: ['url', 'title', 'content'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const posts: SearchDocumentPost[] = [];
|
||||||
|
for (const post of (await getPosts())) {
|
||||||
|
const content = await fs.readFile(`${rootDir}/data/blog/${post.slug}.md`, 'utf-8');
|
||||||
|
const markdown = marked(content);
|
||||||
|
const parsed = await parseMarkdown(markdown, translator);
|
||||||
|
const text = JSDOM.fragment(parsed.content ?? '').textContent;
|
||||||
|
if (text !== null && config.links.enabled && config.links.blog) {
|
||||||
|
posts.push({
|
||||||
|
id: posts.length,
|
||||||
|
url: `/${encodeURIComponent(config.links.blogRoute)}/${post.slug}`,
|
||||||
|
title: post.title,
|
||||||
|
date: post.date,
|
||||||
|
authors: post.authors,
|
||||||
|
hero: post.hero,
|
||||||
|
content: text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index.addAll(posts);
|
||||||
|
|
||||||
|
const query = getQuery(event);
|
||||||
|
const text = query.text as string;
|
||||||
|
const results = index.search(text, { prefix: true, fuzzy: 1 });
|
||||||
|
return results.map((result) => {
|
||||||
|
return posts[result.id];
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user