fix(providers): rename Freepik slug to Magnific and validate Magnific API keys (#10594)

Canonical provider id renamed freepik → magnific (Magnific Mystic official API), with a permanent redirect + runtime alias so old freepik/<model> traffic and /dashboard/providers/freepik URLs keep working. Existing provider=freepik connection rows are rewritten to magnific by migration 160.

Closes #10604.

Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 138 files):
- Focused suite: 54/54 tests pass (magnific-image-handler, provider-validation-image-only, provider-alias-uniqueness, redirects-cli-renames).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.

Co-authored-by: RaviTharuma <RaviTharuma@users.noreply.github.com>
This commit is contained in:
Ravi Tharuma
2026-08-21 03:15:48 +02:00
committed by GitHub
parent f71f3da08b
commit 2cd14b1696
139 changed files with 5734 additions and 4000 deletions

View File

@@ -17,6 +17,7 @@ import {
isClaudeCodeCompatibleProvider,
isOpenAICompatibleProvider,
isAnthropicCompatibleProvider,
resolveProviderId,
} from "@/shared/constants/providers";
import { getConsistentMachineId } from "@/shared/utils/machineId";
import { syncToCloud } from "@/lib/cloudSync";
@@ -114,7 +115,7 @@ export async function POST(request: Request) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}
const {
provider,
provider: requestedProvider,
apiKey,
name,
priority,
@@ -123,6 +124,7 @@ export async function POST(request: Request) {
testStatus,
providerSpecificData: incomingPsd,
} = validation.data;
const provider = resolveProviderId(requestedProvider);
// Business validation
const isValidProvider =

View File

@@ -519,13 +519,11 @@ async function buildUnifiedModelsResponseCore(
const targetModel = getComboTargetModelId(target);
if (!targetModel) return null;
const canonical = getCanonicalModelMetadata(
{
provider: targetModel.providerId,
model: targetModel.modelId,
},
capabilityResolutionSnapshot
);
const canonical = getCanonicalModelMetadata({
provider: targetModel.providerId,
model: targetModel.modelId,
snapshot: capabilityResolutionSnapshot,
});
if (!canonical) return null;
const providerId = canonical.provider || targetModel.providerId;

View File

@@ -1,3 +1,4 @@
import { z } from "zod";
import { handleChat } from "@/sse/handlers/chat";
import { CORS_HEADERS } from "@/shared/utils/cors";
import { createInjectionGuard } from "@/middleware/promptInjectionGuard";
@@ -114,9 +115,11 @@ async function postHandler(request: any) {
} catch {
return finishAdmission(errorResponse(400, "Invalid JSON body"));
}
if (!parsedBody || typeof parsedBody !== "object" || Array.isArray(parsedBody)) {
const parsed = z.object({}).passthrough().safeParse(parsedBody);
if (!parsed.success || Array.isArray(parsed.data)) {
return finishAdmission(errorResponse(400, "Request body must be a JSON object"));
}
parsedBody = parsed.data;
const structuralAdmission = await admitChatStructure(parsedBody, admission.lease, {
sessionId,