index.js 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import { ApiError } from "@/lib/api/errors";
  2. import { createQsirchProvider } from "@/lib/search/providers/qsirch";
  3. import { createFsProvider } from "@/lib/search/providers/fs";
  4. /**
  5. * Provider selection.
  6. *
  7. * We keep provider selection in one place:
  8. * - SEARCH_PROVIDER=qsirch | fs
  9. *
  10. * This allows:
  11. * - local dev: fs fallback
  12. * - production: qsirch (fast, indexed)
  13. */
  14. let cachedProvider = null;
  15. function normalizeProviderName(value) {
  16. return String(value || "")
  17. .trim()
  18. .toLowerCase();
  19. }
  20. function normalizeLower(value, fallback) {
  21. const raw = String(value ?? "").trim();
  22. return raw ? raw.toLowerCase() : fallback;
  23. }
  24. function normalizeTrim(value, fallback) {
  25. const raw = String(value ?? "").trim();
  26. return raw ? raw : fallback;
  27. }
  28. export function getSearchProvider() {
  29. if (cachedProvider) return cachedProvider;
  30. const providerName = normalizeProviderName(
  31. process.env.SEARCH_PROVIDER || "fs"
  32. );
  33. if (providerName === "fs") {
  34. cachedProvider = createFsProvider();
  35. return cachedProvider;
  36. }
  37. if (providerName === "qsirch") {
  38. // Important: normalize runtime env values so behavior matches validateEnv().
  39. // - validateEnv() accepts values case-insensitively.
  40. // - runtime must not accidentally pass "Modified"/"ASYNC" etc. to the provider.
  41. cachedProvider = createQsirchProvider({
  42. baseUrl: process.env.QSIRCH_BASE_URL,
  43. account: process.env.QSIRCH_ACCOUNT,
  44. password: process.env.QSIRCH_PASSWORD,
  45. pathPrefix: normalizeTrim(
  46. process.env.QSIRCH_PATH_PREFIX,
  47. "/Niederlassungen"
  48. ),
  49. dateField: normalizeLower(process.env.QSIRCH_DATE_FIELD, "modified"),
  50. mode: normalizeLower(process.env.QSIRCH_MODE, "sync"),
  51. });
  52. return cachedProvider;
  53. }
  54. throw new ApiError({
  55. status: 500,
  56. code: "SEARCH_BACKEND_UNAVAILABLE",
  57. message: "Internal server error",
  58. });
  59. }
  60. /**
  61. * Unified search entrypoint used by route handlers.
  62. *
  63. * @param {{
  64. * mode: "branch"|"multi"|"all",
  65. * branches: string[]|null,
  66. * q: string|null,
  67. * from: string|null,
  68. * to: string|null,
  69. * limit: number,
  70. * cursor: string|null
  71. * }} input
  72. */
  73. export async function search(input) {
  74. const provider = getSearchProvider();
  75. return provider.search(input);
  76. }