fix: restore analytics branch test and admin flows

This commit is contained in:
Evan Yang
2026-03-13 16:39:44 +08:00
parent 09e7f1f0bf
commit 8a89a38864
6 changed files with 82 additions and 7 deletions

View File

@@ -822,6 +822,10 @@ class TopupRequest(BaseModel):
amount: int
class CashuTopupRequest(BaseModel):
cashu_token: str
@admin_router.post(
"/api/upstream-providers/{provider_id}/topup",
dependencies=[Depends(require_admin_api)],
@@ -885,6 +889,61 @@ async def initiate_provider_topup(
raise HTTPException(status_code=500, detail=str(e))
@admin_router.post(
"/api/upstream-providers/{provider_id}/topup/token",
dependencies=[Depends(require_admin_api)],
)
async def topup_provider_with_token(
provider_id: int, payload: CashuTopupRequest
) -> dict[str, object]:
"""Top up the upstream provider account with a Cashu token."""
from ..upstream.helpers import _instantiate_provider
async with create_session() as session:
provider = await session.get(UpstreamProviderRow, provider_id)
if not provider:
raise HTTPException(status_code=404, detail="Provider not found")
upstream_instance = _instantiate_provider(provider)
if not upstream_instance:
raise HTTPException(
status_code=400, detail="Could not instantiate provider"
)
cashu_token = payload.cashu_token.strip()
if not cashu_token:
raise HTTPException(status_code=400, detail="Cashu token is required")
if not hasattr(upstream_instance, "topup"):
raise HTTPException(
status_code=400, detail="Provider does not support token top-up"
)
try:
topup_data = await upstream_instance.topup(cashu_token)
if isinstance(topup_data, dict) and topup_data.get("error"):
raise HTTPException(status_code=400, detail=str(topup_data["error"]))
return {
"ok": True,
"topup_data": topup_data if isinstance(topup_data, dict) else {},
"message": "Token redeemed successfully",
}
except NotImplementedError as e:
raise HTTPException(
status_code=400,
detail=f"Provider does not support token top-up: {str(e)}",
)
except HTTPException:
raise
except Exception as e:
logger.error(
f"Failed to top up provider {provider_id} with token: {e}",
extra={"error_type": type(e).__name__},
)
raise HTTPException(status_code=500, detail=str(e))
@admin_router.get(
"/api/upstream-providers/{provider_id}/topup/{invoice_id}/status",
dependencies=[Depends(require_admin_api)],

View File

@@ -264,12 +264,12 @@ async def test_models_endpoint_accept_headers(integration_client: AsyncClient) -
async def test_admin_endpoint_unauthenticated(
integration_client: AsyncClient, db_snapshot: Any
) -> None:
"""Test GET /admin/ endpoint redirects to /"""
"""Test admin API requires authentication."""
await db_snapshot.capture()
response = await integration_client.get("/admin/api/settings")
assert response.status_code == 403
assert response.status_code == 401
diff = await db_snapshot.diff()
assert len(diff["api_keys"]["added"]) == 0

View File

@@ -451,7 +451,6 @@ export default function ProvidersPage() {
data: { api_key: newKey },
});
}}
availableMints={availableMints}
/>
))}
</div>

View File

@@ -50,7 +50,6 @@ interface ProviderCardProps {
onDeleteModel: (modelId: string) => void;
onOverrideModel: (model: AdminModel) => void;
onUpdateApiKey: (newKey: string) => void;
availableMints: string[];
}
export function ProviderCard({
@@ -70,7 +69,6 @@ export function ProviderCard({
onDeleteModel,
onOverrideModel,
onUpdateApiKey,
availableMints,
}: ProviderCardProps) {
const [isKeyModalOpen, setIsKeyModalOpen] = useState(false);
const hasDetails = Boolean(provider.api_version) || isExpanded;

View File

@@ -1,6 +1,6 @@
'use client';
import { useCallback, useState } from 'react';
import { useState } from 'react';
import Image from 'next/image';
import { Copy, Loader2, Zap, KeyRound } from 'lucide-react';
import { toast } from 'sonner';
@@ -10,7 +10,6 @@ import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { Separator } from '@/components/ui/separator';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
interface RoutstrCreateKeySectionProps {

View File

@@ -20,6 +20,7 @@ export const UpstreamProviderSchema = z.object({
api_version: z.string().nullable().optional(),
enabled: z.boolean(),
provider_fee: z.number().optional(),
provider_settings: z.record(z.string(), z.unknown()).optional(),
});
export const CreateUpstreamProviderSchema = z.object({
@@ -29,6 +30,7 @@ export const CreateUpstreamProviderSchema = z.object({
api_version: z.string().nullable().optional(),
enabled: z.boolean().default(true),
provider_fee: z.number().optional(),
provider_settings: z.record(z.string(), z.unknown()).optional(),
});
export const UpdateUpstreamProviderSchema = z.object({
@@ -38,6 +40,7 @@ export const UpdateUpstreamProviderSchema = z.object({
api_version: z.string().nullable().optional(),
enabled: z.boolean().optional(),
provider_fee: z.number().optional(),
provider_settings: z.record(z.string(), z.unknown()).optional(),
});
export const AdminModelPricingSchema = z.object({
@@ -914,6 +917,23 @@ export class AdminService {
});
}
static async topupProviderWithToken(
providerId: number,
cashuToken: string
): Promise<{
ok: boolean;
topup_data: Record<string, unknown>;
message: string;
}> {
return await apiClient.post<{
ok: boolean;
topup_data: Record<string, unknown>;
message: string;
}>(`/admin/api/upstream-providers/${providerId}/topup/token`, {
cashu_token: cashuToken,
});
}
static async checkTopupStatus(
providerId: number,
invoiceId: string