Skip to content

Commit eb2d6e5

Browse files
authored
fix: control-panel search (#334)
1 parent 9db3ccc commit eb2d6e5

File tree

6 files changed

+196
-59
lines changed

6 files changed

+196
-59
lines changed

infrastructure/control-panel/src/lib/services/evaultService.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,41 @@ export class EVaultService {
7373
throw error;
7474
}
7575
}
76+
77+
/**
78+
* Get logs for a specific eVault pod
79+
*/
80+
/**
81+
* Get logs for a specific eVault pod
82+
*/
83+
static async getEVaultLogs(namespace: string, podName: string): Promise<string[]> {
84+
try {
85+
const response = await fetch(`/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/logs`);
86+
if (!response.ok) {
87+
throw new Error(`HTTP error! status: ${response.status}`);
88+
}
89+
const data = await response.json();
90+
return data.logs || [];
91+
} catch (error) {
92+
console.error('Failed to fetch eVault logs:', error);
93+
throw error;
94+
}
95+
}
96+
97+
/**
98+
* Get metrics for a specific eVault pod
99+
*/
100+
static async getEVaultMetrics(namespace: string, podName: string): Promise<any> {
101+
try {
102+
const response = await fetch(`/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/metrics`);
103+
if (!response.ok) {
104+
throw new Error(`HTTP error! status: ${response.status}`);
105+
}
106+
const data = await response.json();
107+
return data.metrics || {};
108+
} catch (error) {
109+
console.error('Failed to fetch eVault metrics:', error);
110+
throw error;
111+
}
112+
}
76113
}

infrastructure/control-panel/src/routes/+page.svelte

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
let totalPages = $state(1);
2626
2727
// Track selected items
28-
let selectedEVaults = $state<number[]>([]);
29-
let selectedPlatforms = $state<number[]>([]);
28+
// Selection state - store unique identifiers instead of indices
29+
let selectedEVaults = $state<string[]>([]);
30+
let selectedPlatforms = $state<string[]>([]);
3031
3132
// Filtered data for search
3233
let filteredEVaults = $derived(() => {
@@ -129,49 +130,53 @@
129130
130131
// Handle eVault selection changes
131132
function handleEVaultSelectionChange(index: number, checked: boolean) {
132-
// Get the filtered eVaults to work with the current search results
133-
const filtered = filteredEVaults();
134-
135-
// Convert page-relative index to filtered array index
136-
const filteredIndex = (currentPage - 1) * itemsPerPage + index;
133+
// Get the paginated eVaults to work with the current page
134+
const paginated = paginatedEVaults();
137135
138-
// Get the actual eVault from the filtered results
139-
const selectedEVault = filtered[filteredIndex];
136+
// Get the actual eVault from the paginated results
137+
const selectedEVault = paginated[index];
140138
141139
if (!selectedEVault) {
142-
console.error('Selected eVault not found in filtered results');
143-
return;
144-
}
145-
146-
// Find the index of this eVault in the original evaults array
147-
const originalIndex = evaults.findIndex((e) => e.evaultId === selectedEVault.evaultId);
148-
149-
if (originalIndex === -1) {
150-
console.error('Selected eVault not found in original evaults array');
140+
console.error('Selected eVault not found in paginated results');
151141
return;
152142
}
153143
154144
if (checked) {
155-
selectedEVaults = [...selectedEVaults, originalIndex];
145+
selectedEVaults = [...selectedEVaults, selectedEVault.evaultId];
156146
} else {
157-
selectedEVaults = selectedEVaults.filter((i) => i !== originalIndex);
147+
selectedEVaults = selectedEVaults.filter((id) => id !== selectedEVault.evaultId);
158148
}
159149
160150
// Store selections immediately in sessionStorage
161-
const selectedEVaultData = selectedEVaults.map((i) => evaults[i]);
151+
const selectedEVaultData = selectedEVaults
152+
.map((id) => evaults.find((e) => e.evaultId === id))
153+
.filter(Boolean);
162154
sessionStorage.setItem('selectedEVaults', JSON.stringify(selectedEVaultData));
163155
}
164156
165157
// Handle platform selection changes
166158
function handlePlatformSelectionChange(index: number, checked: boolean) {
159+
// Get the filtered platforms to work with the current search results
160+
const filtered = filteredPlatforms();
161+
162+
// Get the actual platform from the filtered results
163+
const selectedPlatform = filtered[index];
164+
165+
if (!selectedPlatform) {
166+
console.error('Selected platform not found in filtered results');
167+
return;
168+
}
169+
167170
if (checked) {
168-
selectedPlatforms = [...selectedPlatforms, index];
171+
selectedPlatforms = [...selectedPlatforms, selectedPlatform.name];
169172
} else {
170-
selectedPlatforms = selectedPlatforms.filter((i) => i !== index);
173+
selectedPlatforms = selectedPlatforms.filter((name) => name !== selectedPlatform.name);
171174
}
172175
173176
// Store selections immediately in sessionStorage
174-
const selectedPlatformData = selectedPlatforms.map((i) => platforms[i]);
177+
const selectedPlatformData = selectedPlatforms
178+
.map((name) => platforms.find((p) => p.name === name))
179+
.filter(Boolean);
175180
sessionStorage.setItem('selectedPlatforms', JSON.stringify(selectedPlatformData));
176181
}
177182
@@ -184,14 +189,8 @@
184189
console.log('filtered eVaults length:', filtered.length);
185190
186191
if (checked) {
187-
// Select all filtered eVaults by finding their indices in the original array
188-
const filteredIndices = filtered
189-
.map((filteredEVault) => {
190-
return evaults.findIndex((e) => e.evaultId === filteredEVault.evaultId);
191-
})
192-
.filter((index) => index !== -1);
193-
194-
selectedEVaults = filteredIndices;
192+
// Select all filtered eVaults by their evaultId
193+
selectedEVaults = filtered.map((evault) => evault.evaultId);
195194
console.log('✅ Selected all filtered eVaults, selectedEVaults:', selectedEVaults);
196195
} else {
197196
// Deselect all eVaults
@@ -200,28 +199,38 @@
200199
}
201200
202201
// Store selections immediately in sessionStorage
203-
const selectedEVaultData = selectedEVaults.map((i) => evaults[i]);
202+
const selectedEVaultData = selectedEVaults
203+
.map((id) => evaults.find((e) => e.evaultId === id))
204+
.filter(Boolean);
204205
sessionStorage.setItem('selectedEVaults', JSON.stringify(selectedEVaultData));
205206
console.log('💾 Stored in sessionStorage:', selectedEVaultData);
206207
}
207208
208209
// Handle select all platforms
209210
function handleSelectAllPlatforms(checked: boolean) {
210211
console.log('🎯 handleSelectAllPlatforms called with:', checked);
211-
console.log('platforms.length:', platforms.length);
212+
213+
// Get the filtered platforms to work with the current search results
214+
const filtered = filteredPlatforms();
215+
console.log('filtered platforms length:', filtered.length);
212216
213217
if (checked) {
214-
// Select all platforms
215-
selectedPlatforms = Array.from({ length: platforms.length }, (_, i) => i);
216-
console.log('✅ Selected all platforms, selectedPlatforms:', selectedPlatforms);
218+
// Select all filtered platforms by their name
219+
selectedPlatforms = filtered.map((platform) => platform.name);
220+
console.log(
221+
'✅ Selected all filtered platforms, selectedPlatforms:',
222+
selectedPlatforms
223+
);
217224
} else {
218225
// Deselect all platforms
219226
selectedPlatforms = [];
220227
console.log('❌ Deselected all platforms, selectedPlatforms:', selectedPlatforms);
221228
}
222229
223230
// Store selections immediately in sessionStorage
224-
const selectedPlatformData = selectedPlatforms.map((i) => platforms[i]);
231+
const selectedPlatformData = selectedPlatforms
232+
.map((name) => platforms.find((p) => p.name === name))
233+
.filter(Boolean);
225234
sessionStorage.setItem('selectedPlatforms', JSON.stringify(selectedPlatformData));
226235
console.log('💾 Stored in sessionStorage:', selectedPlatformData);
227236
}
@@ -240,8 +249,12 @@
240249
241250
// Navigate to monitoring with selected items
242251
function goToMonitoring() {
243-
const selectedEVaultData = selectedEVaults.map((i) => evaults[i]);
244-
const selectedPlatformData = selectedPlatforms.map((i) => platforms[i]);
252+
const selectedEVaultData = selectedEVaults
253+
.map((id) => evaults.find((e) => e.evaultId === id))
254+
.filter(Boolean);
255+
const selectedPlatformData = selectedPlatforms
256+
.map((name) => platforms.find((p) => p.name === name))
257+
.filter(Boolean);
245258
246259
// Store selected data in sessionStorage to pass to monitoring page
247260
sessionStorage.setItem('selectedEVaults', JSON.stringify(selectedEVaultData));
@@ -333,7 +346,8 @@
333346
let currentSelectedEVaultIndex = $state(-1);
334347
335348
function handleEVaultRowClick(index: number) {
336-
const evault = evaults[index];
349+
const paginated = paginatedEVaults();
350+
const evault = paginated[index];
337351
if (evault) {
338352
goto(`/monitoring/${evault.namespace}/${evault.name}`);
339353
}
@@ -388,15 +402,10 @@
388402
handleSelectedRow={handleEVaultRowClick}
389403
onSelectionChange={handleEVaultSelectionChange}
390404
onSelectAllChange={handleSelectAllEVaults}
391-
selectedIndices={selectedEVaults
392-
.map((globalIndex) => {
393-
const pageStart = (currentPage - 1) * itemsPerPage;
394-
const pageEnd = pageStart + itemsPerPage;
395-
if (globalIndex >= pageStart && globalIndex < pageEnd) {
396-
return globalIndex - pageStart;
397-
}
398-
return -1; // Not on current page
399-
})
405+
selectedIndices={paginatedEVaults()
406+
.map((evault, index) =>
407+
selectedEVaults.includes(evault.evaultId) ? index : -1
408+
)
400409
.filter((index) => index !== -1)}
401410
/>
402411

@@ -469,7 +478,11 @@
469478
withPagination={false}
470479
onSelectionChange={handlePlatformSelectionChange}
471480
onSelectAllChange={handleSelectAllPlatforms}
472-
selectedIndices={selectedPlatforms}
481+
selectedIndices={filteredPlatforms()
482+
.map((platform, index) =>
483+
selectedPlatforms.includes(platform.name) ? index : -1
484+
)
485+
.filter((index) => index !== -1)}
473486
/>
474487
{/if}
475488
</TableCard>

infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/logs/+server.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,33 @@ export const GET: RequestHandler = async ({ params, url }) => {
1010
const tail = url.searchParams.get('tail') || '100';
1111

1212
try {
13+
// First check if the namespace exists
14+
try {
15+
await execAsync(`kubectl get namespace ${namespace}`);
16+
} catch (namespaceError: any) {
17+
if (namespaceError.stderr?.includes('not found')) {
18+
return json({
19+
error: `Namespace '${namespace}' not found. The eVault may have been deleted or terminated.`,
20+
logs: []
21+
}, { status: 404 });
22+
}
23+
throw namespaceError;
24+
}
25+
26+
// Then check if the pod exists
27+
try {
28+
await execAsync(`kubectl get pod ${pod} -n ${namespace}`);
29+
} catch (podError: any) {
30+
if (podError.stderr?.includes('not found')) {
31+
return json({
32+
error: `Pod '${pod}' not found in namespace '${namespace}'. The pod may have been deleted or terminated.`,
33+
logs: []
34+
}, { status: 404 });
35+
}
36+
throw podError;
37+
}
38+
39+
// If both exist, fetch the logs
1340
const { stdout } = await execAsync(
1441
`kubectl logs -n ${namespace} ${pod} -c evault --tail=${tail}`
1542
);
@@ -19,8 +46,20 @@ export const GET: RequestHandler = async ({ params, url }) => {
1946
.filter((line) => line.trim());
2047

2148
return json({ logs });
22-
} catch (error) {
49+
} catch (error: any) {
2350
console.error('Error fetching logs:', error);
24-
return json({ error: 'Failed to fetch logs', logs: [] }, { status: 500 });
51+
52+
// Handle specific kubectl errors
53+
if (error.stderr?.includes('not found')) {
54+
return json({
55+
error: 'Resource not found. The eVault or pod may have been deleted.',
56+
logs: []
57+
}, { status: 404 });
58+
}
59+
60+
return json({
61+
error: 'Failed to fetch logs. Please check if the eVault is still running.',
62+
logs: []
63+
}, { status: 500 });
2564
}
2665
};

infrastructure/control-panel/src/routes/monitoring/[namespace]/[service]/+page.svelte

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,54 @@
11
<script lang="ts">
22
import { onMount, onDestroy } from 'svelte';
33
import { EVaultService } from '$lib/services/evaultService';
4+
5+
// Debug: Check if methods exist
6+
console.log('🔍 EVaultService imported:', EVaultService);
7+
console.log('🔍 getEVaultLogs exists:', typeof EVaultService.getEVaultLogs);
8+
console.log('🔍 getEVaultMetrics exists:', typeof EVaultService.getEVaultMetrics);
9+
10+
// Temporary workaround: Add methods directly if they don't exist
11+
if (!EVaultService.getEVaultLogs) {
12+
console.log('⚠️ Adding getEVaultLogs method directly');
13+
EVaultService.getEVaultLogs = async (namespace: string, podName: string) => {
14+
console.log('🔍 Direct getEVaultLogs called with:', { namespace, podName });
15+
try {
16+
const response = await fetch(
17+
`/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/logs`
18+
);
19+
if (!response.ok) {
20+
throw new Error(`HTTP error! status: ${response.status}`);
21+
}
22+
const responseData = await response.json();
23+
console.log('✅ Direct logs fetched successfully:', responseData);
24+
return responseData.logs || [];
25+
} catch (error) {
26+
console.error('❌ Direct logs fetch failed:', error);
27+
throw error;
28+
}
29+
};
30+
}
31+
32+
if (!EVaultService.getEVaultMetrics) {
33+
console.log('⚠️ Adding getEVaultMetrics method directly');
34+
EVaultService.getEVaultMetrics = async (namespace: string, podName: string) => {
35+
console.log('🔍 Direct getEVaultMetrics called with:', { namespace, podName });
36+
try {
37+
const response = await fetch(
38+
`/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/metrics`
39+
);
40+
if (!response.ok) {
41+
throw new Error(`HTTP error! status: ${response.status}`);
42+
}
43+
const responseData = await response.json();
44+
console.log('✅ Direct metrics fetched successfully:', responseData);
45+
return responseData.metrics || {};
46+
} catch (error) {
47+
console.error('❌ Direct metrics fetch failed:', error);
48+
throw error;
49+
}
50+
};
51+
}
452
import { ButtonAction } from '$lib/ui';
553
import { RefreshCw, Clock, Activity, Server, Globe, ArrowLeft } from 'lucide-svelte';
654
import { goto } from '$app/navigation';

infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@
388388
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
389389
CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements";
390390
CODE_SIGN_IDENTITY = "iPhone Developer";
391-
CURRENT_PROJECT_VERSION = 0.2.0.2;
391+
CURRENT_PROJECT_VERSION = 0.2.1.0;
392392
DEVELOPMENT_TEAM = M49C8XS835;
393393
ENABLE_BITCODE = NO;
394394
"EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64;
@@ -415,7 +415,7 @@
415415
"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)",
416416
"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
417417
);
418-
MARKETING_VERSION = 0.2.0;
418+
MARKETING_VERSION = 0.2.1;
419419
PRODUCT_BUNDLE_IDENTIFIER = "foundation.metastate.eid-wallet";
420420
PRODUCT_NAME = "eID for W3DS";
421421
SDKROOT = iphoneos;
@@ -436,7 +436,7 @@
436436
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
437437
CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements";
438438
CODE_SIGN_IDENTITY = "iPhone Developer";
439-
CURRENT_PROJECT_VERSION = 0.2.0.2;
439+
CURRENT_PROJECT_VERSION = 0.2.1.0;
440440
DEVELOPMENT_TEAM = M49C8XS835;
441441
ENABLE_BITCODE = NO;
442442
"EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64;
@@ -463,7 +463,7 @@
463463
"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)",
464464
"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
465465
);
466-
MARKETING_VERSION = 0.2.0;
466+
MARKETING_VERSION = 0.2.1;
467467
PRODUCT_BUNDLE_IDENTIFIER = "foundation.metastate.eid-wallet";
468468
PRODUCT_NAME = "eID for W3DS";
469469
SDKROOT = iphoneos;

0 commit comments

Comments
 (0)