skillguard/artifacts/api-server/src/routes/providers.ts

283 lines
8.2 KiB
TypeScript
Raw Normal View History

Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
import { Router, type IRouter, type Request } from "express";
import { db } from "@workspace/db";
import { aiProvidersTable, type AiProvider } from "@workspace/db";
import { eq } from "drizzle-orm";
import {
ListProvidersResponse,
CreateProviderBody,
UpdateProviderParams,
UpdateProviderBody,
UpdateProviderResponse,
DeleteProviderParams,
TestProviderParams,
TestProviderResponse,
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
TestProviderConnectionBody,
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
ListProviderModelsBody,
ListProviderModelsResponse,
} from "@workspace/api-zod";
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
import { callProvider, listProviderModels } from "../lib/aiAnalysis";
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
import { t, reqLang } from "../lib/i18n";
const router: IRouter = Router();
function maskToken(token: string | null): string {
if (!token) return "";
if (token.length <= 8) return "••••";
return `${token.slice(0, 3)}${token.slice(-4)}`;
}
function serializeProvider(p: AiProvider) {
return {
id: p.id,
name: p.name,
apiType: p.apiType,
baseUrl: p.baseUrl,
model: p.model,
enabled: p.enabled,
hasToken: !!p.apiToken,
tokenPreview: maskToken(p.apiToken),
createdAt: p.createdAt.toISOString(),
};
}
router.get("/providers", async (_req, res) => {
const rows = await db.select().from(aiProvidersTable).orderBy(aiProvidersTable.id);
res.json(ListProvidersResponse.parse(rows.map(serializeProvider)));
});
router.post("/providers", async (req, res) => {
const parsed = CreateProviderBody.safeParse(req.body);
if (!parsed.success)
return res
.status(400)
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
.json({ message: t("invalidInput", reqLang(req)), details: parsed.error.issues });
const d = parsed.data;
const [created] = await db
.insert(aiProvidersTable)
.values({
name: d.name,
apiType: d.apiType,
baseUrl: d.baseUrl,
model: d.model,
apiToken: d.apiToken ?? null,
enabled: d.enabled ?? true,
})
.returning();
return res
.status(201)
.json(UpdateProviderResponse.parse(serializeProvider(created)));
});
router.patch("/providers/:id", async (req, res) => {
const params = UpdateProviderParams.safeParse(req.params);
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
if (!params.success) return res.status(400).json({ message: t("invalidId", reqLang(req)) });
const parsed = UpdateProviderBody.safeParse(req.body);
if (!parsed.success)
return res
.status(400)
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
.json({ message: t("invalidInput", reqLang(req)), details: parsed.error.issues });
const d = parsed.data;
const update: Partial<typeof aiProvidersTable.$inferInsert> = {};
if (d.name !== undefined) update.name = d.name;
if (d.apiType !== undefined) update.apiType = d.apiType;
if (d.baseUrl !== undefined) update.baseUrl = d.baseUrl;
if (d.model !== undefined) update.model = d.model;
if (d.enabled !== undefined) update.enabled = d.enabled;
if (d.apiToken !== undefined && d.apiToken !== "")
update.apiToken = d.apiToken;
const [updated] = await db
.update(aiProvidersTable)
.set(update)
.where(eq(aiProvidersTable.id, params.data.id))
.returning();
if (!updated)
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
return res.status(404).json({ message: t("providerNotFound", reqLang(req)) });
return res.json(UpdateProviderResponse.parse(serializeProvider(updated)));
});
router.delete("/providers/:id", async (req, res) => {
const params = DeleteProviderParams.safeParse(req.params);
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
if (!params.success) return res.status(400).json({ message: t("invalidId", reqLang(req)) });
await db
.delete(aiProvidersTable)
.where(eq(aiProvidersTable.id, params.data.id));
return res.status(204).send();
});
router.post("/providers/:id/test", async (req, res) => {
const params = TestProviderParams.safeParse(req.params);
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
if (!params.success) return res.status(400).json({ message: t("invalidId", reqLang(req)) });
const [provider] = await db
.select()
.from(aiProvidersTable)
.where(eq(aiProvidersTable.id, params.data.id));
if (!provider)
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
return res.status(404).json({ message: t("providerNotFound", reqLang(req)) });
if (!provider.apiToken) {
return res.json(
TestProviderResponse.parse({
ok: false,
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
message: t("noApiTokenPlain", reqLang(req)),
}),
);
}
try {
const reply = await callProvider(
provider,
"Du bist ein Verbindungstest.",
'Antworte mit dem einzelnen Wort "OK".',
);
return res.json(
TestProviderResponse.parse({
ok: true,
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
message: t("connSuccessReply", reqLang(req), {
reply: reply.trim().slice(0, 80) || t("connReplyEmpty", reqLang(req)),
}),
}),
);
} catch (err) {
return res.json(
TestProviderResponse.parse({
ok: false,
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
message: err instanceof Error ? err.message : t("connFailed", reqLang(req)),
}),
);
}
});
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
router.post("/providers/test-connection", async (req, res) => {
const parsed = TestProviderConnectionBody.safeParse(req.body);
if (!parsed.success)
return res
.status(400)
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
.json({ message: t("invalidInput", reqLang(req)), details: parsed.error.issues });
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
const d = parsed.data;
let token: string | null =
d.apiToken && d.apiToken !== "" ? d.apiToken : null;
if (!token && d.providerId !== undefined && d.providerId !== null) {
const [existing] = await db
.select()
.from(aiProvidersTable)
.where(eq(aiProvidersTable.id, d.providerId));
if (existing?.apiToken) token = existing.apiToken;
}
if (!token) {
return res.json(
TestProviderResponse.parse({
ok: false,
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
message: t("noApiTokenProvided", reqLang(req)),
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
}),
);
}
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
const hasModel = typeof d.model === "string" && d.model.trim() !== "";
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
const provider: AiProvider = {
id: d.providerId ?? 0,
name: "",
apiType: d.apiType,
baseUrl: d.baseUrl,
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
model: hasModel ? (d.model as string) : "",
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
apiToken: token,
enabled: true,
createdAt: new Date(),
};
try {
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
if (hasModel) {
const reply = await callProvider(
provider,
"Du bist ein Verbindungstest.",
'Antworte mit dem einzelnen Wort "OK".',
);
return res.json(
TestProviderResponse.parse({
ok: true,
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
message: t("connSuccessReply", reqLang(req), {
reply: reply.trim().slice(0, 80) || t("connReplyEmpty", reqLang(req)),
}),
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
}),
);
}
const models = await listProviderModels(provider);
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
return res.json(
TestProviderResponse.parse({
ok: true,
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
message:
models.length > 0
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
? t("connSuccessModels", reqLang(req), { count: String(models.length) })
: t("connSuccessNoModels", reqLang(req)),
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
}),
);
} catch (err) {
return res.json(
TestProviderResponse.parse({
ok: false,
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
message: err instanceof Error ? err.message : t("connFailed", reqLang(req)),
Verbindungscheck beim Provider-Einrichten (Task #10) Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider dialogs so users can test a connection with the currently entered values before saving. Backend: - New endpoint POST /providers/test-connection that accepts an ad-hoc provider config (apiType, baseUrl, model, optional apiToken, optional providerId) in the request body and runs a one-shot test via the existing callProvider logic. When apiToken is empty and providerId is given, it falls back to the stored token of that provider (edit case). Returns { ok, message }; the token is never returned or leaked (existing redactSecrets still applies to errors). - Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran codegen for Zod schemas and the React client. Frontend (artifacts/skillguard/src/pages/admin.tsx): - Add dialog: "Verbindung testen" button (disabled until Base URL + Token set or while testing) with loading spinner and an inline green success / red error result box. Result resets when the dialog closes. - Edit dialog: same inline test; empty token field falls back to the stored token via providerId. Result resets on open/close. - The existing per-card "Verbindung testen" button is unchanged. Verification: typecheck passes for api-server and skillguard; curl tested the new endpoint for success-path (fetch error surfaced), empty-token, and invalid body (400) cases. Token not present in any response. Deviations: none. Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
}),
);
}
});
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
router.post("/providers/list-models", async (req, res) => {
const parsed = ListProviderModelsBody.safeParse(req.body);
if (!parsed.success)
return res
.status(400)
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
.json({ message: t("invalidInput", reqLang(req)), details: parsed.error.issues });
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
const d = parsed.data;
let token: string | null =
d.apiToken && d.apiToken !== "" ? d.apiToken : null;
if (!token && d.providerId !== undefined && d.providerId !== null) {
const [existing] = await db
.select()
.from(aiProvidersTable)
.where(eq(aiProvidersTable.id, d.providerId));
if (existing?.apiToken) token = existing.apiToken;
}
if (!token) {
return res.json(
ListProviderModelsResponse.parse({
ok: false,
models: [],
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
message: t("noApiTokenProvided", reqLang(req)),
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
}),
);
}
const provider: AiProvider = {
id: d.providerId ?? 0,
name: "",
apiType: d.apiType,
baseUrl: d.baseUrl,
model: "",
apiToken: token,
enabled: true,
createdAt: new Date(),
};
try {
const models = await listProviderModels(provider);
return res.json(
ListProviderModelsResponse.parse({ ok: true, models }),
);
} catch (err) {
return res.json(
ListProviderModelsResponse.parse({
ok: false,
models: [],
message:
err instanceof Error
? err.message
Add DE/EN/ES multilingual support to SkillGuard (Task #49) German is source of truth; EN/ES fully translated with no German residue. Auto-detects browser language (fallback German), persists choice, language switcher on all pages, localized formats/Clerk/legal. Scans store their language. Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n, language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls. Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req) (?lang query -> Accept-Language header -> "de"). No German left in routes. Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware format.ts, Clerk localizations. All page/component strings externalized to de/en/es locale area files across catalog, education, scan form/report/compare, history, dashboard, admin, legal pages. T006 verification + review-fix follow-up (this session): - Applied formatNumber to all visible metrics in scan-report (risk score, severity counts, security/privacy) and scan-compare (risk score, file count, diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng). - Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package `@workspace/api-client-react` (was failing workspace typecheck). - Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass; curl confirms localized error responses (de/en/es) on scans route. Deviations: AI connection-test prompts left in German intentionally (sent to the model, not user-facing). proposeFollowUpTasks already created #52. Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
: t("modelsLoadFailed", reqLang(req)),
Guided AI provider setup with model discovery Task: Replace free-text model entry in Admin → Providers with a guided flow (Name → API type → API endpoint → API token → Test connection) that auto-discovers available models after a successful connection test and presents them in a Select positioned right after the API endpoint field. Model-independent connection test (key fix): - The setup connection test no longer requires a model, removing the chicken-and-egg where discovery could never run. test-connection's model is now optional: when a model is supplied it does a full chat round-trip; when omitted it verifies credentials via the provider's models endpoint and reports how many models are available. The form sends no model on the initial test, so a successful test now reliably triggers discovery. Backend: - aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models using Bearer auth for openai/custom and x-api-key + anthropic-version for anthropic. Normalizes data[].id (falls back to models[].id/.name), dedupes + sorts, and redacts secrets in error messages via the existing redactSecrets helper. - providers.ts: added POST /providers/list-models accepting ad-hoc config (apiType, baseUrl, optional apiToken, optional providerId). Falls back to the stored token by providerId when token omitted; returns { ok, models, message } and never leaks the token. API contract: - openapi.yaml: added /providers/list-models path, ProviderListModelsInput and ProviderModelsResult schemas. Regenerated zod + react-query client via the api-spec codegen workflow (orval). Admin UI (admin.tsx): - New ModelField component renders a loading state, a Select when models are discovered, or a manual free-text input fallback (with hint) when discovery returns nothing — so saving always works for custom endpoints. - Field order follows the guided flow: Name → API type → API endpoint → API token → Test connection, with the model selector appearing after the token once discovery succeeds. A successful test automatically triggers discovery; editing endpoint or token resets discovery state. Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl of the new endpoint returns graceful errors without leaking the token. Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
}),
);
}
});
export default router;