mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 21:32:10 +03:00
feat: add authentication to alias API and improve model save error handling
This commit is contained in:
@@ -1498,8 +1498,8 @@ function CompatibleModelsSection({
|
||||
|
||||
setAdding(true);
|
||||
try {
|
||||
// Save to customModels DB so it shows up in /v1/models
|
||||
await fetch("/api/provider-models", {
|
||||
// Save to customModels DB FIRST - only create alias if this succeeds
|
||||
const customModelRes = await fetch("/api/provider-models", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
@@ -1509,11 +1509,18 @@ function CompatibleModelsSection({
|
||||
source: "manual",
|
||||
}),
|
||||
});
|
||||
// Also create alias for routing
|
||||
|
||||
if (!customModelRes.ok) {
|
||||
const errorData = await customModelRes.json().catch(() => ({}));
|
||||
throw new Error(errorData.error?.message || "Failed to save custom model");
|
||||
}
|
||||
|
||||
// Only create alias after customModel is saved successfully
|
||||
await onSetAlias(modelId, resolvedAlias, providerStorageAlias);
|
||||
setNewModel("");
|
||||
} catch (error) {
|
||||
console.log("Error adding model:", error);
|
||||
alert(error instanceof Error ? error.message : "Failed to add model. Please try again.");
|
||||
} finally {
|
||||
setAdding(false);
|
||||
}
|
||||
@@ -1540,8 +1547,9 @@ function CompatibleModelsSection({
|
||||
if (!modelId) return false;
|
||||
const resolvedAlias = resolveAlias(modelId);
|
||||
if (!resolvedAlias) return false;
|
||||
// Save to customModels DB so it shows up in /v1/models
|
||||
await fetch("/api/provider-models", {
|
||||
|
||||
// Save to customModels DB FIRST - only create alias if this succeeds
|
||||
const customModelRes = await fetch("/api/provider-models", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
@@ -1551,7 +1559,13 @@ function CompatibleModelsSection({
|
||||
source: "imported",
|
||||
}),
|
||||
});
|
||||
// Also create alias for routing
|
||||
|
||||
if (!customModelRes.ok) {
|
||||
console.error("Failed to save imported model to customModels DB");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only create alias after customModel is saved successfully
|
||||
await onSetAlias(modelId, resolvedAlias, providerStorageAlias);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,46 @@ import { NextResponse } from "next/server";
|
||||
import { getModelAliases, setModelAlias, deleteModelAlias, isCloudEnabled } from "@/models";
|
||||
import { getConsistentMachineId } from "@/shared/utils/machineId";
|
||||
import { syncToCloud } from "@/lib/cloudSync";
|
||||
import { extractApiKey, isValidApiKey } from "@/sse/services/auth";
|
||||
import { jwtVerify } from "jose";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
/**
|
||||
* Verify authentication - check API key or JWT cookie
|
||||
*/
|
||||
async function verifyAuth(request) {
|
||||
// Check API key (for external clients)
|
||||
const apiKey = extractApiKey(request);
|
||||
if (apiKey && (await isValidApiKey(apiKey))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check JWT cookie (for dashboard session)
|
||||
if (process.env.JWT_SECRET) {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get("auth_token")?.value;
|
||||
if (token) {
|
||||
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
|
||||
await jwtVerify(token, secret);
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
// Invalid/expired token or cookies not available
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// GET /api/models/alias - Get all aliases
|
||||
export async function GET() {
|
||||
export async function GET(request) {
|
||||
try {
|
||||
// Require authentication for security
|
||||
if (!(await verifyAuth(request))) {
|
||||
return NextResponse.json({ error: "Authentication required" }, { status: 401 });
|
||||
}
|
||||
|
||||
const aliases = await getModelAliases();
|
||||
return NextResponse.json({ aliases });
|
||||
} catch (error) {
|
||||
@@ -17,6 +53,11 @@ export async function GET() {
|
||||
// PUT /api/models/alias - Set model alias
|
||||
export async function PUT(request) {
|
||||
try {
|
||||
// Require authentication for security
|
||||
if (!(await verifyAuth(request))) {
|
||||
return NextResponse.json({ error: "Authentication required" }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { model, alias } = body;
|
||||
|
||||
@@ -37,6 +78,11 @@ export async function PUT(request) {
|
||||
// DELETE /api/models/alias?alias=xxx - Delete alias
|
||||
export async function DELETE(request) {
|
||||
try {
|
||||
// Require authentication for security
|
||||
if (!(await verifyAuth(request))) {
|
||||
return NextResponse.json({ error: "Authentication required" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const alias = searchParams.get("alias");
|
||||
|
||||
|
||||
@@ -4,6 +4,37 @@ import {
|
||||
addCustomModel,
|
||||
removeCustomModel,
|
||||
} from "@/lib/localDb";
|
||||
import { extractApiKey, isValidApiKey } from "@/sse/services/auth";
|
||||
import { jwtVerify } from "jose";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
/**
|
||||
* Verify authentication - check API key or JWT cookie
|
||||
*/
|
||||
async function verifyAuth(request) {
|
||||
// Check API key (for external clients)
|
||||
const apiKey = extractApiKey(request);
|
||||
if (apiKey && (await isValidApiKey(apiKey))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check JWT cookie (for dashboard session)
|
||||
if (process.env.JWT_SECRET) {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get("auth_token")?.value;
|
||||
if (token) {
|
||||
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
|
||||
await jwtVerify(token, secret);
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
// Invalid/expired token or cookies not available
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/provider-models?provider=<id>
|
||||
@@ -11,6 +42,14 @@ import {
|
||||
*/
|
||||
export async function GET(request) {
|
||||
try {
|
||||
// Require authentication for security
|
||||
if (!(await verifyAuth(request))) {
|
||||
return Response.json(
|
||||
{ error: { message: "Authentication required", type: "invalid_api_key" } },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const provider = searchParams.get("provider");
|
||||
|
||||
@@ -31,6 +70,14 @@ export async function GET(request) {
|
||||
*/
|
||||
export async function POST(request) {
|
||||
try {
|
||||
// Require authentication for security
|
||||
if (!(await verifyAuth(request))) {
|
||||
return Response.json(
|
||||
{ error: { message: "Authentication required", type: "invalid_api_key" } },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { provider, modelId, modelName, source } = body;
|
||||
|
||||
@@ -56,6 +103,14 @@ export async function POST(request) {
|
||||
*/
|
||||
export async function DELETE(request) {
|
||||
try {
|
||||
// Require authentication for security
|
||||
if (!(await verifyAuth(request))) {
|
||||
return Response.json(
|
||||
{ error: { message: "Authentication required", type: "invalid_api_key" } },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const provider = searchParams.get("provider");
|
||||
const modelId = searchParams.get("model");
|
||||
|
||||
Reference in New Issue
Block a user