mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-07-22 12:22:20 +00:00
fix: restore analytics branch test and admin flows
This commit is contained in:
@@ -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)],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -451,7 +451,6 @@ export default function ProvidersPage() {
|
||||
data: { api_key: newKey },
|
||||
});
|
||||
}}
|
||||
availableMints={availableMints}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user