diff --git a/src/router/pages/AssetsIsoPage.tsx b/src/router/pages/AssetsIsoPage.tsx index 63b39f8..cdcb7d1 100644 --- a/src/router/pages/AssetsIsoPage.tsx +++ b/src/router/pages/AssetsIsoPage.tsx @@ -11,19 +11,17 @@ import { BookOpen, ExternalLink, CheckCircle, - XCircle, Usb, ArrowLeft, CloudLightning, } from 'lucide-react'; import { useAssetList, useDownloadAsset, useAssetStatus } from '../../services/api/hooks/useAssets'; import { assetsApi } from '../../services/api/assets'; -import type { AssetType } from '../../services/api/types/asset'; export function AssetsIsoPage() { const { data, isLoading, error } = useAssetList(); const downloadAsset = useDownloadAsset(); - const [selectedSchematicId, setSelectedSchematicId] = useState(null); + const [selectedSchematicId] = useState(null); const [selectedVersion, setSelectedVersion] = useState('v1.8.0'); const { data: statusData } = useAssetStatus(selectedSchematicId); diff --git a/src/router/pages/IsoPage.tsx b/src/router/pages/IsoPage.tsx index bace905..a56166f 100644 --- a/src/router/pages/IsoPage.tsx +++ b/src/router/pages/IsoPage.tsx @@ -15,22 +15,20 @@ import { } from 'lucide-react'; import { useAssetList, useDownloadAsset, useDeleteAsset } from '../../services/api/hooks/useAssets'; import { assetsApi } from '../../services/api/assets'; -import type { Platform } from '../../services/api/types/asset'; +import type { Platform, Asset } from '../../services/api/types/asset'; -// Helper function to extract version from ISO filename -// Filename format: talos-v1.11.2-metal-amd64.iso -function extractVersionFromPath(path: string): string { +// Helper function to extract platform from filename +// Filename format: metal-amd64.iso +function extractPlatformFromPath(path: string): string { const filename = path.split('/').pop() || ''; - const match = filename.match(/talos-(v\d+\.\d+\.\d+)-metal/); + const match = filename.match(/-(amd64|arm64)\./); return match ? match[1] : 'unknown'; } -// Helper function to extract platform from ISO filename -// Filename format: talos-v1.11.2-metal-amd64.iso -function extractPlatformFromPath(path: string): string { - const filename = path.split('/').pop() || ''; - const match = filename.match(/-(amd64|arm64)\.iso$/); - return match ? match[1] : 'unknown'; +// Type for ISO asset with schematic and version info +interface IsoAssetWithMetadata extends Asset { + schematic_id: string; + version: string; } export function IsoPage() { @@ -38,8 +36,8 @@ export function IsoPage() { const downloadAsset = useDownloadAsset(); const deleteAsset = useDeleteAsset(); - const [schematicId, setSchematicId] = useState(''); - const [selectedVersion, setSelectedVersion] = useState('v1.11.2'); + const [schematicId, setSchematicId] = useState('434a0300db532066f1098e05ac068159371d00f0aba0a3103a0e826e83825c82'); + const [selectedVersion, setSelectedVersion] = useState('v1.11.5'); const [selectedPlatform, setSelectedPlatform] = useState('amd64'); const [isDownloading, setIsDownloading] = useState(false); @@ -53,10 +51,10 @@ export function IsoPage() { try { await downloadAsset.mutateAsync({ schematicId, + version: selectedVersion, request: { - version: selectedVersion, platform: selectedPlatform, - assets: ['iso'] + asset_types: ['iso'] }, }); // Refresh the list after download @@ -69,13 +67,13 @@ export function IsoPage() { } }; - const handleDelete = async (schematicIdToDelete: string) => { - if (!confirm('Are you sure you want to delete this schematic and all its assets? This action cannot be undone.')) { + const handleDelete = async (schematicIdToDelete: string, versionToDelete: string) => { + if (!confirm(`Are you sure you want to delete ${schematicIdToDelete}@${versionToDelete} and all its assets? This action cannot be undone.`)) { return; } try { - await deleteAsset.mutateAsync(schematicIdToDelete); + await deleteAsset.mutateAsync({ schematicId: schematicIdToDelete, version: versionToDelete }); await refetch(); } catch (err) { console.error('Delete failed:', err); @@ -83,17 +81,16 @@ export function IsoPage() { } }; - // Find all ISO assets from all schematics (including multiple ISOs per schematic) - const isoAssets = data?.schematics - .flatMap(schematic => { - // Get ALL ISO assets for this schematic (not just the first one) - const isoAssetsForSchematic = schematic.assets.filter(asset => asset.type === 'iso'); - return isoAssetsForSchematic.map(isoAsset => ({ - ...isoAsset, - schematic_id: schematic.schematic_id, - version: schematic.version - })); - }) || []; + // Find all ISO assets from all assets (schematic@version combinations) + const isoAssets = data?.assets?.flatMap(asset => { + // Get ALL ISO assets for this schematic@version + const isoAssetsForAsset = asset.assets.filter(a => a.type === 'iso'); + return isoAssetsForAsset.map(isoAsset => ({ + ...isoAsset, + schematic_id: asset.schematic_id, + version: asset.version + })); + }) || []; return (
@@ -146,46 +143,6 @@ export function IsoPage() {
- {/* Schematic ID Input */} -
- - setSchematicId(e.target.value)} - placeholder="e.g., 434a0300db532066f1098e05ac068159371d00f0aba0a3103a0e826e83825c82" - className="w-full px-3 py-2 border rounded-lg bg-background font-mono text-sm" - /> -

- Get your schematic ID from the{' '} - - Talos Image Factory - -

-
- - {/* Version Selection */} -
- - -
- {/* Platform Selection */}
@@ -215,6 +172,49 @@ export function IsoPage() {
+ {/* Version Selection */} +
+ + +
+ + {/* Schematic ID Input */} +
+ + setSchematicId(e.target.value)} + placeholder="e.g., 434a0300db532066f1098e05ac068159371d00f0aba0a3103a0e826e83825c82" + className="w-full px-3 py-2 border rounded-lg bg-background font-mono text-sm" + /> +

+ Get your schematic ID from the{' '} + + Talos Image Factory + +

+
+ {/* Download Button */} diff --git a/src/services/api/assets.ts b/src/services/api/assets.ts index 3bc202f..2b3f8ec 100644 --- a/src/services/api/assets.ts +++ b/src/services/api/assets.ts @@ -1,42 +1,42 @@ import { apiClient } from './client'; -import type { AssetListResponse, Schematic, DownloadAssetRequest, AssetStatusResponse } from './types/asset'; +import type { AssetListResponse, PXEAsset, DownloadAssetRequest, AssetStatusResponse } from './types/asset'; // Get API base URL const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5055'; export const assetsApi = { - // List all schematics + // List all assets (schematic@version combinations) list: async (): Promise => { - const response = await apiClient.get('/api/v1/assets'); + const response = await apiClient.get('/api/v1/pxe/assets'); return response as AssetListResponse; }, - // Get schematic details - get: async (schematicId: string): Promise => { - const response = await apiClient.get(`/api/v1/assets/${schematicId}`); - return response as Schematic; + // Get asset details for specific schematic@version + get: async (schematicId: string, version: string): Promise => { + const response = await apiClient.get(`/api/v1/pxe/assets/${schematicId}/${version}`); + return response as PXEAsset; }, - // Download assets for a schematic - download: async (schematicId: string, request: DownloadAssetRequest): Promise<{ message: string }> => { - const response = await apiClient.post(`/api/v1/assets/${schematicId}/download`, request); + // Download assets for a schematic@version + download: async (schematicId: string, version: string, request: DownloadAssetRequest): Promise<{ message: string }> => { + const response = await apiClient.post(`/api/v1/pxe/assets/${schematicId}/${version}/download`, request); return response as { message: string }; }, // Get download status - status: async (schematicId: string): Promise => { - const response = await apiClient.get(`/api/v1/assets/${schematicId}/status`); + status: async (schematicId: string, version: string): Promise => { + const response = await apiClient.get(`/api/v1/pxe/assets/${schematicId}/${version}/status`); return response as AssetStatusResponse; }, // Get download URL for an asset (includes base URL for direct download) - getAssetUrl: (schematicId: string, assetType: 'kernel' | 'initramfs' | 'iso'): string => { - return `${API_BASE_URL}/api/v1/assets/${schematicId}/pxe/${assetType}`; + getAssetUrl: (schematicId: string, version: string, assetType: 'kernel' | 'initramfs' | 'iso'): string => { + return `${API_BASE_URL}/api/v1/pxe/assets/${schematicId}/${version}/pxe/${assetType}`; }, - // Delete a schematic and all its assets - delete: async (schematicId: string): Promise<{ message: string }> => { - const response = await apiClient.delete(`/api/v1/assets/${schematicId}`); + // Delete an asset (schematic@version) and all its files + delete: async (schematicId: string, version: string): Promise<{ message: string }> => { + const response = await apiClient.delete(`/api/v1/pxe/assets/${schematicId}/${version}`); return response as { message: string }; }, }; diff --git a/src/services/api/hooks/useAssets.ts b/src/services/api/hooks/useAssets.ts index ce16456..9657397 100644 --- a/src/services/api/hooks/useAssets.ts +++ b/src/services/api/hooks/useAssets.ts @@ -9,19 +9,19 @@ export function useAssetList() { }); } -export function useAsset(schematicId: string | null | undefined) { +export function useAsset(schematicId: string | null | undefined, version: string | null | undefined) { return useQuery({ - queryKey: ['assets', schematicId], - queryFn: () => assetsApi.get(schematicId!), - enabled: !!schematicId, + queryKey: ['assets', schematicId, version], + queryFn: () => assetsApi.get(schematicId!, version!), + enabled: !!schematicId && !!version, }); } -export function useAssetStatus(schematicId: string | null | undefined) { +export function useAssetStatus(schematicId: string | null | undefined, version: string | null | undefined) { return useQuery({ - queryKey: ['assets', schematicId, 'status'], - queryFn: () => assetsApi.status(schematicId!), - enabled: !!schematicId, + queryKey: ['assets', schematicId, version, 'status'], + queryFn: () => assetsApi.status(schematicId!, version!), + enabled: !!schematicId && !!version, refetchInterval: (query) => { const data = query.state.data; // Poll every 2 seconds if downloading @@ -34,12 +34,12 @@ export function useDownloadAsset() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: ({ schematicId, request }: { schematicId: string; request: DownloadAssetRequest }) => - assetsApi.download(schematicId, request), + mutationFn: ({ schematicId, version, request }: { schematicId: string; version: string; request: DownloadAssetRequest }) => + assetsApi.download(schematicId, version, request), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['assets'] }); - queryClient.invalidateQueries({ queryKey: ['assets', variables.schematicId] }); - queryClient.invalidateQueries({ queryKey: ['assets', variables.schematicId, 'status'] }); + queryClient.invalidateQueries({ queryKey: ['assets', variables.schematicId, variables.version] }); + queryClient.invalidateQueries({ queryKey: ['assets', variables.schematicId, variables.version, 'status'] }); }, }); } @@ -48,11 +48,12 @@ export function useDeleteAsset() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (schematicId: string) => assetsApi.delete(schematicId), - onSuccess: (_, schematicId) => { + mutationFn: ({ schematicId, version }: { schematicId: string; version: string }) => + assetsApi.delete(schematicId, version), + onSuccess: (_, { schematicId, version }) => { queryClient.invalidateQueries({ queryKey: ['assets'] }); - queryClient.invalidateQueries({ queryKey: ['assets', schematicId] }); - queryClient.invalidateQueries({ queryKey: ['assets', schematicId, 'status'] }); + queryClient.invalidateQueries({ queryKey: ['assets', schematicId, version] }); + queryClient.invalidateQueries({ queryKey: ['assets', schematicId, version, 'status'] }); }, }); } diff --git a/src/services/api/types/asset.ts b/src/services/api/types/asset.ts index ec895ad..4495f65 100644 --- a/src/services/api/types/asset.ts +++ b/src/services/api/types/asset.ts @@ -10,8 +10,8 @@ export interface Asset { downloaded: boolean; } -// Schematic representation matching backend -export interface Schematic { +// PXEAsset represents a schematic@version combination (composite key) +export interface PXEAsset { schematic_id: string; version: string; path: string; @@ -19,13 +19,12 @@ export interface Schematic { } export interface AssetListResponse { - schematics: Schematic[]; + assets: PXEAsset[]; } export interface DownloadAssetRequest { - version: string; platform?: Platform; - assets?: AssetType[]; + asset_types?: string[]; force?: boolean; }