From 2bf4b214232b39dfa1885eccd99db47432c5aade Mon Sep 17 00:00:00 2001 From: Tim Schipper Date: Mon, 4 Aug 2025 21:33:47 +0200 Subject: [PATCH 1/3] feat: add search controller + search and reverseSearch methods --- packages/core/server/controllers/index.ts | 2 + packages/core/server/controllers/search.ts | 80 ++++++++++++++++++++++ packages/core/server/routes/index.ts | 22 ++++++ 3 files changed, 104 insertions(+) create mode 100644 packages/core/server/controllers/search.ts diff --git a/packages/core/server/controllers/index.ts b/packages/core/server/controllers/index.ts index 7e77af6..70702ca 100644 --- a/packages/core/server/controllers/index.ts +++ b/packages/core/server/controllers/index.ts @@ -2,10 +2,12 @@ import urlAliasController from './url-alias'; import urlPatternController from './url-pattern'; import infoController from './info'; import coreController from './core'; +import searchController from './search'; export default { 'url-alias': urlAliasController, 'url-pattern': urlPatternController, info: infoController, core: coreController, + search: searchController, }; diff --git a/packages/core/server/controllers/search.ts b/packages/core/server/controllers/search.ts new file mode 100644 index 0000000..060bc02 --- /dev/null +++ b/packages/core/server/controllers/search.ts @@ -0,0 +1,80 @@ +import { Context } from 'koa'; +import { UID } from '@strapi/strapi'; +import { isContentTypeEnabled } from '../util/enabledContentTypes'; + +/** + * Search controller + */ + +export default { + search: async (ctx: Context & { params: { id: number } }) => { + const { q } = ctx.query; + const { id } = ctx.params; + let results = []; + + await Promise.all(Object.entries(strapi.contentTypes) + .map(async ([uid, config]: [UID.CollectionType, any]) => { + const hasWT = isContentTypeEnabled(uid); + + if (!hasWT) return; + + const coreStoreSettings = await strapi.query('strapi::core-store').findMany({ + where: { + key: `plugin_content_manager_configuration_content_types::${uid}`, + }, + }); + + if (!coreStoreSettings[0]) return; + + const value = JSON.parse(coreStoreSettings[0].value); + const { mainField } = value.settings; + + const entries = await strapi.entityService.findMany(uid, { + filters: { + [mainField]: { + $containsi: q, + }, + }, + fields: [mainField], + populate: { + url_alias: { + fields: ['id'], + }, + }, + }); + + const entriesWithContentType = entries?.map((entry) => ({ + ...entry, + contentType: uid, + })); + + // eslint-disable-next-line max-len + results = [...results, ...(Array.isArray(entriesWithContentType) ? entriesWithContentType : [])]; + })); + + // @ts-ignore + ctx.body = results; + }, + reverseSearch: async (ctx: Context & { params: { contentType: string; documentId: string } }) => { + const { contentType, documentId } = ctx.params; + + // Zoek met filters via findMany omdat documentId geen primaire ID is + const [entry] = await strapi.entityService.findMany(contentType as UID, { + filters: { documentId }, + fields: ['id', 'title', 'documentId'], + limit: 1, + }); + + if (!entry) { + ctx.throw(404, 'Entry not found'); + return; + } + + ctx.body = { + id: entry.id, + title: entry.title, + slug: entry.slug, + documentId: entry.documentId, + }; + }, +}; diff --git a/packages/core/server/routes/index.ts b/packages/core/server/routes/index.ts index 8811d69..780db7b 100644 --- a/packages/core/server/routes/index.ts +++ b/packages/core/server/routes/index.ts @@ -186,6 +186,28 @@ export default { policies: [], }, }, + /** + * Search routes + */ + { + method: 'GET', + path: '/search', + handler: 'search.search', + config: { + policies: [], + }, + }, + /** + * Reverse Search routes for a title or slug + */ + { + method: 'GET', + path: '/search/reverse/:contentType/:documentId', + handler: 'search.reverseSearch', + config: { + policies: [], + }, + }, ], }, }; From a3d15bab635cbf5cd4d06af34db8d02c0b1c701f Mon Sep 17 00:00:00 2001 From: Tim Schipper Date: Wed, 20 Aug 2025 14:13:02 +0200 Subject: [PATCH 2/3] feat: add helper service + resolve gitfeedback search controller --- packages/core/server/controllers/search.ts | 65 +++++++++---------- .../core/server/services/get-main-field.ts | 13 ++++ 2 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 packages/core/server/services/get-main-field.ts diff --git a/packages/core/server/controllers/search.ts b/packages/core/server/controllers/search.ts index 060bc02..0779c6a 100644 --- a/packages/core/server/controllers/search.ts +++ b/packages/core/server/controllers/search.ts @@ -1,56 +1,51 @@ import { Context } from 'koa'; import { UID } from '@strapi/strapi'; import { isContentTypeEnabled } from '../util/enabledContentTypes'; +import { getMainField } from '../services/get-main-field'; /** * Search controller */ - export default { search: async (ctx: Context & { params: { id: number } }) => { const { q } = ctx.query; const { id } = ctx.params; let results = []; - await Promise.all(Object.entries(strapi.contentTypes) - .map(async ([uid, config]: [UID.CollectionType, any]) => { - const hasWT = isContentTypeEnabled(uid); + const qStr = typeof q === 'string' ? q.trim() : ''; + if (!qStr) { + ctx.throw(400, 'Missing or invalid query parameter "?q=" (must be a non-empty strin4g)'); + return; + } + await Promise.all( + Object.entries(strapi.contentTypes).map(async ([uid, config]: [UID.CollectionType, any]) => { + const hasWT = isContentTypeEnabled(config); if (!hasWT) return; - const coreStoreSettings = await strapi.query('strapi::core-store').findMany({ - where: { - key: `plugin_content_manager_configuration_content_types::${uid}`, - }, - }); - - if (!coreStoreSettings[0]) return; + const mainField = await getMainField(uid); + if (!mainField) return; - const value = JSON.parse(coreStoreSettings[0].value); - const { mainField } = value.settings; - - const entries = await strapi.entityService.findMany(uid, { + const entries = await (strapi as any).documents(uid).findMany({ filters: { - [mainField]: { - $containsi: q, - }, + [mainField]: { $containsi: qStr }, }, - fields: [mainField], + fields: [mainField, 'documentId'], populate: { - url_alias: { - fields: ['id'], - }, + url_alias: { fields: ['id'] }, }, }); - const entriesWithContentType = entries?.map((entry) => ({ + if (!entries || entries.length === 0) return; + + const entriesWithContentType = entries.map((entry: any) => ({ ...entry, contentType: uid, })); - // eslint-disable-next-line max-len - results = [...results, ...(Array.isArray(entriesWithContentType) ? entriesWithContentType : [])]; - })); + results.push(...entriesWithContentType); + }), + ); // @ts-ignore ctx.body = results; @@ -58,11 +53,16 @@ export default { reverseSearch: async (ctx: Context & { params: { contentType: string; documentId: string } }) => { const { contentType, documentId } = ctx.params; - // Zoek met filters via findMany omdat documentId geen primaire ID is - const [entry] = await strapi.entityService.findMany(contentType as UID, { - filters: { documentId }, - fields: ['id', 'title', 'documentId'], - limit: 1, + if (typeof contentType !== 'string' || !(contentType in strapi.contentTypes)) { + ctx.throw(400, `Unknown or invalid content type: ${contentType}`); + return; + } + + const mainField = await getMainField(contentType as UID.CollectionType); + + const entry = await (strapi as any).documents(contentType as UID.CollectionType).findOne({ + documentId, + fields: ['id', 'documentId', ...(mainField ? [mainField] : [])], }); if (!entry) { @@ -72,9 +72,8 @@ export default { ctx.body = { id: entry.id, - title: entry.title, - slug: entry.slug, documentId: entry.documentId, + ...(mainField ? { [mainField]: entry[mainField] } : {}), }; }, }; diff --git a/packages/core/server/services/get-main-field.ts b/packages/core/server/services/get-main-field.ts new file mode 100644 index 0000000..32807da --- /dev/null +++ b/packages/core/server/services/get-main-field.ts @@ -0,0 +1,13 @@ +import { UID } from '@strapi/strapi'; + +export async function getMainField(uid: UID.CollectionType): Promise { + const coreStoreSettings = await strapi.query('strapi::core-store').findMany({ + where: { key: `plugin_content_manager_configuration_content_types::${uid}` }, + }); + if (!coreStoreSettings?.[0]) return null; + + console.log('Core store settings:', coreStoreSettings); + + const value = JSON.parse(coreStoreSettings[0].value); + return value?.settings?.mainField ?? null; +} From b5505a8b954b7262403a2be7165897f1ce7274bb Mon Sep 17 00:00:00 2001 From: Tim Schipper Date: Wed, 20 Aug 2025 14:44:33 +0200 Subject: [PATCH 3/3] cleanup: remove console.log --- packages/core/server/services/get-main-field.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/core/server/services/get-main-field.ts b/packages/core/server/services/get-main-field.ts index 32807da..43a8a37 100644 --- a/packages/core/server/services/get-main-field.ts +++ b/packages/core/server/services/get-main-field.ts @@ -6,8 +6,6 @@ export async function getMainField(uid: UID.CollectionType): Promise