@open-mercato/search 0.4.2-canary-c02407ff85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +678 -0
- package/build.mjs +92 -0
- package/dist/di.js +157 -0
- package/dist/di.js.map +7 -0
- package/dist/fulltext/drivers/index.js +21 -0
- package/dist/fulltext/drivers/index.js.map +7 -0
- package/dist/fulltext/drivers/meilisearch/index.js +320 -0
- package/dist/fulltext/drivers/meilisearch/index.js.map +7 -0
- package/dist/fulltext/index.js +7 -0
- package/dist/fulltext/index.js.map +7 -0
- package/dist/fulltext/types.js +1 -0
- package/dist/fulltext/types.js.map +7 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +7 -0
- package/dist/indexer/index.js +8 -0
- package/dist/indexer/index.js.map +7 -0
- package/dist/indexer/search-indexer.js +848 -0
- package/dist/indexer/search-indexer.js.map +7 -0
- package/dist/indexer/subscribers/delete.js +41 -0
- package/dist/indexer/subscribers/delete.js.map +7 -0
- package/dist/lib/debug.js +34 -0
- package/dist/lib/debug.js.map +7 -0
- package/dist/lib/fallback-presenter.js +107 -0
- package/dist/lib/fallback-presenter.js.map +7 -0
- package/dist/lib/field-policy.js +75 -0
- package/dist/lib/field-policy.js.map +7 -0
- package/dist/lib/index.js +19 -0
- package/dist/lib/index.js.map +7 -0
- package/dist/lib/merger.js +93 -0
- package/dist/lib/merger.js.map +7 -0
- package/dist/lib/presenter-enricher.js +192 -0
- package/dist/lib/presenter-enricher.js.map +7 -0
- package/dist/modules/search/acl.js +14 -0
- package/dist/modules/search/acl.js.map +7 -0
- package/dist/modules/search/ai-tools.js +284 -0
- package/dist/modules/search/ai-tools.js.map +7 -0
- package/dist/modules/search/api/embeddings/reindex/cancel/route.js +65 -0
- package/dist/modules/search/api/embeddings/reindex/cancel/route.js.map +7 -0
- package/dist/modules/search/api/embeddings/reindex/route.js +165 -0
- package/dist/modules/search/api/embeddings/reindex/route.js.map +7 -0
- package/dist/modules/search/api/embeddings/route.js +246 -0
- package/dist/modules/search/api/embeddings/route.js.map +7 -0
- package/dist/modules/search/api/index/route.js +245 -0
- package/dist/modules/search/api/index/route.js.map +7 -0
- package/dist/modules/search/api/reindex/cancel/route.js +65 -0
- package/dist/modules/search/api/reindex/cancel/route.js.map +7 -0
- package/dist/modules/search/api/reindex/route.js +332 -0
- package/dist/modules/search/api/reindex/route.js.map +7 -0
- package/dist/modules/search/api/search/global/route.js +100 -0
- package/dist/modules/search/api/search/global/route.js.map +7 -0
- package/dist/modules/search/api/search/route.js +101 -0
- package/dist/modules/search/api/search/route.js.map +7 -0
- package/dist/modules/search/api/settings/fulltext/route.js +55 -0
- package/dist/modules/search/api/settings/fulltext/route.js.map +7 -0
- package/dist/modules/search/api/settings/global-search/route.js +80 -0
- package/dist/modules/search/api/settings/global-search/route.js.map +7 -0
- package/dist/modules/search/api/settings/route.js +118 -0
- package/dist/modules/search/api/settings/route.js.map +7 -0
- package/dist/modules/search/api/settings/vector-store/route.js +77 -0
- package/dist/modules/search/api/settings/vector-store/route.js.map +7 -0
- package/dist/modules/search/backend/config/search/page.js +10 -0
- package/dist/modules/search/backend/config/search/page.js.map +7 -0
- package/dist/modules/search/backend/config/search/page.meta.js +24 -0
- package/dist/modules/search/backend/config/search/page.meta.js.map +7 -0
- package/dist/modules/search/cli.js +698 -0
- package/dist/modules/search/cli.js.map +7 -0
- package/dist/modules/search/di.js +32 -0
- package/dist/modules/search/di.js.map +7 -0
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js +357 -0
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js.map +7 -0
- package/dist/modules/search/frontend/components/HybridSearchTable.js +343 -0
- package/dist/modules/search/frontend/components/HybridSearchTable.js.map +7 -0
- package/dist/modules/search/frontend/components/SearchSettingsPageClient.js +303 -0
- package/dist/modules/search/frontend/components/SearchSettingsPageClient.js.map +7 -0
- package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js +360 -0
- package/dist/modules/search/frontend/components/sections/FulltextSearchSection.js.map +7 -0
- package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js +101 -0
- package/dist/modules/search/frontend/components/sections/GlobalSearchSection.js.map +7 -0
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js +608 -0
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js.map +7 -0
- package/dist/modules/search/frontend/index.js +9 -0
- package/dist/modules/search/frontend/index.js.map +7 -0
- package/dist/modules/search/frontend/utils.js +41 -0
- package/dist/modules/search/frontend/utils.js.map +7 -0
- package/dist/modules/search/i18n/de.json +61 -0
- package/dist/modules/search/i18n/en.json +72 -0
- package/dist/modules/search/i18n/es.json +61 -0
- package/dist/modules/search/i18n/pl.json +61 -0
- package/dist/modules/search/index.js +11 -0
- package/dist/modules/search/index.js.map +7 -0
- package/dist/modules/search/lib/auto-indexing.js +29 -0
- package/dist/modules/search/lib/auto-indexing.js.map +7 -0
- package/dist/modules/search/lib/embedding-config.js +131 -0
- package/dist/modules/search/lib/embedding-config.js.map +7 -0
- package/dist/modules/search/lib/global-search-config.js +45 -0
- package/dist/modules/search/lib/global-search-config.js.map +7 -0
- package/dist/modules/search/lib/reindex-lock.js +99 -0
- package/dist/modules/search/lib/reindex-lock.js.map +7 -0
- package/dist/modules/search/subscribers/fulltext_upsert.js +64 -0
- package/dist/modules/search/subscribers/fulltext_upsert.js.map +7 -0
- package/dist/modules/search/subscribers/vector_delete.js +58 -0
- package/dist/modules/search/subscribers/vector_delete.js.map +7 -0
- package/dist/modules/search/subscribers/vector_purge.js +142 -0
- package/dist/modules/search/subscribers/vector_purge.js.map +7 -0
- package/dist/modules/search/subscribers/vector_upsert.js +58 -0
- package/dist/modules/search/subscribers/vector_upsert.js.map +7 -0
- package/dist/modules/search/workers/fulltext-index.worker.js +240 -0
- package/dist/modules/search/workers/fulltext-index.worker.js.map +7 -0
- package/dist/modules/search/workers/vector-index.worker.js +234 -0
- package/dist/modules/search/workers/vector-index.worker.js.map +7 -0
- package/dist/queue/fulltext-indexing.js +15 -0
- package/dist/queue/fulltext-indexing.js.map +7 -0
- package/dist/queue/index.js +3 -0
- package/dist/queue/index.js.map +7 -0
- package/dist/queue/vector-indexing.js +15 -0
- package/dist/queue/vector-indexing.js.map +7 -0
- package/dist/service.js +286 -0
- package/dist/service.js.map +7 -0
- package/dist/strategies/fulltext.strategy.js +116 -0
- package/dist/strategies/fulltext.strategy.js.map +7 -0
- package/dist/strategies/index.js +12 -0
- package/dist/strategies/index.js.map +7 -0
- package/dist/strategies/token.strategy.js +80 -0
- package/dist/strategies/token.strategy.js.map +7 -0
- package/dist/strategies/vector.strategy.js +137 -0
- package/dist/strategies/vector.strategy.js.map +7 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +7 -0
- package/dist/vector/drivers/chromadb/index.js +44 -0
- package/dist/vector/drivers/chromadb/index.js.map +7 -0
- package/dist/vector/drivers/index.js +9 -0
- package/dist/vector/drivers/index.js.map +7 -0
- package/dist/vector/drivers/pgvector/index.js +509 -0
- package/dist/vector/drivers/pgvector/index.js.map +7 -0
- package/dist/vector/drivers/qdrant/index.js +44 -0
- package/dist/vector/drivers/qdrant/index.js.map +7 -0
- package/dist/vector/index.js +4 -0
- package/dist/vector/index.js.map +7 -0
- package/dist/vector/lib/vector-logs.js +33 -0
- package/dist/vector/lib/vector-logs.js.map +7 -0
- package/dist/vector/services/checksum.js +20 -0
- package/dist/vector/services/checksum.js.map +7 -0
- package/dist/vector/services/embedding.js +222 -0
- package/dist/vector/services/embedding.js.map +7 -0
- package/dist/vector/services/index.js +4 -0
- package/dist/vector/services/index.js.map +7 -0
- package/dist/vector/services/vector-index.service.js +960 -0
- package/dist/vector/services/vector-index.service.js.map +7 -0
- package/dist/vector/types/pg.d.js +1 -0
- package/dist/vector/types/pg.d.js.map +7 -0
- package/dist/vector/types.js +75 -0
- package/dist/vector/types.js.map +7 -0
- package/jest.config.cjs +19 -0
- package/package.json +142 -0
- package/src/__tests__/queue.test.ts +148 -0
- package/src/__tests__/service.test.ts +345 -0
- package/src/__tests__/workers.test.ts +319 -0
- package/src/di.ts +291 -0
- package/src/fulltext/drivers/index.ts +41 -0
- package/src/fulltext/drivers/meilisearch/index.ts +410 -0
- package/src/fulltext/index.ts +13 -0
- package/src/fulltext/types.ts +115 -0
- package/src/index.ts +36 -0
- package/src/indexer/index.ts +13 -0
- package/src/indexer/search-indexer.ts +1141 -0
- package/src/indexer/subscribers/delete.ts +49 -0
- package/src/lib/debug.ts +46 -0
- package/src/lib/fallback-presenter.ts +106 -0
- package/src/lib/field-policy.ts +169 -0
- package/src/lib/index.ts +13 -0
- package/src/lib/merger.ts +159 -0
- package/src/lib/presenter-enricher.ts +323 -0
- package/src/modules/search/README.md +694 -0
- package/src/modules/search/acl.ts +10 -0
- package/src/modules/search/ai-tools.ts +467 -0
- package/src/modules/search/api/embeddings/reindex/cancel/route.ts +77 -0
- package/src/modules/search/api/embeddings/reindex/route.ts +197 -0
- package/src/modules/search/api/embeddings/route.ts +304 -0
- package/src/modules/search/api/index/route.ts +297 -0
- package/src/modules/search/api/reindex/cancel/route.ts +77 -0
- package/src/modules/search/api/reindex/route.ts +419 -0
- package/src/modules/search/api/search/global/route.ts +120 -0
- package/src/modules/search/api/search/route.ts +121 -0
- package/src/modules/search/api/settings/fulltext/route.ts +82 -0
- package/src/modules/search/api/settings/global-search/route.ts +91 -0
- package/src/modules/search/api/settings/route.ts +187 -0
- package/src/modules/search/api/settings/vector-store/route.ts +105 -0
- package/src/modules/search/backend/config/search/page.meta.ts +22 -0
- package/src/modules/search/backend/config/search/page.tsx +12 -0
- package/src/modules/search/cli.ts +818 -0
- package/src/modules/search/di.ts +50 -0
- package/src/modules/search/frontend/components/GlobalSearchDialog.tsx +436 -0
- package/src/modules/search/frontend/components/HybridSearchTable.tsx +418 -0
- package/src/modules/search/frontend/components/SearchSettingsPageClient.tsx +476 -0
- package/src/modules/search/frontend/components/sections/FulltextSearchSection.tsx +624 -0
- package/src/modules/search/frontend/components/sections/GlobalSearchSection.tsx +124 -0
- package/src/modules/search/frontend/components/sections/VectorSearchSection.tsx +943 -0
- package/src/modules/search/frontend/index.ts +3 -0
- package/src/modules/search/frontend/utils.ts +82 -0
- package/src/modules/search/i18n/de.json +61 -0
- package/src/modules/search/i18n/en.json +72 -0
- package/src/modules/search/i18n/es.json +61 -0
- package/src/modules/search/i18n/pl.json +61 -0
- package/src/modules/search/index.ts +9 -0
- package/src/modules/search/lib/auto-indexing.ts +35 -0
- package/src/modules/search/lib/embedding-config.ts +161 -0
- package/src/modules/search/lib/global-search-config.ts +69 -0
- package/src/modules/search/lib/reindex-lock.ts +201 -0
- package/src/modules/search/subscribers/fulltext_upsert.ts +83 -0
- package/src/modules/search/subscribers/vector_delete.ts +75 -0
- package/src/modules/search/subscribers/vector_purge.ts +161 -0
- package/src/modules/search/subscribers/vector_upsert.ts +75 -0
- package/src/modules/search/workers/fulltext-index.worker.ts +318 -0
- package/src/modules/search/workers/vector-index.worker.ts +292 -0
- package/src/queue/fulltext-indexing.ts +87 -0
- package/src/queue/index.ts +2 -0
- package/src/queue/vector-indexing.ts +66 -0
- package/src/service.ts +397 -0
- package/src/strategies/fulltext.strategy.ts +155 -0
- package/src/strategies/index.ts +17 -0
- package/src/strategies/token.strategy.ts +153 -0
- package/src/strategies/vector.strategy.ts +234 -0
- package/src/types.ts +38 -0
- package/src/vector/drivers/chromadb/index.ts +49 -0
- package/src/vector/drivers/index.ts +4 -0
- package/src/vector/drivers/pgvector/index.ts +627 -0
- package/src/vector/drivers/qdrant/index.ts +49 -0
- package/src/vector/index.ts +3 -0
- package/src/vector/lib/vector-logs.ts +46 -0
- package/src/vector/services/checksum.ts +18 -0
- package/src/vector/services/embedding.ts +275 -0
- package/src/vector/services/index.ts +3 -0
- package/src/vector/services/vector-index.service.ts +1234 -0
- package/src/vector/types/pg.d.ts +1 -0
- package/src/vector/types.ts +220 -0
- package/tsconfig.json +9 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
3
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
4
|
+
const metadata = {
|
|
5
|
+
GET: { requireAuth: true, requireFeatures: ["search.view"] }
|
|
6
|
+
};
|
|
7
|
+
const unauthorized = async () => {
|
|
8
|
+
const { t } = await resolveTranslations();
|
|
9
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
10
|
+
};
|
|
11
|
+
async function GET(req) {
|
|
12
|
+
const auth = await getAuthFromRequest(req);
|
|
13
|
+
if (!auth?.sub) return await unauthorized();
|
|
14
|
+
const hostSet = Boolean(process.env.MEILISEARCH_HOST?.trim());
|
|
15
|
+
const apiKeySet = Boolean(process.env.MEILISEARCH_API_KEY?.trim());
|
|
16
|
+
const configured = hostSet && apiKeySet;
|
|
17
|
+
const indexPrefix = process.env.MEILISEARCH_INDEX_PREFIX?.trim();
|
|
18
|
+
const excludeEncrypted = ["1", "true", "yes", "on"].includes(
|
|
19
|
+
(process.env.SEARCH_EXCLUDE_ENCRYPTED_FIELDS ?? "").toLowerCase()
|
|
20
|
+
);
|
|
21
|
+
const response = {
|
|
22
|
+
driver: configured ? "meilisearch" : null,
|
|
23
|
+
configured,
|
|
24
|
+
envVars: {
|
|
25
|
+
MEILISEARCH_HOST: {
|
|
26
|
+
set: hostSet,
|
|
27
|
+
hint: "The URL of your Meilisearch server (e.g., http://localhost:7700)"
|
|
28
|
+
},
|
|
29
|
+
MEILISEARCH_API_KEY: {
|
|
30
|
+
set: apiKeySet,
|
|
31
|
+
hint: "API key for authentication with Meilisearch"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
optionalEnvVars: {
|
|
35
|
+
MEILISEARCH_INDEX_PREFIX: {
|
|
36
|
+
set: Boolean(indexPrefix),
|
|
37
|
+
value: indexPrefix,
|
|
38
|
+
default: "om",
|
|
39
|
+
hint: "Prefix for index names to namespace indexes per tenant"
|
|
40
|
+
},
|
|
41
|
+
SEARCH_EXCLUDE_ENCRYPTED_FIELDS: {
|
|
42
|
+
set: Boolean(process.env.SEARCH_EXCLUDE_ENCRYPTED_FIELDS),
|
|
43
|
+
value: excludeEncrypted,
|
|
44
|
+
default: false,
|
|
45
|
+
hint: "Exclude encrypted fields from full-text indexing for security"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return NextResponse.json(response);
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
GET,
|
|
53
|
+
metadata
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/search/api/settings/fulltext/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['search.view'] },\n}\n\ntype EnvVarStatus = {\n set: boolean\n hint: string\n}\n\ntype OptionalEnvVarStatus = {\n set: boolean\n value?: string | boolean\n default?: string | boolean\n hint: string\n}\n\ntype FulltextConfigResponse = {\n driver: 'meilisearch' | null\n configured: boolean\n envVars: {\n MEILISEARCH_HOST: EnvVarStatus\n MEILISEARCH_API_KEY: EnvVarStatus\n }\n optionalEnvVars: {\n MEILISEARCH_INDEX_PREFIX: OptionalEnvVarStatus\n SEARCH_EXCLUDE_ENCRYPTED_FIELDS: OptionalEnvVarStatus\n }\n}\n\nconst unauthorized = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n const hostSet = Boolean(process.env.MEILISEARCH_HOST?.trim())\n const apiKeySet = Boolean(process.env.MEILISEARCH_API_KEY?.trim())\n const configured = hostSet && apiKeySet\n\n const indexPrefix = process.env.MEILISEARCH_INDEX_PREFIX?.trim()\n const excludeEncrypted = ['1', 'true', 'yes', 'on'].includes(\n (process.env.SEARCH_EXCLUDE_ENCRYPTED_FIELDS ?? '').toLowerCase()\n )\n\n const response: FulltextConfigResponse = {\n driver: configured ? 'meilisearch' : null,\n configured,\n envVars: {\n MEILISEARCH_HOST: {\n set: hostSet,\n hint: 'The URL of your Meilisearch server (e.g., http://localhost:7700)',\n },\n MEILISEARCH_API_KEY: {\n set: apiKeySet,\n hint: 'API key for authentication with Meilisearch',\n },\n },\n optionalEnvVars: {\n MEILISEARCH_INDEX_PREFIX: {\n set: Boolean(indexPrefix),\n value: indexPrefix,\n default: 'om',\n hint: 'Prefix for index names to namespace indexes per tenant',\n },\n SEARCH_EXCLUDE_ENCRYPTED_FIELDS: {\n set: Boolean(process.env.SEARCH_EXCLUDE_ENCRYPTED_FIELDS),\n value: excludeEncrypted,\n default: false,\n hint: 'Exclude encrypted fields from full-text indexing for security',\n },\n },\n }\n\n return NextResponse.json(response)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AAE7B,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,aAAa,EAAE;AAC7D;AA2BA,MAAM,eAAe,YAAY;AAC/B,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,SAAO,aAAa,KAAK,EAAE,OAAO,EAAE,2BAA2B,cAAc,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AACnG;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,IAAK,QAAO,MAAM,aAAa;AAE1C,QAAM,UAAU,QAAQ,QAAQ,IAAI,kBAAkB,KAAK,CAAC;AAC5D,QAAM,YAAY,QAAQ,QAAQ,IAAI,qBAAqB,KAAK,CAAC;AACjE,QAAM,aAAa,WAAW;AAE9B,QAAM,cAAc,QAAQ,IAAI,0BAA0B,KAAK;AAC/D,QAAM,mBAAmB,CAAC,KAAK,QAAQ,OAAO,IAAI,EAAE;AAAA,KACjD,QAAQ,IAAI,mCAAmC,IAAI,YAAY;AAAA,EAClE;AAEA,QAAM,WAAmC;AAAA,IACvC,QAAQ,aAAa,gBAAgB;AAAA,IACrC;AAAA,IACA,SAAS;AAAA,MACP,kBAAkB;AAAA,QAChB,KAAK;AAAA,QACL,MAAM;AAAA,MACR;AAAA,MACA,qBAAqB;AAAA,QACnB,KAAK;AAAA,QACL,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,0BAA0B;AAAA,QACxB,KAAK,QAAQ,WAAW;AAAA,QACxB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,MACA,iCAAiC;AAAA,QAC/B,KAAK,QAAQ,QAAQ,IAAI,+BAA+B;AAAA,QACxD,OAAO;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,aAAa,KAAK,QAAQ;AACnC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
6
|
+
import {
|
|
7
|
+
resolveGlobalSearchStrategies,
|
|
8
|
+
saveGlobalSearchStrategies,
|
|
9
|
+
DEFAULT_GLOBAL_SEARCH_STRATEGIES
|
|
10
|
+
} from "../../../lib/global-search-config.js";
|
|
11
|
+
const updateSchema = z.object({
|
|
12
|
+
enabledStrategies: z.array(z.enum(["fulltext", "vector", "tokens"])).min(1)
|
|
13
|
+
});
|
|
14
|
+
const metadata = {
|
|
15
|
+
GET: { requireAuth: true, requireFeatures: ["search.view"] },
|
|
16
|
+
POST: { requireAuth: true, requireFeatures: ["search.manage"] }
|
|
17
|
+
};
|
|
18
|
+
const toJson = (payload, init) => NextResponse.json(payload, init);
|
|
19
|
+
const unauthorized = async () => {
|
|
20
|
+
const { t } = await resolveTranslations();
|
|
21
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
22
|
+
};
|
|
23
|
+
async function GET(req) {
|
|
24
|
+
const auth = await getAuthFromRequest(req);
|
|
25
|
+
if (!auth?.sub) return await unauthorized();
|
|
26
|
+
const container = await createRequestContainer();
|
|
27
|
+
try {
|
|
28
|
+
const enabledStrategies = await resolveGlobalSearchStrategies(container, {
|
|
29
|
+
defaultValue: DEFAULT_GLOBAL_SEARCH_STRATEGIES
|
|
30
|
+
});
|
|
31
|
+
return toJson({ enabledStrategies });
|
|
32
|
+
} finally {
|
|
33
|
+
const disposable = container;
|
|
34
|
+
if (typeof disposable.dispose === "function") {
|
|
35
|
+
await disposable.dispose();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function POST(req) {
|
|
40
|
+
const { t } = await resolveTranslations();
|
|
41
|
+
const auth = await getAuthFromRequest(req);
|
|
42
|
+
if (!auth?.sub) return await unauthorized();
|
|
43
|
+
let body;
|
|
44
|
+
try {
|
|
45
|
+
body = await req.json();
|
|
46
|
+
} catch {
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ error: t("api.errors.invalidPayload", "Invalid request body") },
|
|
49
|
+
{ status: 400 }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const parsed = updateSchema.safeParse(body);
|
|
53
|
+
if (!parsed.success) {
|
|
54
|
+
return NextResponse.json(
|
|
55
|
+
{ error: t("search.api.errors.invalidStrategies", "Invalid strategies configuration") },
|
|
56
|
+
{ status: 400 }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const container = await createRequestContainer();
|
|
60
|
+
try {
|
|
61
|
+
await saveGlobalSearchStrategies(container, parsed.data.enabledStrategies);
|
|
62
|
+
return NextResponse.json({ ok: true, enabledStrategies: parsed.data.enabledStrategies });
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ error: error instanceof Error ? error.message : t("api.errors.internal", "Internal error") },
|
|
66
|
+
{ status: 500 }
|
|
67
|
+
);
|
|
68
|
+
} finally {
|
|
69
|
+
const disposable = container;
|
|
70
|
+
if (typeof disposable.dispose === "function") {
|
|
71
|
+
await disposable.dispose();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
GET,
|
|
77
|
+
POST,
|
|
78
|
+
metadata
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/search/api/settings/global-search/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n resolveGlobalSearchStrategies,\n saveGlobalSearchStrategies,\n DEFAULT_GLOBAL_SEARCH_STRATEGIES,\n} from '../../../lib/global-search-config'\nimport type { SearchStrategyId } from '@open-mercato/shared/modules/search'\n\nconst updateSchema = z.object({\n enabledStrategies: z.array(z.enum(['fulltext', 'vector', 'tokens'])).min(1),\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['search.view'] },\n POST: { requireAuth: true, requireFeatures: ['search.manage'] },\n}\n\ntype SettingsResponse = {\n enabledStrategies: SearchStrategyId[]\n}\n\nconst toJson = (payload: SettingsResponse, init?: ResponseInit) => NextResponse.json(payload, init)\n\nconst unauthorized = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n const container = await createRequestContainer()\n try {\n const enabledStrategies = await resolveGlobalSearchStrategies(container, {\n defaultValue: DEFAULT_GLOBAL_SEARCH_STRATEGIES,\n })\n\n return toJson({ enabledStrategies })\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n\nexport async function POST(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json(\n { error: t('api.errors.invalidPayload', 'Invalid request body') },\n { status: 400 }\n )\n }\n\n const parsed = updateSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json(\n { error: t('search.api.errors.invalidStrategies', 'Invalid strategies configuration') },\n { status: 400 }\n )\n }\n\n const container = await createRequestContainer()\n try {\n await saveGlobalSearchStrategies(container, parsed.data.enabledStrategies)\n\n return NextResponse.json({ ok: true, enabledStrategies: parsed.data.enabledStrategies })\n } catch (error) {\n return NextResponse.json(\n { error: error instanceof Error ? error.message : t('api.errors.internal', 'Internal error') },\n { status: 500 }\n )\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,mBAAmB,EAAE,MAAM,EAAE,KAAK,CAAC,YAAY,UAAU,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC;AAC5E,CAAC;AAEM,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,aAAa,EAAE;AAAA,EAC3D,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAChE;AAMA,MAAM,SAAS,CAAC,SAA2B,SAAwB,aAAa,KAAK,SAAS,IAAI;AAElG,MAAM,eAAe,YAAY;AAC/B,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,SAAO,aAAa,KAAK,EAAE,OAAO,EAAE,2BAA2B,cAAc,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AACnG;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,IAAK,QAAO,MAAM,aAAa;AAE1C,QAAM,YAAY,MAAM,uBAAuB;AAC/C,MAAI;AACF,UAAM,oBAAoB,MAAM,8BAA8B,WAAW;AAAA,MACvE,cAAc;AAAA,IAChB,CAAC;AAED,WAAO,OAAO,EAAE,kBAAkB,CAAC;AAAA,EACrC,UAAE;AACA,UAAM,aAAa;AACnB,QAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,YAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,EACF;AACF;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,IAAK,QAAO,MAAM,aAAa;AAE1C,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,EAAE,6BAA6B,sBAAsB,EAAE;AAAA,MAChE,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,SAAS,aAAa,UAAU,IAAI;AAC1C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,EAAE,uCAAuC,kCAAkC,EAAE;AAAA,MACtF,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,MAAI;AACF,UAAM,2BAA2B,WAAW,OAAO,KAAK,iBAAiB;AAEzE,WAAO,aAAa,KAAK,EAAE,IAAI,MAAM,mBAAmB,OAAO,KAAK,kBAAkB,CAAC;AAAA,EACzF,SAAS,OAAO;AACd,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,EAAE,uBAAuB,gBAAgB,EAAE;AAAA,MAC7F,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF,UAAE;AACA,UAAM,aAAa;AACnB,QAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,YAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
3
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
4
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
5
|
+
import { getReindexLockStatus } from "../../lib/reindex-lock.js";
|
|
6
|
+
const metadata = {
|
|
7
|
+
GET: { requireAuth: true, requireFeatures: ["search.view"] }
|
|
8
|
+
};
|
|
9
|
+
const toJson = (payload, init) => NextResponse.json(payload, init);
|
|
10
|
+
const unauthorized = async () => {
|
|
11
|
+
const { t } = await resolveTranslations();
|
|
12
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
13
|
+
};
|
|
14
|
+
async function GET(req) {
|
|
15
|
+
const auth = await getAuthFromRequest(req);
|
|
16
|
+
if (!auth?.sub) return await unauthorized();
|
|
17
|
+
const container = await createRequestContainer();
|
|
18
|
+
try {
|
|
19
|
+
const strategies = [];
|
|
20
|
+
let defaultStrategies = [];
|
|
21
|
+
let fulltextStats = null;
|
|
22
|
+
try {
|
|
23
|
+
const searchService = container.resolve("searchService");
|
|
24
|
+
const searchStrategies = container.resolve("searchStrategies");
|
|
25
|
+
if (searchStrategies) {
|
|
26
|
+
for (const strategy of searchStrategies) {
|
|
27
|
+
const s = strategy;
|
|
28
|
+
const available = await s.isAvailable?.() ?? true;
|
|
29
|
+
strategies.push({
|
|
30
|
+
id: s.id ?? "unknown",
|
|
31
|
+
name: s.name ?? s.id ?? "unknown",
|
|
32
|
+
priority: s.priority ?? 0,
|
|
33
|
+
available
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (auth.tenantId) {
|
|
37
|
+
const fulltextStrategy = searchStrategies.find(
|
|
38
|
+
(s) => s?.id === "fulltext"
|
|
39
|
+
);
|
|
40
|
+
if (fulltextStrategy) {
|
|
41
|
+
try {
|
|
42
|
+
const stats = await fulltextStrategy.getIndexStats(auth.tenantId);
|
|
43
|
+
if (stats) {
|
|
44
|
+
fulltextStats = stats;
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (searchService) {
|
|
52
|
+
defaultStrategies = searchService.getDefaultStrategies?.() ?? [];
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
const fulltextConfigured = Boolean(
|
|
57
|
+
process.env.MEILISEARCH_HOST && process.env.MEILISEARCH_HOST.trim().length > 0
|
|
58
|
+
);
|
|
59
|
+
const vectorConfigured = Boolean(
|
|
60
|
+
process.env.OPENAI_API_KEY || process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.MISTRAL_API_KEY || process.env.COHERE_API_KEY || process.env.AWS_ACCESS_KEY_ID || process.env.OLLAMA_BASE_URL
|
|
61
|
+
);
|
|
62
|
+
const tokensEnabled = process.env.OM_SEARCH_ENABLED !== "false";
|
|
63
|
+
let fulltextReindexLock = null;
|
|
64
|
+
let vectorReindexLock = null;
|
|
65
|
+
if (auth.tenantId) {
|
|
66
|
+
const em = container.resolve("em");
|
|
67
|
+
const knex = em.getConnection().getKnex();
|
|
68
|
+
const fulltextLockStatus = await getReindexLockStatus(knex, auth.tenantId, { type: "fulltext" });
|
|
69
|
+
if (fulltextLockStatus) {
|
|
70
|
+
const startedAt = new Date(fulltextLockStatus.startedAt);
|
|
71
|
+
fulltextReindexLock = {
|
|
72
|
+
type: "fulltext",
|
|
73
|
+
action: fulltextLockStatus.action,
|
|
74
|
+
startedAt: fulltextLockStatus.startedAt,
|
|
75
|
+
elapsedMinutes: Math.round((Date.now() - startedAt.getTime()) / 6e4),
|
|
76
|
+
processedCount: fulltextLockStatus.processedCount,
|
|
77
|
+
totalCount: fulltextLockStatus.totalCount
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const vectorLockStatus = await getReindexLockStatus(knex, auth.tenantId, { type: "vector" });
|
|
81
|
+
if (vectorLockStatus) {
|
|
82
|
+
const startedAt = new Date(vectorLockStatus.startedAt);
|
|
83
|
+
vectorReindexLock = {
|
|
84
|
+
type: "vector",
|
|
85
|
+
action: vectorLockStatus.action,
|
|
86
|
+
startedAt: vectorLockStatus.startedAt,
|
|
87
|
+
elapsedMinutes: Math.round((Date.now() - startedAt.getTime()) / 6e4),
|
|
88
|
+
processedCount: vectorLockStatus.processedCount,
|
|
89
|
+
totalCount: vectorLockStatus.totalCount
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const reindexLock = fulltextReindexLock ?? vectorReindexLock;
|
|
94
|
+
return toJson({
|
|
95
|
+
settings: {
|
|
96
|
+
strategies,
|
|
97
|
+
fulltextConfigured,
|
|
98
|
+
fulltextStats,
|
|
99
|
+
vectorConfigured,
|
|
100
|
+
tokensEnabled,
|
|
101
|
+
defaultStrategies,
|
|
102
|
+
reindexLock,
|
|
103
|
+
fulltextReindexLock,
|
|
104
|
+
vectorReindexLock
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
} finally {
|
|
108
|
+
const disposable = container;
|
|
109
|
+
if (typeof disposable.dispose === "function") {
|
|
110
|
+
await disposable.dispose();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export {
|
|
115
|
+
GET,
|
|
116
|
+
metadata
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/search/api/settings/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { SearchService } from '@open-mercato/search'\nimport type { FullTextSearchStrategy } from '@open-mercato/search/strategies'\nimport type { Knex } from 'knex'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getReindexLockStatus } from '../../lib/reindex-lock'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['search.view'] },\n}\n\ntype StrategyStatus = {\n id: string\n name: string\n priority: number\n available: boolean\n}\n\ntype FulltextStats = {\n numberOfDocuments: number\n isIndexing: boolean\n fieldDistribution: Record<string, number>\n}\n\ntype ReindexLock = {\n type: 'fulltext' | 'vector'\n action: string\n startedAt: string\n elapsedMinutes: number\n processedCount?: number | null\n totalCount?: number | null\n}\n\ntype SearchSettings = {\n strategies: StrategyStatus[]\n fulltextConfigured: boolean\n fulltextStats: FulltextStats | null\n vectorConfigured: boolean\n tokensEnabled: boolean\n defaultStrategies: string[]\n /** @deprecated Use fulltextReindexLock or vectorReindexLock instead */\n reindexLock: ReindexLock | null\n fulltextReindexLock: ReindexLock | null\n vectorReindexLock: ReindexLock | null\n}\n\ntype SettingsResponse = {\n settings: SearchSettings\n}\n\nconst toJson = (payload: SettingsResponse, init?: ResponseInit) => NextResponse.json(payload, init)\n\nconst unauthorized = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n const container = await createRequestContainer()\n try {\n const strategies: StrategyStatus[] = []\n let defaultStrategies: string[] = []\n let fulltextStats: FulltextStats | null = null\n\n try {\n const searchService = container.resolve('searchService') as SearchService | undefined\n const searchStrategies = container.resolve('searchStrategies') as unknown[] | undefined\n\n if (searchStrategies) {\n for (const strategy of searchStrategies) {\n const s = strategy as { id?: string; name?: string; priority?: number; isAvailable?: () => Promise<boolean> }\n const available = await s.isAvailable?.() ?? true\n strategies.push({\n id: s.id ?? 'unknown',\n name: s.name ?? s.id ?? 'unknown',\n priority: s.priority ?? 0,\n available,\n })\n }\n\n // Get fulltext stats if available and tenant is set\n if (auth.tenantId) {\n const fulltextStrategy = searchStrategies.find(\n (s: unknown) => (s as { id?: string })?.id === 'fulltext'\n ) as FullTextSearchStrategy | undefined\n\n if (fulltextStrategy) {\n try {\n const stats = await fulltextStrategy.getIndexStats(auth.tenantId)\n if (stats) {\n fulltextStats = stats\n }\n } catch {\n // Stats not available\n }\n }\n }\n }\n\n if (searchService) {\n defaultStrategies = searchService.getDefaultStrategies?.() ?? []\n }\n } catch {\n // Search service may not be available\n }\n\n const fulltextConfigured = Boolean(\n process.env.MEILISEARCH_HOST && process.env.MEILISEARCH_HOST.trim().length > 0\n )\n\n const vectorConfigured = Boolean(\n process.env.OPENAI_API_KEY ||\n process.env.GOOGLE_GENERATIVE_AI_API_KEY ||\n process.env.MISTRAL_API_KEY ||\n process.env.COHERE_API_KEY ||\n process.env.AWS_ACCESS_KEY_ID ||\n process.env.OLLAMA_BASE_URL\n )\n\n const tokensEnabled = process.env.OM_SEARCH_ENABLED !== 'false'\n\n // Check for active reindex locks with heartbeat-based stale detection\n let fulltextReindexLock: ReindexLock | null = null\n let vectorReindexLock: ReindexLock | null = null\n\n if (auth.tenantId) {\n const em = container.resolve('em') as EntityManager\n const knex = (em.getConnection() as unknown as { getKnex: () => Knex }).getKnex()\n\n // Check fulltext lock (auto-cleans stale locks based on heartbeat)\n const fulltextLockStatus = await getReindexLockStatus(knex, auth.tenantId, { type: 'fulltext' })\n if (fulltextLockStatus) {\n const startedAt = new Date(fulltextLockStatus.startedAt)\n fulltextReindexLock = {\n type: 'fulltext',\n action: fulltextLockStatus.action,\n startedAt: fulltextLockStatus.startedAt,\n elapsedMinutes: Math.round((Date.now() - startedAt.getTime()) / 60000),\n processedCount: fulltextLockStatus.processedCount,\n totalCount: fulltextLockStatus.totalCount,\n }\n }\n\n // Check vector lock (auto-cleans stale locks based on heartbeat)\n const vectorLockStatus = await getReindexLockStatus(knex, auth.tenantId, { type: 'vector' })\n if (vectorLockStatus) {\n const startedAt = new Date(vectorLockStatus.startedAt)\n vectorReindexLock = {\n type: 'vector',\n action: vectorLockStatus.action,\n startedAt: vectorLockStatus.startedAt,\n elapsedMinutes: Math.round((Date.now() - startedAt.getTime()) / 60000),\n processedCount: vectorLockStatus.processedCount,\n totalCount: vectorLockStatus.totalCount,\n }\n }\n }\n\n // Keep deprecated reindexLock for backwards compatibility (prefer fulltext if both are active)\n const reindexLock = fulltextReindexLock ?? vectorReindexLock\n\n return toJson({\n settings: {\n strategies,\n fulltextConfigured,\n fulltextStats,\n vectorConfigured,\n tokensEnabled,\n defaultStrategies,\n reindexLock,\n fulltextReindexLock,\n vectorReindexLock,\n },\n })\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
3
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
4
|
+
const metadata = {
|
|
5
|
+
GET: { requireAuth: true, requireFeatures: ["search.view"] }
|
|
6
|
+
};
|
|
7
|
+
const unauthorized = async () => {
|
|
8
|
+
const { t } = await resolveTranslations();
|
|
9
|
+
return NextResponse.json({ error: t("api.errors.unauthorized", "Unauthorized") }, { status: 401 });
|
|
10
|
+
};
|
|
11
|
+
async function GET(req) {
|
|
12
|
+
const auth = await getAuthFromRequest(req);
|
|
13
|
+
if (!auth?.sub) return await unauthorized();
|
|
14
|
+
const databaseUrlSet = Boolean(process.env.DATABASE_URL?.trim());
|
|
15
|
+
const qdrantUrlSet = Boolean(process.env.QDRANT_URL?.trim());
|
|
16
|
+
const qdrantApiKeySet = Boolean(process.env.QDRANT_API_KEY?.trim());
|
|
17
|
+
const chromaUrlSet = Boolean(process.env.CHROMA_URL?.trim());
|
|
18
|
+
const drivers = [
|
|
19
|
+
{
|
|
20
|
+
id: "pgvector",
|
|
21
|
+
name: "PostgreSQL (pgvector)",
|
|
22
|
+
configured: databaseUrlSet,
|
|
23
|
+
implemented: true,
|
|
24
|
+
envVars: [
|
|
25
|
+
{
|
|
26
|
+
name: "DATABASE_URL",
|
|
27
|
+
set: databaseUrlSet,
|
|
28
|
+
hint: "PostgreSQL connection string with pgvector extension"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "qdrant",
|
|
34
|
+
name: "Qdrant",
|
|
35
|
+
configured: qdrantUrlSet,
|
|
36
|
+
implemented: false,
|
|
37
|
+
envVars: [
|
|
38
|
+
{
|
|
39
|
+
name: "QDRANT_URL",
|
|
40
|
+
set: qdrantUrlSet,
|
|
41
|
+
hint: "URL of your Qdrant server (e.g., http://localhost:6333)"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "QDRANT_API_KEY",
|
|
45
|
+
set: qdrantApiKeySet,
|
|
46
|
+
hint: "API key for Qdrant authentication (optional for local)"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "chromadb",
|
|
52
|
+
name: "ChromaDB",
|
|
53
|
+
configured: chromaUrlSet,
|
|
54
|
+
implemented: false,
|
|
55
|
+
envVars: [
|
|
56
|
+
{
|
|
57
|
+
name: "CHROMA_URL",
|
|
58
|
+
set: chromaUrlSet,
|
|
59
|
+
hint: "URL of your ChromaDB server (e.g., http://localhost:8000)"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
const currentDriver = "pgvector";
|
|
65
|
+
const configured = databaseUrlSet;
|
|
66
|
+
const response = {
|
|
67
|
+
currentDriver,
|
|
68
|
+
configured,
|
|
69
|
+
drivers
|
|
70
|
+
};
|
|
71
|
+
return NextResponse.json(response);
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
GET,
|
|
75
|
+
metadata
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/search/api/settings/vector-store/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { VectorDriverId } from '@open-mercato/shared/modules/vector'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['search.view'] },\n}\n\ntype DriverStatus = {\n id: VectorDriverId\n name: string\n configured: boolean\n implemented: boolean\n envVars: {\n name: string\n set: boolean\n hint: string\n }[]\n}\n\ntype VectorStoreConfigResponse = {\n currentDriver: VectorDriverId\n configured: boolean\n drivers: DriverStatus[]\n}\n\nconst unauthorized = async () => {\n const { t } = await resolveTranslations()\n return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) return await unauthorized()\n\n // Check pgvector - uses existing DATABASE_URL\n const databaseUrlSet = Boolean(process.env.DATABASE_URL?.trim())\n\n // Check qdrant - would need QDRANT_URL\n const qdrantUrlSet = Boolean(process.env.QDRANT_URL?.trim())\n const qdrantApiKeySet = Boolean(process.env.QDRANT_API_KEY?.trim())\n\n // Check chromadb - would need CHROMA_URL\n const chromaUrlSet = Boolean(process.env.CHROMA_URL?.trim())\n\n const drivers: DriverStatus[] = [\n {\n id: 'pgvector',\n name: 'PostgreSQL (pgvector)',\n configured: databaseUrlSet,\n implemented: true,\n envVars: [\n {\n name: 'DATABASE_URL',\n set: databaseUrlSet,\n hint: 'PostgreSQL connection string with pgvector extension',\n },\n ],\n },\n {\n id: 'qdrant',\n name: 'Qdrant',\n configured: qdrantUrlSet,\n implemented: false,\n envVars: [\n {\n name: 'QDRANT_URL',\n set: qdrantUrlSet,\n hint: 'URL of your Qdrant server (e.g., http://localhost:6333)',\n },\n {\n name: 'QDRANT_API_KEY',\n set: qdrantApiKeySet,\n hint: 'API key for Qdrant authentication (optional for local)',\n },\n ],\n },\n {\n id: 'chromadb',\n name: 'ChromaDB',\n configured: chromaUrlSet,\n implemented: false,\n envVars: [\n {\n name: 'CHROMA_URL',\n set: chromaUrlSet,\n hint: 'URL of your ChromaDB server (e.g., http://localhost:8000)',\n },\n ],\n },\n ]\n\n // Currently only pgvector is supported\n const currentDriver: VectorDriverId = 'pgvector'\n const configured = databaseUrlSet\n\n const response: VectorStoreConfigResponse = {\n currentDriver,\n configured,\n drivers,\n }\n\n return NextResponse.json(response)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AAG7B,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,aAAa,EAAE;AAC7D;AAoBA,MAAM,eAAe,YAAY;AAC/B,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,SAAO,aAAa,KAAK,EAAE,OAAO,EAAE,2BAA2B,cAAc,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AACnG;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,IAAK,QAAO,MAAM,aAAa;AAG1C,QAAM,iBAAiB,QAAQ,QAAQ,IAAI,cAAc,KAAK,CAAC;AAG/D,QAAM,eAAe,QAAQ,QAAQ,IAAI,YAAY,KAAK,CAAC;AAC3D,QAAM,kBAAkB,QAAQ,QAAQ,IAAI,gBAAgB,KAAK,CAAC;AAGlE,QAAM,eAAe,QAAQ,QAAQ,IAAI,YAAY,KAAK,CAAC;AAE3D,QAAM,UAA0B;AAAA,IAC9B;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,KAAK;AAAA,UACL,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,KAAK;AAAA,UACL,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,KAAK;AAAA,UACL,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,KAAK;AAAA,UACL,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,gBAAgC;AACtC,QAAM,aAAa;AAEnB,QAAM,WAAsC;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,aAAa,KAAK,QAAQ;AACnC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
3
|
+
import { SearchSettingsPageClient } from "../../../frontend/components/SearchSettingsPageClient.js";
|
|
4
|
+
async function SearchSettingsPage() {
|
|
5
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(SearchSettingsPageClient, {}) }) });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
SearchSettingsPage as default
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/search/backend/config/search/page.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { SearchSettingsPageClient } from '../../../frontend/components/SearchSettingsPageClient'\n\nexport default async function SearchSettingsPage() {\n return (\n <Page>\n <PageBody>\n <SearchSettingsPageClient />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "AAOQ;AAPR,SAAS,MAAM,gBAAgB;AAC/B,SAAS,gCAAgC;AAEzC,eAAO,qBAA4C;AACjD,SACE,oBAAC,QACC,8BAAC,YACC,8BAAC,4BAAyB,GAC5B,GACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
const searchIcon = React.createElement(
|
|
3
|
+
"svg",
|
|
4
|
+
{ width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
|
|
5
|
+
React.createElement("circle", { cx: 11, cy: 11, r: 8 }),
|
|
6
|
+
React.createElement("path", { d: "m21 21-4.3-4.3" })
|
|
7
|
+
);
|
|
8
|
+
const metadata = {
|
|
9
|
+
requireAuth: true,
|
|
10
|
+
requireFeatures: ["search.view"],
|
|
11
|
+
pageTitle: "Search Settings",
|
|
12
|
+
pageTitleKey: "search.config.nav.hybridSearch",
|
|
13
|
+
pageGroup: "Configuration",
|
|
14
|
+
pageGroupKey: "backend.nav.configuration",
|
|
15
|
+
pageOrder: 425,
|
|
16
|
+
icon: searchIcon,
|
|
17
|
+
breadcrumb: [
|
|
18
|
+
{ label: "Search Settings", labelKey: "search.config.nav.hybridSearch" }
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
export {
|
|
22
|
+
metadata
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/search/backend/config/search/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import React from 'react'\n\nconst searchIcon = React.createElement(\n 'svg',\n { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },\n React.createElement('circle', { cx: 11, cy: 11, r: 8 }),\n React.createElement('path', { d: 'm21 21-4.3-4.3' }),\n)\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['search.view'],\n pageTitle: 'Search Settings',\n pageTitleKey: 'search.config.nav.hybridSearch',\n pageGroup: 'Configuration',\n pageGroupKey: 'backend.nav.configuration',\n pageOrder: 425,\n icon: searchIcon,\n breadcrumb: [\n { label: 'Search Settings', labelKey: 'search.config.nav.hybridSearch' },\n ],\n} as const\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,WAAW;AAElB,MAAM,aAAa,MAAM;AAAA,EACvB;AAAA,EACA,EAAE,OAAO,IAAI,QAAQ,IAAI,SAAS,aAAa,MAAM,QAAQ,QAAQ,gBAAgB,aAAa,GAAG,eAAe,SAAS,gBAAgB,QAAQ;AAAA,EACrJ,MAAM,cAAc,UAAU,EAAE,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;AAAA,EACtD,MAAM,cAAc,QAAQ,EAAE,GAAG,iBAAiB,CAAC;AACrD;AAEO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,aAAa;AAAA,EAC/B,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,YAAY;AAAA,IACV,EAAE,OAAO,mBAAmB,UAAU,iCAAiC;AAAA,EACzE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|