diff --git a/.vscode/settings.json b/.vscode/settings.json index 182fde60..8e3d0f16 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,18 @@ "sonarlint.rules": { "css:S4662": { "level": "off" + }, + "javascript:S6747": { + "level": "off" + }, + "javascript:S7764": { + "level": "off" + }, + "javascript:S6772": { + "level": "off" + }, + "javascript:S3776": { + "level": "off" } } } diff --git a/src/app/(dashboard)/dashboard/cli-tools/components/ClaudeToolCard.js b/src/app/(dashboard)/dashboard/cli-tools/components/ClaudeToolCard.js index b15caf94..b145c700 100644 --- a/src/app/(dashboard)/dashboard/cli-tools/components/ClaudeToolCard.js +++ b/src/app/(dashboard)/dashboard/cli-tools/components/ClaudeToolCard.js @@ -186,7 +186,7 @@ export default function ClaudeToolCard({
- {tool.name} { e.target.style.display = "none"; }} /> + {tool.name} { e.target.style.display = "none"; }} />
diff --git a/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js b/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js index fee7b342..5e7aa7a9 100644 --- a/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js +++ b/src/app/(dashboard)/dashboard/cli-tools/components/CodexToolCard.js @@ -159,7 +159,7 @@ wire_api = "responses"
- {tool.name} { e.target.style.display = "none"; }} /> + {tool.name} { e.target.style.display = "none"; }} />
diff --git a/src/app/(dashboard)/dashboard/cli-tools/components/DefaultToolCard.js b/src/app/(dashboard)/dashboard/cli-tools/components/DefaultToolCard.js index 8f945e45..172c2510 100644 --- a/src/app/(dashboard)/dashboard/cli-tools/components/DefaultToolCard.js +++ b/src/app/(dashboard)/dashboard/cli-tools/components/DefaultToolCard.js @@ -224,13 +224,14 @@ export default function DefaultToolCard({ toolId, tool, isExpanded, onToggle, ba const renderIcon = () => { if (tool.image) { return ( - {tool.name} { e.target.style.display = "none"; }} + {tool.name} { e.target.style.display = "none"; }} /> ); } @@ -238,13 +239,14 @@ export default function DefaultToolCard({ toolId, tool, isExpanded, onToggle, ba return {tool.icon}; } return ( - {tool.name} { e.target.style.display = "none"; }} + {tool.name} { e.target.style.display = "none"; }} /> ); }; diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.js b/src/app/(dashboard)/dashboard/providers/[id]/page.js index d43667f7..b4bee0c5 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.js +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.js @@ -1,13 +1,13 @@ "use client"; -import { useState, useEffect, useMemo, useCallback } from "react"; +import { useState, useEffect, useCallback } from "react"; +import PropTypes from "prop-types"; import { useParams } from "next/navigation"; import Link from "next/link"; import Image from "next/image"; import { Card, Button, Badge, Input, Modal, CardSkeleton, OAuthModal, Toggle } from "@/shared/components"; import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, getProviderAlias } from "@/shared/constants/providers"; import { getModelsByProviderId } from "@/shared/constants/models"; -import { PROVIDER_ENDPOINTS } from "@/shared/constants/config"; import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard"; export default function ProviderDetailPage() { @@ -193,6 +193,47 @@ export default function ProviderDetailPage() { } }; + const renderModelsSection = () => { + if (providerInfo.passthroughModels) { + return ( + + ); + } + if (models.length === 0) { + return

No models configured

; + } + return ( +
+ {models.map((model) => { + const fullModel = `${providerAlias}/${model.id}`; + const oldFormatModel = `${providerId}/${model.id}`; + const existingAlias = Object.entries(modelAliases).find( + ([, m]) => m === fullModel || m === oldFormatModel + )?.[0]; + return ( + handleSetAlias(model.id, alias)} + onDeleteAlias={() => handleDeleteAlias(existingAlias)} + /> + ); + })} +
+ ); + }; + if (!providerInfo) { return (
@@ -235,13 +276,14 @@ export default function ProviderDetailPage() { width={48} height={48} className="object-contain rounded-lg" + style={{ width: "auto", height: "auto" }} onError={(e) => { e.currentTarget.style.display = "none"; }} />

{providerInfo.name}

- {connections.length} connection{connections.length !== 1 ? "s" : ""} + {connections.length} connection{connections.length === 1 ? "" : "s"}

@@ -297,41 +339,7 @@ export default function ProviderDetailPage() {

{providerInfo.passthroughModels ? "Model Aliases" : "Available Models"}

- {providerInfo.passthroughModels ? ( - - ) : models.length === 0 ? ( -

No models configured

- ) : ( -
- {models.map((model) => { - const fullModel = `${providerAlias}/${model.id}`; - // Also check for old format (providerId/model) for backward compatibility - const oldFormatModel = `${providerId}/${model.id}`; - const existingAlias = Object.entries(modelAliases).find( - ([, m]) => m === fullModel || m === oldFormatModel - )?.[0]; - return ( - handleSetAlias(model.id, alias)} - onDeleteAlias={() => handleDeleteAlias(existingAlias)} - /> - ); - })} -
- )} + {renderModelsSection()} @@ -377,6 +385,16 @@ function ModelRow({ model, fullModel, alias, copied, onCopy }) { ); } +ModelRow.propTypes = { + model: PropTypes.shape({ + id: PropTypes.string.isRequired, + }).isRequired, + fullModel: PropTypes.string.isRequired, + alias: PropTypes.string, + copied: PropTypes.string, + onCopy: PropTypes.func.isRequired, +}; + function PassthroughModelsSection({ providerAlias, modelAliases, copied, onCopy, onSetAlias, onDeleteAlias }) { const [newModel, setNewModel] = useState(""); const [adding, setAdding] = useState(false); @@ -429,8 +447,9 @@ function PassthroughModelsSection({ providerAlias, modelAliases, copied, onCopy, {/* Add new model */}
- + setNewModel(e.target.value)} @@ -463,14 +482,23 @@ function PassthroughModelsSection({ providerAlias, modelAliases, copied, onCopy, ); } +PassthroughModelsSection.propTypes = { + providerAlias: PropTypes.string.isRequired, + modelAliases: PropTypes.object.isRequired, + copied: PropTypes.string, + onCopy: PropTypes.func.isRequired, + onSetAlias: PropTypes.func.isRequired, + onDeleteAlias: PropTypes.func.isRequired, +}; + function PassthroughModelRow({ modelId, fullModel, copied, onCopy, onDeleteAlias }) { return (
smart_toy - +

{modelId}

- +
{fullModel}

{provider.name}

- {getStatusDisplay()} + {getStatusDisplay(connected, error, errorCode)} {errorTime && • {errorTime}}
@@ -175,33 +178,26 @@ function ProviderCard({ providerId, provider, stats }) { ); } +ProviderCard.propTypes = { + providerId: PropTypes.string.isRequired, + provider: PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + color: PropTypes.string, + textIcon: PropTypes.string, + }).isRequired, + stats: PropTypes.shape({ + connected: PropTypes.number, + error: PropTypes.number, + errorCode: PropTypes.string, + errorTime: PropTypes.string, + }).isRequired, +}; + // API Key providers - only use textIcon, no image function ApiKeyProviderCard({ providerId, provider, stats }) { const { connected, error, errorCode, errorTime } = stats; - const getStatusDisplay = () => { - const parts = []; - if (connected > 0) { - parts.push( - - {connected} Connected - - ); - } - if (error > 0) { - const errText = errorCode ? `${error} Error (${errorCode})` : `${error} Error`; - parts.push( - - {errText} - - ); - } - if (parts.length === 0) { - return No connections; - } - return parts; - }; - return ( @@ -221,7 +217,7 @@ function ApiKeyProviderCard({ providerId, provider, stats }) {

{provider.name}

- {getStatusDisplay()} + {getStatusDisplay(connected, error, errorCode)} {errorTime && • {errorTime}}
@@ -234,3 +230,19 @@ function ApiKeyProviderCard({ providerId, provider, stats }) { ); } + +ApiKeyProviderCard.propTypes = { + providerId: PropTypes.string.isRequired, + provider: PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + color: PropTypes.string, + textIcon: PropTypes.string, + }).isRequired, + stats: PropTypes.shape({ + connected: PropTypes.number, + error: PropTypes.number, + errorCode: PropTypes.string, + errorTime: PropTypes.string, + }).isRequired, +}; diff --git a/src/app/api/auth/login/route.js b/src/app/api/auth/login/route.js index f6cbdb06..c095a83f 100644 --- a/src/app/api/auth/login/route.js +++ b/src/app/api/auth/login/route.js @@ -1,5 +1,5 @@ import { NextResponse } from "next/server"; -import { getSettings, updateSettings } from "@/lib/localDb"; +import { getSettings } from "@/lib/localDb"; import bcrypt from "bcryptjs"; import { SignJWT } from "jose"; import { cookies } from "next/headers"; @@ -17,12 +17,12 @@ export async function POST(request) { const storedHash = settings.password; let isValid = false; - if (!storedHash) { + if (storedHash) { + isValid = await bcrypt.compare(password, storedHash); + } else { // Use env var or default const initialPassword = process.env.INITIAL_PASSWORD || "123456"; isValid = password === initialPassword; - } else { - isValid = await bcrypt.compare(password, storedHash); } if (isValid) { diff --git a/src/app/landing/components/Navigation.js b/src/app/landing/components/Navigation.js index e6887f12..ba552d63 100644 --- a/src/app/landing/components/Navigation.js +++ b/src/app/landing/components/Navigation.js @@ -10,12 +10,17 @@ export default function Navigation() {