mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
feat(i18n): migrate API Manager page (1008 lines, 43+ strings)
- ApiManagerPageClient: full migration including stats cards, table headers, modals, permissions system, validation messages, and usage tips - PermissionsModal: search, model selection, allow/restrict toggles - Expanded apiManager namespace from 18 to 67 keys in en.json and pt-BR.json
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useState, useEffect, useMemo, useCallback, memo } from "react";
|
||||
import { Card, Button, Input, Modal, CardSkeleton } from "@/shared/components";
|
||||
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
// Constants for validation
|
||||
const MAX_KEY_NAME_LENGTH = 100;
|
||||
@@ -70,6 +71,8 @@ interface Model {
|
||||
type ProviderGroup = [provider: string, models: Model[]];
|
||||
|
||||
export default function ApiManagerPageClient() {
|
||||
const t = useTranslations("apiManager");
|
||||
const tc = useTranslations("common");
|
||||
const [keys, setKeys] = useState<ApiKey[]>([]);
|
||||
const [allModels, setAllModels] = useState<Model[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -193,7 +196,7 @@ export default function ApiManagerPageClient() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm("Delete this API key?")) return;
|
||||
if (!confirm(t("deleteConfirm"))) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
clearError();
|
||||
@@ -332,7 +335,7 @@ export default function ApiManagerPageClient() {
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-bold">{keys.length}</p>
|
||||
<p className="text-xs text-text-muted">Total Keys</p>
|
||||
<p className="text-xs text-text-muted">{t("totalKeys")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -348,7 +351,7 @@ export default function ApiManagerPageClient() {
|
||||
.length
|
||||
}
|
||||
</p>
|
||||
<p className="text-xs text-text-muted">Restricted</p>
|
||||
<p className="text-xs text-text-muted">{t("restricted")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -361,7 +364,7 @@ export default function ApiManagerPageClient() {
|
||||
<p className="text-2xl font-bold">
|
||||
{Object.values(usageStats).reduce((sum, s) => sum + s.totalRequests, 0)}
|
||||
</p>
|
||||
<p className="text-xs text-text-muted">Total Requests</p>
|
||||
<p className="text-xs text-text-muted">{t("totalRequests")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -374,7 +377,7 @@ export default function ApiManagerPageClient() {
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-bold">{allModels.length}</p>
|
||||
<p className="text-xs text-text-muted">Models Available</p>
|
||||
<p className="text-xs text-text-muted">{t("modelsAvailable")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -385,13 +388,11 @@ export default function ApiManagerPageClient() {
|
||||
<Card>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">API Key Management</h2>
|
||||
<p className="text-sm text-text-muted">
|
||||
Create and manage API keys for authenticating requests to your endpoint
|
||||
</p>
|
||||
<h2 className="text-lg font-semibold">{t("keyManagement")}</h2>
|
||||
<p className="text-sm text-text-muted">{t("keyManagementDesc")}</p>
|
||||
</div>
|
||||
<Button icon="add" onClick={() => setShowAddModal(true)}>
|
||||
Create Key
|
||||
{t("createKey")}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -404,42 +405,40 @@ export default function ApiManagerPageClient() {
|
||||
<span className="material-symbols-outlined text-xl text-amber-500">vpn_key</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold">Registered Keys</h3>
|
||||
<h3 className="font-semibold">{t("registeredKeys")}</h3>
|
||||
<p className="text-xs text-text-muted">
|
||||
{keys.length} {keys.length === 1 ? "key" : "keys"} registered
|
||||
{keys.length}{" "}
|
||||
{keys.length === 1
|
||||
? t("keyRegistered", { count: keys.length })
|
||||
: t("keysRegistered", { count: keys.length })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-text-muted mb-4">
|
||||
Each key isolates usage tracking and can be revoked independently. Keys are masked after
|
||||
creation for security.
|
||||
</p>
|
||||
<p className="text-sm text-text-muted mb-4">{t("keysSecurityNote")}</p>
|
||||
|
||||
{keys.length === 0 ? (
|
||||
<div className="text-center py-12 border border-dashed border-border rounded-lg">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 text-primary mb-4">
|
||||
<span className="material-symbols-outlined text-[32px]">vpn_key</span>
|
||||
</div>
|
||||
<p className="text-text-main font-medium mb-2">No API keys yet</p>
|
||||
<p className="text-sm text-text-muted mb-4">
|
||||
Create your first API key to authenticate requests to your endpoint
|
||||
</p>
|
||||
<p className="text-text-main font-medium mb-2">{t("noKeys")}</p>
|
||||
<p className="text-sm text-text-muted mb-4">{t("noKeysDesc")}</p>
|
||||
<Button icon="add" onClick={() => setShowAddModal(true)}>
|
||||
Create Your First Key
|
||||
{t("createFirstKey")}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col border border-border rounded-lg overflow-hidden">
|
||||
{/* Table Header */}
|
||||
<div className="grid grid-cols-12 gap-4 px-4 py-3 bg-surface/50 border-b border-border text-xs font-semibold text-text-muted uppercase tracking-wider">
|
||||
<div className="col-span-2">Name</div>
|
||||
<div className="col-span-3">Key</div>
|
||||
<div className="col-span-2">Permissions</div>
|
||||
<div className="col-span-2">Usage</div>
|
||||
<div className="col-span-1">Created</div>
|
||||
<div className="col-span-2 text-right">Actions</div>
|
||||
<div className="col-span-2">{t("name")}</div>
|
||||
<div className="col-span-3">{t("key")}</div>
|
||||
<div className="col-span-2">{t("permissions")}</div>
|
||||
<div className="col-span-2">{t("usage")}</div>
|
||||
<div className="col-span-1">{t("created")}</div>
|
||||
<div className="col-span-2 text-right">{t("actions")}</div>
|
||||
</div>
|
||||
|
||||
{/* Table Rows */}
|
||||
@@ -489,21 +488,21 @@ export default function ApiManagerPageClient() {
|
||||
className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-green-500/10 text-green-600 dark:text-green-400 text-xs font-medium hover:bg-green-500/20 transition-colors"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[14px]">lock_open</span>
|
||||
All models
|
||||
{t("allModels")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-2 flex flex-col justify-center">
|
||||
<span className="text-sm font-medium tabular-nums">
|
||||
{stats?.totalRequests ?? 0}{" "}
|
||||
<span className="text-text-muted font-normal text-xs">reqs</span>
|
||||
<span className="text-text-muted font-normal text-xs">{t("reqs")}</span>
|
||||
</span>
|
||||
{stats?.lastUsed ? (
|
||||
<span className="text-[10px] text-text-muted">
|
||||
Last: {new Date(stats.lastUsed).toLocaleDateString()}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-[10px] text-text-muted italic">Never used</span>
|
||||
<span className="text-[10px] text-text-muted italic">{t("neverUsed")}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-1 flex items-center text-sm text-text-muted">
|
||||
@@ -539,28 +538,23 @@ export default function ApiManagerPageClient() {
|
||||
<span className="material-symbols-outlined text-xl text-blue-500">lightbulb</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold mb-2">Usage Tips</h3>
|
||||
<h3 className="font-semibold mb-2">{t("usageTips")}</h3>
|
||||
<ul className="text-sm text-text-muted space-y-1.5">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="material-symbols-outlined text-xs text-primary mt-1">check</span>
|
||||
<span>
|
||||
Use API keys in the{" "}
|
||||
<code className="text-xs bg-surface px-1.5 py-0.5 rounded">Authorization</code>{" "}
|
||||
header as{" "}
|
||||
<code className="text-xs bg-surface px-1.5 py-0.5 rounded">Bearer YOUR_KEY</code>
|
||||
</span>
|
||||
<span>{t("tipAuth")}</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="material-symbols-outlined text-xs text-primary mt-1">check</span>
|
||||
<span>Keys are only shown once during creation — store them securely</span>
|
||||
<span>{t("tipSecure")}</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="material-symbols-outlined text-xs text-primary mt-1">check</span>
|
||||
<span>Create separate keys for different clients or environments</span>
|
||||
<span>{t("tipSeparate")}</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="material-symbols-outlined text-xs text-primary mt-1">check</span>
|
||||
<span>Restrict keys to specific models for better security and cost control</span>
|
||||
<span>{t("tipRestrict")}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -570,7 +564,7 @@ export default function ApiManagerPageClient() {
|
||||
{/* Add Key Modal */}
|
||||
<Modal
|
||||
isOpen={showAddModal}
|
||||
title="Create API Key"
|
||||
title={t("createKey")}
|
||||
onClose={() => {
|
||||
setShowAddModal(false);
|
||||
setNewKeyName("");
|
||||
@@ -578,16 +572,16 @@ export default function ApiManagerPageClient() {
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-text-main mb-1.5 block">Key Name</label>
|
||||
<label className="text-sm font-medium text-text-main mb-1.5 block">
|
||||
{t("keyName")}
|
||||
</label>
|
||||
<Input
|
||||
value={newKeyName}
|
||||
onChange={(e) => setNewKeyName(e.target.value)}
|
||||
placeholder="e.g., Production Key, Development Key"
|
||||
placeholder={t("keyNamePlaceholder")}
|
||||
autoFocus
|
||||
/>
|
||||
<p className="text-xs text-text-muted mt-1.5">
|
||||
Choose a descriptive name to identify this key's purpose
|
||||
</p>
|
||||
<p className="text-xs text-text-muted mt-1.5">{t("keyNameDesc")}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
@@ -598,17 +592,17 @@ export default function ApiManagerPageClient() {
|
||||
variant="ghost"
|
||||
fullWidth
|
||||
>
|
||||
Cancel
|
||||
{tc("cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleCreateKey} fullWidth disabled={!newKeyName.trim()}>
|
||||
Create Key
|
||||
{t("createKey")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* Created Key Modal */}
|
||||
<Modal isOpen={!!createdKey} title="API Key Created" onClose={() => setCreatedKey(null)}>
|
||||
<Modal isOpen={!!createdKey} title={t("keyCreated")} onClose={() => setCreatedKey(null)}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
@@ -617,11 +611,9 @@ export default function ApiManagerPageClient() {
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm text-green-800 dark:text-green-200 font-medium mb-1">
|
||||
Key created successfully!
|
||||
</p>
|
||||
<p className="text-sm text-green-700 dark:text-green-300">
|
||||
Copy and store this key now — it won't be shown again.
|
||||
{t("keyCreatedSuccess")}
|
||||
</p>
|
||||
<p className="text-sm text-green-700 dark:text-green-300">{t("keyCreatedNote")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -632,7 +624,7 @@ export default function ApiManagerPageClient() {
|
||||
icon={copied === "created_key" ? "check" : "content_copy"}
|
||||
onClick={() => copy(createdKey, "created_key")}
|
||||
>
|
||||
{copied === "created_key" ? "Copied!" : "Copy"}
|
||||
{copied === "created_key" ? tc("copied") : tc("copy")}
|
||||
</Button>
|
||||
</div>
|
||||
<Button onClick={() => setCreatedKey(null)} fullWidth>
|
||||
@@ -683,6 +675,9 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
onSearchChange: (v: string) => void;
|
||||
onSave: (models: string[]) => void;
|
||||
}) {
|
||||
const t = useTranslations("apiManager");
|
||||
const tc = useTranslations("common");
|
||||
|
||||
// Initialize state from props - component remounts when key prop changes
|
||||
const initialModels = Array.isArray(apiKey?.allowedModels) ? apiKey.allowedModels : [];
|
||||
const [selectedModels, setSelectedModels] = useState<string[]>(initialModels);
|
||||
@@ -773,7 +768,7 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
return (
|
||||
<Modal
|
||||
isOpen={onClose ? isOpen : false}
|
||||
title={`Permissions: ${apiKey?.name || ""}`}
|
||||
title={t("permissionsTitle", { name: apiKey?.name || "" })}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
@@ -788,7 +783,7 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
}`}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[18px]">lock_open</span>
|
||||
Allow All
|
||||
{t("allowAll")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleRestrictMode}
|
||||
@@ -799,7 +794,7 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
}`}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[18px]">lock</span>
|
||||
Restrict
|
||||
{t("restrict")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -823,9 +818,7 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
allowAll ? "text-green-700 dark:text-green-300" : "text-amber-700 dark:text-amber-300"
|
||||
}`}
|
||||
>
|
||||
{allowAll
|
||||
? "This key can access all available models."
|
||||
: `This key can access ${selectedCount} of ${totalModels} models.`}
|
||||
{allowAll ? t("allowAllDesc") : t("restrictDesc", { selectedCount, totalModels })}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -833,19 +826,21 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
{!allowAll && selectedCount > 0 && (
|
||||
<div className="flex flex-col gap-1.5 p-2 bg-primary/5 rounded-lg border border-primary/20">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-primary">{selectedCount} selected</span>
|
||||
<span className="text-xs font-medium text-primary">
|
||||
{t("selectedCount", { count: selectedCount })}
|
||||
</span>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
onClick={handleSelectAllModels}
|
||||
className="text-[10px] text-primary hover:bg-primary/10 px-1.5 py-0.5 rounded transition-colors"
|
||||
>
|
||||
All
|
||||
{tc("all")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDeselectAllModels}
|
||||
className="text-[10px] text-red-500 hover:bg-red-500/10 px-1.5 py-0.5 rounded transition-colors"
|
||||
>
|
||||
Clear
|
||||
{t("clear")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -877,7 +872,7 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
<Input
|
||||
value={searchModel}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
placeholder="Search models by name or provider..."
|
||||
placeholder={t("searchModels")}
|
||||
icon="search"
|
||||
/>
|
||||
{searchModel && (
|
||||
@@ -894,7 +889,7 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
{modelsByProvider.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-6 text-text-muted">
|
||||
<span className="material-symbols-outlined text-2xl mb-1">search_off</span>
|
||||
<p className="text-xs">No models found</p>
|
||||
<p className="text-xs">{t("noModelsFound")}</p>
|
||||
</div>
|
||||
) : (
|
||||
modelsByProvider.map(([provider, models]) => {
|
||||
@@ -995,10 +990,10 @@ const PermissionsModal = memo(function PermissionsModal({
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2">
|
||||
<Button onClick={handleSave} fullWidth>
|
||||
Save Permissions
|
||||
{t("savePermissions")}
|
||||
</Button>
|
||||
<Button onClick={onClose} variant="ghost" fullWidth>
|
||||
Cancel
|
||||
{tc("cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -152,8 +152,8 @@
|
||||
"key": "Key",
|
||||
"revokeKey": "Revoke Key",
|
||||
"revokeConfirm": "Are you sure you want to revoke this API key?",
|
||||
"noKeys": "No API keys created yet",
|
||||
"noKeysDesc": "Create an API key to authenticate with OmniRoute",
|
||||
"noKeys": "No API keys yet",
|
||||
"noKeysDesc": "Create your first API key to authenticate requests to your endpoint",
|
||||
"keyLabel": "Key Label",
|
||||
"permissions": "Permissions",
|
||||
"expiresAt": "Expires",
|
||||
@@ -162,10 +162,59 @@
|
||||
"showKey": "Show Key",
|
||||
"hideKey": "Hide Key",
|
||||
"copyKey": "Copy API Key",
|
||||
"allModels": "All Models",
|
||||
"allModels": "All models",
|
||||
"selectedModels": "Selected Models",
|
||||
"readOnly": "Read Only",
|
||||
"fullAccess": "Full Access"
|
||||
"fullAccess": "Full Access",
|
||||
"keyManagement": "API Key Management",
|
||||
"keyManagementDesc": "Create and manage API keys for authenticating requests to your endpoint",
|
||||
"totalKeys": "Total Keys",
|
||||
"restricted": "Restricted",
|
||||
"totalRequests": "Total Requests",
|
||||
"modelsAvailable": "Models Available",
|
||||
"registeredKeys": "Registered Keys",
|
||||
"keysRegistered": "{count} keys registered",
|
||||
"keyRegistered": "{count} key registered",
|
||||
"keysSecurityNote": "Each key isolates usage tracking and can be revoked independently. Keys are masked after creation for security.",
|
||||
"createFirstKey": "Create Your First Key",
|
||||
"name": "Name",
|
||||
"usage": "Usage",
|
||||
"created": "Created",
|
||||
"actions": "Actions",
|
||||
"reqs": "reqs",
|
||||
"neverUsed": "Never used",
|
||||
"deleteConfirm": "Delete this API key?",
|
||||
"usageTips": "Usage Tips",
|
||||
"tipAuth": "Use API keys in the Authorization header as Bearer YOUR_KEY",
|
||||
"tipSecure": "Keys are only shown once during creation \u2014 store them securely",
|
||||
"tipSeparate": "Create separate keys for different clients or environments",
|
||||
"tipRestrict": "Restrict keys to specific models for better security and cost control",
|
||||
"keyName": "Key Name",
|
||||
"keyNamePlaceholder": "e.g., Production Key, Development Key",
|
||||
"keyNameDesc": "Choose a descriptive name to identify this key's purpose",
|
||||
"keyCreated": "API Key Created",
|
||||
"keyCreatedSuccess": "Key created successfully!",
|
||||
"keyCreatedNote": "Copy and store this key now \u2014 it won't be shown again.",
|
||||
"done": "Done",
|
||||
"savePermissions": "Save Permissions",
|
||||
"allowAll": "Allow All",
|
||||
"restrict": "Restrict",
|
||||
"allowAllInfo": "This key can access all available models.",
|
||||
"restrictInfo": "This key can access {selected} of {total} models.",
|
||||
"selected": "{count} selected",
|
||||
"all": "All",
|
||||
"clear": "Clear",
|
||||
"searchModels": "Search models by name or provider...",
|
||||
"noModelsFound": "No models found",
|
||||
"keyNameRequired": "Key name is required",
|
||||
"keyNameTooLong": "Key name must be {max} characters or less",
|
||||
"keyNameInvalid": "Key name can only contain letters, numbers, spaces, hyphens, and underscores",
|
||||
"model": "{count} model",
|
||||
"models": "{count} models",
|
||||
"permissionsTitle": "Permissions: {name}",
|
||||
"allowAllDesc": "This key can access all available models.",
|
||||
"restrictDesc": "This key can access {selectedCount} of {totalModels} models.",
|
||||
"selectedCount": "{count} selected"
|
||||
},
|
||||
"auditLog": {
|
||||
"title": "Audit Log",
|
||||
|
||||
@@ -152,8 +152,8 @@
|
||||
"key": "Chave",
|
||||
"revokeKey": "Revogar Chave",
|
||||
"revokeConfirm": "Tem certeza que deseja revogar esta chave de API?",
|
||||
"noKeys": "Nenhuma chave de API criada",
|
||||
"noKeysDesc": "Crie uma chave de API para autenticar com o OmniRoute",
|
||||
"noKeys": "Nenhuma chave de API ainda",
|
||||
"noKeysDesc": "Crie sua primeira chave de API para autenticar requisições ao seu endpoint",
|
||||
"keyLabel": "Rótulo da Chave",
|
||||
"permissions": "Permissões",
|
||||
"expiresAt": "Expira",
|
||||
@@ -162,10 +162,59 @@
|
||||
"showKey": "Mostrar Chave",
|
||||
"hideKey": "Ocultar Chave",
|
||||
"copyKey": "Copiar Chave de API",
|
||||
"allModels": "Todos os Modelos",
|
||||
"allModels": "Todos os modelos",
|
||||
"selectedModels": "Modelos Selecionados",
|
||||
"readOnly": "Somente Leitura",
|
||||
"fullAccess": "Acesso Total"
|
||||
"fullAccess": "Acesso Total",
|
||||
"keyManagement": "Gerenciamento de Chaves de API",
|
||||
"keyManagementDesc": "Crie e gerencie chaves de API para autenticar requisições ao seu endpoint",
|
||||
"totalKeys": "Total de Chaves",
|
||||
"restricted": "Restrita",
|
||||
"totalRequests": "Total de Requisições",
|
||||
"modelsAvailable": "Modelos Disponíveis",
|
||||
"registeredKeys": "Chaves Registradas",
|
||||
"keysRegistered": "{count} chaves registradas",
|
||||
"keyRegistered": "{count} chave registrada",
|
||||
"keysSecurityNote": "Cada chave isola o rastreamento de uso e pode ser revogada independentemente. As chaves são mascaradas após a criação por segurança.",
|
||||
"createFirstKey": "Crie Sua Primeira Chave",
|
||||
"name": "Nome",
|
||||
"usage": "Uso",
|
||||
"created": "Criado",
|
||||
"actions": "Ações",
|
||||
"reqs": "reqs",
|
||||
"neverUsed": "Nunca usada",
|
||||
"deleteConfirm": "Excluir esta chave de API?",
|
||||
"usageTips": "Dicas de Uso",
|
||||
"tipAuth": "Use chaves de API no cabeçalho Authorization como Bearer SUA_CHAVE",
|
||||
"tipSecure": "As chaves são mostradas apenas uma vez durante a criação \u2014 armazene-as com segurança",
|
||||
"tipSeparate": "Crie chaves separadas para diferentes clientes ou ambientes",
|
||||
"tipRestrict": "Restrinja chaves a modelos específicos para maior segurança e controle de custos",
|
||||
"keyName": "Nome da Chave",
|
||||
"keyNamePlaceholder": "ex: Chave de Produção, Chave de Desenvolvimento",
|
||||
"keyNameDesc": "Escolha um nome descritivo para identificar o propósito desta chave",
|
||||
"keyCreated": "Chave de API Criada",
|
||||
"keyCreatedSuccess": "Chave criada com sucesso!",
|
||||
"keyCreatedNote": "Copie e armazene esta chave agora \u2014 ela não será mostrada novamente.",
|
||||
"done": "Pronto",
|
||||
"savePermissions": "Salvar Permissões",
|
||||
"allowAll": "Permitir Tudo",
|
||||
"restrict": "Restringir",
|
||||
"allowAllInfo": "Esta chave pode acessar todos os modelos disponíveis.",
|
||||
"restrictInfo": "Esta chave pode acessar {selected} de {total} modelos.",
|
||||
"selected": "{count} selecionados",
|
||||
"all": "Todos",
|
||||
"clear": "Limpar",
|
||||
"searchModels": "Buscar modelos por nome ou provedor...",
|
||||
"noModelsFound": "Nenhum modelo encontrado",
|
||||
"keyNameRequired": "Nome da chave é obrigatório",
|
||||
"keyNameTooLong": "Nome da chave deve ter {max} caracteres ou menos",
|
||||
"keyNameInvalid": "Nome da chave pode conter apenas letras, números, espaços, hífens e sublinhados",
|
||||
"model": "{count} modelo",
|
||||
"models": "{count} modelos",
|
||||
"permissionsTitle": "Permissões: {name}",
|
||||
"allowAllDesc": "Esta chave pode acessar todos os modelos disponíveis.",
|
||||
"restrictDesc": "Esta chave pode acessar {selectedCount} de {totalModels} modelos.",
|
||||
"selectedCount": "{count} selecionados"
|
||||
},
|
||||
"auditLog": {
|
||||
"title": "Log de Auditoria",
|
||||
|
||||
Reference in New Issue
Block a user