{"version":3,"sources":["jsdelivr-header.js","/npm/nodebb-plugin-meilisearch@0.7.6/library.js"],"names":[],"mappings":"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","file":"/npm/nodebb-plugin-meilisearch@0.7.6/library.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/nodebb-plugin-meilisearch@0.7.6/library.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","'use strict';\n\nconst winston = require.main.require('winston');\nconst { MeiliSearch } = require('meilisearch');\n\nconst routeHelpers = require.main.require('./src/routes/helpers');\nconst settings = require.main.require('./src/meta/settings');\nconst Posts = require.main.require('./src/posts');\nconst Topics = require.main.require('./src/topics');\nconst batch = require.main.require('./src/batch');\nconst Sockets = require.main.require('./src/socket.io');\nconst pubsub = require.main.require('./src/pubsub');\nconst plugins = require.main.require('./src/plugins');\n\nconst plugin = {};\n\nplugin.id = 'meilisearch';\n\nplugin.healthy = false;\n\nplugin.indexing = {\n\trunning: false,\n\ttopic_progress: {\n\t\ttotal: null,\n\t\tcurrent: null,\n\t},\n\tpost_progress: {\n\t\ttotal: null,\n\t\tcurrent: null,\n\t},\n};\n\n/** @type MeiliSearch */\nplugin.client = undefined;\nplugin.defaults = {\n\thost: 'http://localhost:7700',\n\tapiKey: undefined,\n\tmaxDocuments: undefined,\n\tindexed: false,\n\trankingRules: [\n\t\t{ rule: 'words' },\n\t\t{ rule: 'typo' },\n\t\t{ rule: 'proximity' },\n\t\t{ rule: 'attribute' },\n\t\t{ rule: 'sort' },\n\t\t{ rule: 'exactness' },\n\t],\n\tstopWords: [],\n\ttypoTolerance: 'on',\n\ttypoToleranceMinWordSizeOneTypo: 5,\n\ttypoToleranceMinWordSizeTwoTypos: 9,\n\ttypoToleranceDisableOnWords: [],\n\tsynonyms: [],\n\thealthCheckInterval: 60,\n};\n\nplugin.breakingSettings = [\n\t'maxDocuments',\n\t'rankingRules',\n\t'stopWords',\n\t'typoTolerance',\n\t'typoToleranceMinWordSizeOneTypo',\n\t'typoToleranceMinWordSizeTwoTypos',\n\t'typoToleranceDisableOnWords',\n\t'typoToleranceDisableOnAttributes',\n\t'synonyms',\n];\nplugin.initialized = false;\n\nplugin.initializingOnAnotherInstance = false;\n\nplugin.healthCheckTask = null;\n\npubsub.on('meilisearch:indexing', (indexing) => {\n\tplugin.indexing = indexing;\n\tSockets.server.to('admin/plugins/meilisearch').emit('plugins.meilisearch.reindex', plugin.indexing);\n});\npubsub.on('meilisearch:init', (initializing) => {\n\tplugin.initializingOnAnotherInstance = initializing;\n});\n\nplugin.init = async function (params) {\n\tconst { router } = params;\n\twinston.debug('[plugin/meilisearch] Initializing MeiliSearch plugin');\n\trouteHelpers.setupAdminPageRoute(router, '/admin/plugins/meilisearch', [], (req, res) => {\n\t\tres.render('admin/plugins/meilisearch', {\n\t\t\tindexing: plugin.indexing,\n\t\t\ttopicPercent: Math.round(100 * plugin.indexing.topic_progress.current / plugin.indexing.topic_progress.total),\n\t\t\tpostPercent: Math.round(100 * plugin.indexing.post_progress.current / plugin.indexing.post_progress.total),\n\t\t});\n\t});\n\tawait settings.setOnEmpty(plugin.id, plugin.defaults);\n\tawait plugin.prepareSearch();\n\tplugin.initialized = true;\n};\n\nplugin.prepareSearch = async function (data) {\n\twinston.debug(`[plugin/meilisearch] Connecting to MeiliSearch host: ${await settings.getOne(plugin.id, 'host')}`);\n\tplugin.client = new MeiliSearch({\n\t\thost: data?.host || await settings.getOne(plugin.id, 'host'),\n\t\tapiKey: data?.apiKey || await settings.getOne(plugin.id, 'apiKey') || undefined,\n\t});\n\tif (plugin.healthCheckTask) clearInterval(plugin.healthCheckTask);\n\tplugin.healthCheckTask = setInterval(\n\t\tplugin.checkHealth,\n\t\tparseInt(data?.healthCheckInterval || await settings.getOne(plugin.id, 'healthCheckInterval'), 10) * 1000,\n\t);\n\tif (\n\t\t!await settings.getOne(plugin.id, 'indexed') && await plugin.checkHealth() && !plugin.initializingOnAnotherInstance\n\t) {\n\t\tawait plugin.updateIndexSettings();\n\t\tawait plugin.reindex(false);\n\t}\n};\n\nplugin.addRoutes = async function ({ router, middleware, helpers }) {\n\tconst middlewares = [\n\t\tmiddleware.ensureLoggedIn,\n\t\tmiddleware.admin.checkPrivileges,\n\t];\n\trouteHelpers.setupApiRoute(router, 'get', '/meilisearch/reindex', middlewares, async (req, res) => {\n\t\thelpers.formatApiResponse(200, res, plugin.indexing);\n\t});\n\n\trouteHelpers.setupApiRoute(router, 'post', '/meilisearch/reindex', middlewares, async (req, res) => {\n\t\tawait settings.set(plugin.id, { indexed: false }, true);\n\t\tawait plugin.reindex(false);\n\t\thelpers.formatApiResponse(200, res, {\n\t\t\tindexed: await settings.getOne(plugin.id, 'indexed'),\n\t\t});\n\t});\n\trouteHelpers.setupApiRoute(router, 'delete', '/meilisearch/reindex', middlewares, async (req, res) => {\n\t\tawait settings.set(plugin.id, { indexed: false }, true);\n\t\tawait plugin.reindex(true);\n\t\thelpers.formatApiResponse(200, res, {\n\t\t\tindexed: await settings.getOne(plugin.id, 'indexed'),\n\t\t});\n\t});\n};\n\nplugin.addAdminNavigation = function (header) {\n\theader.plugins.push({\n\t\troute: '/plugins/meilisearch',\n\t\ticon: 'fa-tint',\n\t\tname: 'Meilisearch',\n\t});\n\n\treturn header;\n};\n\nplugin.checkHealth = async function () {\n\ttry {\n\t\tplugin.healthy = await plugin.client.isHealthy();\n\t\treturn plugin.healthy;\n\t} catch (err) {\n\t\tplugin.healthy = false;\n\t\twinston.warn(err);\n\t\treturn false;\n\t}\n};\n\nplugin.getNotices = async function (notices) {\n\tconst checkHealth = await plugin.checkHealth();\n\tnotices.push({\n\t\tdone: checkHealth,\n\t\tdoneText: 'MeiliSearch connection OK',\n\t\tnotDoneText: 'Could not connect to MeiliSearch',\n\t});\n\treturn notices;\n};\n\nplugin.updateIndexSettings = async (data) => {\n\tawait plugin.client.createIndex('post', { primaryKey: 'pid' });\n\tawait plugin.client.createIndex('topic', { primaryKey: 'tid' });\n\tdata = {\n\t\tmaxDocuments: parseInt(data?.maxDocuments || await settings.getOne(plugin.id, 'maxDocuments') || 500, 10),\n\t\trankingRules: (data?.rankingRules || await settings.getOne(plugin.id, 'rankingRules'))?.map(value => value.rule),\n\t\tstopWords: (data?.stopWords || await settings.getOne(plugin.id, 'stopWords'))?.map(value => value.word),\n\t\ttypoTolerance: ['on', true].includes(\n\t\t\tdata?.typoTolerance || await settings.getOne(plugin.id, 'typoTolerance') || undefined,\n\t\t),\n\t\ttypoToleranceMinWordSizeOneTypo: parseInt(\n\t\t\tdata?.typoToleranceMinWordSizeOneTypo ||\n\t\t\t\tawait settings.getOne(plugin.id, 'typoToleranceMinWordSizeOneTypo') || 5,\n\t\t\t10,\n\t\t),\n\t\ttypoToleranceMinWordSizeTwoTypos: parseInt(\n\t\t\tdata?.typoToleranceMinWordSizeTwoTypos ||\n\t\t\t\tawait settings.getOne(plugin.id, 'typoToleranceMinWordSizeTwoTypos') || 9,\n\t\t\t10,\n\t\t),\n\t\ttypoToleranceDisableOnWords:\n\t\t\t(data?.typoToleranceDisableOnWords || await settings.getOne(plugin.id, 'typoToleranceDisableOnWords') ||\n\t\t\t\tundefined)?.map(value => value.word),\n\t\tsynonyms: Object.fromEntries(\n\t\t\t(data?.synonyms || await settings.getOne(plugin.id, 'synonyms') || [])?.map((\n\t\t\t\t{ word, synonyms },\n\t\t\t) => [word, synonyms?.split(',').map(synonym => synonym.trim())]),\n\t\t),\n\t};\n\tawait plugin.client.index('post').updateSettings({\n\t\tfilterableAttributes: ['tid', 'cid', 'uid', 'timestamp'],\n\t\tsortableAttributes: ['timestamp', 'cid'],\n\t\tsearchableAttributes: ['content'],\n\t\tpagination: {\n\t\t\tmaxTotalHits: data.maxDocuments,\n\t\t},\n\t\trankingRules: data.rankingRules,\n\t\tstopWords: data.stopWords,\n\t\ttypoTolerance: {\n\t\t\tenabled: data.typoTolerance,\n\t\t\tminWordSizeForTypos: {\n\t\t\t\toneTypo: data.typoToleranceMinWordSizeOneTypo,\n\t\t\t\ttwoTypos: data.typoToleranceMinWordSizeTwoTypos,\n\t\t\t},\n\t\t\tdisableOnWords: data.typoToleranceDisableOnWords,\n\t\t},\n\t\tsynonyms: data.synonyms,\n\t});\n\tawait plugin.client.index('topic').updateSettings({\n\t\tfilterableAttributes: ['cid', 'uid', 'timestamp'],\n\t\tsortableAttributes: ['cid', 'title', 'timestamp'],\n\t\tsearchableAttributes: ['title'],\n\t\tpagination: {\n\t\t\tmaxTotalHits: data.maxDocuments,\n\t\t},\n\t\trankingRules: data.rankingRules,\n\t\tstopWords: data.stopWords,\n\t\ttypoTolerance: {\n\t\t\tenabled: data.typoTolerance,\n\t\t\tminWordSizeForTypos: {\n\t\t\t\toneTypo: data.typoToleranceMinWordSizeOneTypo,\n\t\t\t\ttwoTypos: data.typoToleranceMinWordSizeTwoTypos,\n\t\t\t},\n\t\t\tdisableOnWords: data.typoToleranceDisableOnWords,\n\t\t},\n\t\tsynonyms: data.synonyms,\n\t});\n};\n\nplugin.reindex = async function (force = false) {\n\tif (plugin.indexing.running) {\n\t\twinston.warn('[plugins/meilisearch] Already indexing');\n\t\treturn;\n\t}\n\tplugin.indexing = {\n\t\trunning: true,\n\t\ttopic_progress: {\n\t\t\tcurrent: 0,\n\t\t\ttotal: 0,\n\t\t},\n\t\tpost_progress: {\n\t\t\tcurrent: 0,\n\t\t\ttotal: 0,\n\t\t},\n\t};\n\tpubsub.publish('meilisearch:reindex', plugin.indexing);\n\twinston.info(`[plugin/meilisearch] Indexing posts and topics${force ? ' (forced)' : ''}`);\n\ttry {\n\t\tif (force) {\n\t\t\tawait plugin.client.index('post').deleteAllDocuments();\n\t\t\tawait plugin.client.index('topic').deleteAllDocuments();\n\t\t}\n\t\tPromise.all([\n\t\t\tbatch.processSortedSet(\n\t\t\t\t'topics:tid',\n\t\t\t\tasync (tids) => {\n\t\t\t\t\tplugin.indexing.topic_progress.current += tids.length;\n\t\t\t\t\tSockets.server.to('admin/plugins/meilisearch').emit('plugins.meilisearch.reindex', plugin.indexing);\n\t\t\t\t\tpubsub.publish('meilisearch:reindex', plugin.indexing);\n\t\t\t\t\tconst topics = await Topics.getTopicsFields(tids, ['tid', 'cid', 'uid', 'mainPid', 'title', 'timestamp']);\n\t\t\t\t\tawait plugin.client.index('topic').updateDocuments(\n\t\t\t\t\t\ttopics.map(topic => ({\n\t\t\t\t\t\t\ttid: topic.tid,\n\t\t\t\t\t\t\tcid: topic.cid,\n\t\t\t\t\t\t\tuid: topic.uid,\n\t\t\t\t\t\t\tmainPid: topic.mainPid,\n\t\t\t\t\t\t\ttitle: topic.title,\n\t\t\t\t\t\t\ttimestamp: topic.timestamp,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t{ primaryKey: 'tid' },\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tbatch: parseInt(await settings.getOne(plugin.id, 'maxDocuments') || 500, 10),\n\t\t\t\t\tprogress: plugin.indexing.topic_progress,\n\t\t\t\t},\n\t\t\t),\n\t\t\tbatch.processSortedSet(\n\t\t\t\t'posts:pid',\n\t\t\t\tasync (pids) => {\n\t\t\t\t\tplugin.indexing.post_progress.current += pids.length;\n\t\t\t\t\tSockets.server.to('admin/plugins/meilisearch').emit('plugins.meilisearch.reindex', plugin.indexing);\n\t\t\t\t\tpubsub.publish('meilisearch:reindex', plugin.indexing);\n\t\t\t\t\tconst posts = await Posts.getPostsFields(pids, ['pid', 'tid', 'uid', 'content', 'timestamp']);\n\t\t\t\t\tconst cids = await Posts.getCidsByPids(pids);\n\t\t\t\t\tawait plugin.client.index('post').updateDocuments(\n\t\t\t\t\t\tposts.map((post, index) => ({\n\t\t\t\t\t\t\tpid: post.pid,\n\t\t\t\t\t\t\ttid: post.tid,\n\t\t\t\t\t\t\tcid: cids[index],\n\t\t\t\t\t\t\tuid: post.uid,\n\t\t\t\t\t\t\tcontent: post.content,\n\t\t\t\t\t\t\ttimestamp: post.timestamp,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t{ primaryKey: 'pid' },\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tbatch: parseInt(await settings.getOne(plugin.id, 'maxDocuments') || 500, 10),\n\t\t\t\t\tprogress: plugin.indexing.post_progress,\n\t\t\t\t},\n\t\t\t),\n\t\t]).then(() => {\n\t\t\tplugin.indexing = {\n\t\t\t\trunning: false,\n\t\t\t\ttopic_progress: {\n\t\t\t\t\ttotal: null,\n\t\t\t\t\tcurrent: null,\n\t\t\t\t},\n\t\t\t\tpost_progress: {\n\t\t\t\t\ttotal: null,\n\t\t\t\t\tcurrent: null,\n\t\t\t\t},\n\t\t\t};\n\t\t\twinston.info('[plugin/meilisearch] Indexing complete');\n\t\t});\n\t} catch (err) {\n\t\twinston.error(`[plugin/meilisearch] Indexing failed: ${err.message}`);\n\t\tplugin.indexing = {\n\t\t\trunning: false,\n\t\t\ttopic_progress: {\n\t\t\t\ttotal: null,\n\t\t\t\tcurrent: null,\n\t\t\t},\n\t\t\tpost_progress: {\n\t\t\t\ttotal: null,\n\t\t\t\tcurrent: null,\n\t\t\t},\n\t\t};\n\t}\n\tawait settings.set(plugin.id, { indexed: true }, true);\n};\n\nplugin.indexPost = async function ({ post }) {\n\tif (!post.cid) {\n\t\tpost.cid = await Posts.getCidByPid(post.pid);\n\t}\n\tawait plugin.client.index('post').updateDocuments([\n\t\t{\n\t\t\tpid: post.pid,\n\t\t\ttid: post.tid,\n\t\t\tcid: post.cid,\n\t\t\tuid: post.uid,\n\t\t\tcontent: post.content,\n\t\t\ttimestamp: post.timestamp,\n\t\t},\n\t], { primaryKey: 'pid' });\n};\n\nplugin.deindexPost = async function ({ post }) {\n\tawait plugin.client.index('post').deleteDocument(post.pid);\n};\n\nplugin.indexTopic = async function ({ topic }) {\n\tawait plugin.client.index('topic').updateDocuments([{\n\t\ttid: topic.tid,\n\t\tcid: topic.cid,\n\t\tuid: topic.uid,\n\t\tmainPid: topic.mainPid,\n\t\ttitle: topic.title,\n\t\ttimestamp: topic.timestamp,\n\t}], { primaryKey: 'tid' });\n};\n\nplugin.deindexTopic = async function ({ topic }) {\n\tawait plugin.client.index('topic').deleteDocument(topic.tid);\n};\n\nplugin.checkConflict = function () {\n\tconst hooksToCheck = [\n\t\t'filter:search.query',\n\t\t'filter:topic.search',\n\t];\n\t// blacklist, in case someone makes a plugin using these hooks that doesn't conflict.\n\t// also, outside of dbsearch the user is expected to realize they installed two search plugins.\n\tconst conflictingPlugins = [\n\t\t'nodebb-plugin-dbsearch',\n\t\t'nodebb-plugin-solr',\n\t\t'nodebb-plugin-elasticsearch',\n\t\t'ndoebb-plugin-search-elasticsearch',\n\t];\n\tfor (const hook of hooksToCheck) {\n\t\tif (plugins.loadedHooks[hook].filter(hookData => conflictingPlugins.includes(hookData.id)).length >= 1) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n};\n\nplugin.search = async function (data) {\n\tif (plugin.checkConflict()) {\n\t\t// The dbsearch plugin was detected, abort search!\n\t\twinston.warn(\n\t\t\t'[plugin/meilisearch] Another search plugin (most likely dbsearch) is enabled, so search via Meilisearch was aborted.',\n\t\t);\n\t\treturn data;\n\t}\n\tif (!plugin.healthy && !(await plugin.checkHealth())) {\n\t\twinston.warn(\n\t\t\t'[plugin/meilisearch] Meilisearch instance did not return a healthy response, so search via Meilisearch was aborted.',\n\t\t);\n\t\treturn data;\n\t}\n\t// topic search uses term instead of content\n\tif (data.term) {\n\t\tdata.content = data.term;\n\t\tdata.index = 'post';\n\t\tdata.searchData = { tid: data.tid };\n\t}\n\twinston.debug(`[plugin/meilisearch] Searching for ${data.content} in ${data.index}`);\n\tconst searchData = data?.searchData;\n\tconst index = Array.isArray(data?.index) ? data.index[0] : data.index;\n\tconst id = `${index?.length ? index[0] : 'p'}id`;\n\tconst result = await plugin.client.index(data.index).search(data.content, {\n\t\tattributesToRetrieve: [id],\n\t\tlimit: parseInt(await settings.getOne(plugin.id, 'maxDocuments') || 500, 10),\n\t\tfilter: plugin.buildFilter(\n\t\t\tdata.cid,\n\t\t\tdata.uid,\n\t\t\tsearchData?.timeFilter,\n\t\t\tsearchData?.timeRange,\n\t\t\tsearchData?.tid,\n\t\t),\n\t\tsort: plugin.buildSort(searchData?.sortBy, searchData?.sortDirection),\n\t\tmatchingStrategy: data.matchWords === 'all' ? 'all' : 'last',\n\t});\n\tdata.ids = result.hits.map(hit => hit[id]);\n\treturn data;\n};\n\nplugin.buildFilter = function (categories, postedBy, timeFilter, timeRange, tid) {\n\tconst filter = [];\n\tif (categories?.length) {\n\t\tfilter.push(categories.map(cid => `cid = ${cid}`));\n\t}\n\tif (postedBy?.length) {\n\t\tfilter.push(postedBy.map(uid => `uid = ${uid}`));\n\t}\n\tif (timeFilter && timeRange) {\n\t\tfilter.push(`timestamp ${timeFilter === 'newer' ? '>' : '<'} ${Date.now() - (timeRange * 1000)}`);\n\t}\n\tif (tid) {\n\t\tfilter.push(`tid = ${tid}`);\n\t}\n\treturn filter.length ? filter : undefined;\n};\n\nplugin.buildSort = function (sortBy, sortDirection) {\n\tlet field = '';\n\tswitch (sortBy) {\n\t\tcase 'timestamp':\n\t\t\tfield = 'timestamp';\n\t\t\tbreak;\n\t\tcase 'topic.title':\n\t\t\tfield = 'title';\n\t\t\tbreak;\n\t\tcase 'category':\n\t\t\tfield = 'cid';\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n\treturn [`${field}:${sortDirection === 'ascending' ? 'asc' : 'desc'}`];\n};\n\nplugin.saveSettings = async (data) => {\n\tif (data.plugin === plugin.id && !data.quiet && plugin.initialized) {\n\t\ttry {\n\t\t\tawait plugin.prepareSearch(data.settings);\n\t\t\tif (\n\t\t\t\tObject.entries(data.settings).some(isBreaking)\n\t\t\t) {\n\t\t\t\twinston.info(`settings changed, updating index`);\n\t\t\t\tawait plugin.updateIndexSettings(data.settings);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\twinston.error(`[plugin/meilisearch] Error while saving settings: ${err.message}`);\n\t\t}\n\t}\n\treturn data;\n};\n\nasync function isBreaking([setting, value]) {\n\twinston.info(`[plugin/meilisearch] Checking if ${setting} with value ${JSON.stringify(value)} is breaking`);\n\treturn plugin.breakingSettings.includes(setting) && !deepCompare(await settings.getOne(plugin.id, setting), value);\n}\n\nfunction deepCompare(a, b) {\n\tif (typeof a !== typeof b) return false;\n\tswitch (typeof a) {\n\t\tcase 'object':\n\t\t\treturn Object.keys(a).length === Object.keys(b).length &&\n\t\t\t\tObject.keys(a).every(key => deepCompare(a[key], b[key]));\n\t\tcase 'string':\n\t\tcase 'number':\n\t\tcase 'boolean':\n\t\tdefault:\n\t\t\treturn a === b;\n\t}\n}\nmodule.exports = plugin;\n"]}