mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-07 07:42:13 +03:00
* feat(i18n): add Turkish locale-aware text helpers (search/sort) * test(i18n): cover null/whitespace edges + document compareTr/normalize contract * fix(i18n): route dashboard search through Turkish-safe matchesSearch * fix(i18n): sort user-visible lists with Turkish collation (compareTr) * fix(i18n): keep providerId tiebreaker as ASCII sort (technical id) * docs(i18n): document intentional lang=en in global-error boundary * chore(lint): guard against locale-unsafe toLowerCase().includes search * fix(i18n): migrate missed provider-name search + harden lint disable placement * fix(i18n): downgrade no-restricted-syntax to warn (incremental adoption) The rule errored on ~19 pre-existing toLowerCase().includes() call-sites in src/app accumulated since this PR's base. Keep it as a warning so the guard-rail guides future code without breaking the 0-errors lint gate (project policy: 0 errors, warnings tolerated). --------- Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
63 lines
2.4 KiB
TypeScript
63 lines
2.4 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { skillRegistry } from "@/lib/skills/registry";
|
|
import { parsePaginationParams, buildPaginatedResponse } from "@/shared/types/pagination";
|
|
import { getSkillsProviderSetting } from "@/lib/skills/providerSettings";
|
|
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
|
import { matchesSearch } from "@/shared/utils/turkishText";
|
|
|
|
const POPULAR_BY_PROVIDER = {
|
|
skillsmp: ["web-search", "file-reader", "sql-assistant", "devops-helper", "docs-assistant"],
|
|
skillssh: ["git", "terminal", "postgres", "kubernetes", "playwright"],
|
|
} as const;
|
|
|
|
export async function GET(request?: Request) {
|
|
const authError = await requireManagementAuth(request);
|
|
if (authError) return authError;
|
|
|
|
try {
|
|
await skillRegistry.loadFromDatabase();
|
|
const provider = await getSkillsProviderSetting();
|
|
const url = request?.url || "http://localhost/api/skills";
|
|
const parsedUrl = new URL(url);
|
|
const query = parsedUrl.searchParams.get("q")?.trim() || "";
|
|
const modeFilter = parsedUrl.searchParams.get("mode");
|
|
const sourceFilter = parsedUrl.searchParams.get("source");
|
|
|
|
let allSkills = skillRegistry.list();
|
|
|
|
if (query) {
|
|
allSkills = allSkills.filter((skill) => {
|
|
const tagsText = Array.isArray(skill.tags) ? skill.tags.join(" ") : "";
|
|
return (
|
|
matchesSearch(skill.name, query) ||
|
|
matchesSearch(skill.description, query) ||
|
|
matchesSearch(tagsText, query)
|
|
);
|
|
});
|
|
}
|
|
|
|
if (modeFilter === "on" || modeFilter === "off" || modeFilter === "auto") {
|
|
allSkills = allSkills.filter(
|
|
(skill) => (skill.mode || (skill.enabled ? "on" : "off")) === modeFilter
|
|
);
|
|
}
|
|
|
|
if (sourceFilter === "skillsmp" || sourceFilter === "skillssh" || sourceFilter === "local") {
|
|
allSkills = allSkills.filter((skill) => (skill.sourceProvider || "local") === sourceFilter);
|
|
}
|
|
|
|
const params = parsePaginationParams(parsedUrl.searchParams);
|
|
const paged = allSkills.slice((params.page - 1) * params.limit, params.page * params.limit);
|
|
const response = buildPaginatedResponse(paged, allSkills.length, params);
|
|
return NextResponse.json({
|
|
...response,
|
|
skills: response.data,
|
|
provider,
|
|
popularDefaults: POPULAR_BY_PROVIDER[provider],
|
|
});
|
|
} catch (err: unknown) {
|
|
const error = err instanceof Error ? err.message : String(err);
|
|
return NextResponse.json({ error }, { status: 500 });
|
|
}
|
|
}
|