diff --git a/infrastructure/control-panel/src/lib/services/evaultService.ts b/infrastructure/control-panel/src/lib/services/evaultService.ts index e9455f1f..8e0b2961 100644 --- a/infrastructure/control-panel/src/lib/services/evaultService.ts +++ b/infrastructure/control-panel/src/lib/services/evaultService.ts @@ -15,9 +15,15 @@ export class EVaultService { } } - static async getEVaultLogs(namespace: string, podName: string, tail: number = 100): Promise { + static async getEVaultLogs( + namespace: string, + podName: string, + tail: number = 100 + ): Promise { try { - const response = await fetch(`/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/logs?tail=${tail}`); + const response = await fetch( + `/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/logs?tail=${tail}` + ); if (!response.ok) { throw new Error('Failed to fetch logs'); } @@ -31,7 +37,9 @@ export class EVaultService { static async getEVaultDetails(namespace: string, podName: string): Promise { try { - const response = await fetch(`/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/details`); + const response = await fetch( + `/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/details` + ); if (!response.ok) { throw new Error('Failed to fetch eVault details'); } @@ -44,7 +52,9 @@ export class EVaultService { static async getEVaultMetrics(namespace: string, podName: string): Promise { try { - const response = await fetch(`/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/metrics`); + const response = await fetch( + `/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/metrics` + ); if (!response.ok) { throw new Error('Failed to fetch metrics'); } @@ -54,4 +64,4 @@ export class EVaultService { return null; } } -} \ No newline at end of file +} diff --git a/infrastructure/control-panel/src/routes/api/evaults/+server.ts b/infrastructure/control-panel/src/routes/api/evaults/+server.ts index 5e058744..126983e8 100644 --- a/infrastructure/control-panel/src/routes/api/evaults/+server.ts +++ b/infrastructure/control-panel/src/routes/api/evaults/+server.ts @@ -26,54 +26,60 @@ export const GET: RequestHandler = async () => { // Get minikube IP for NodePort services let minikubeIP = 'localhost'; try { - const { stdout: minikubeIPOutput } = await execAsync('minikube ip 2>/dev/null || echo "localhost"'); + const { stdout: minikubeIPOutput } = await execAsync( + 'minikube ip 2>/dev/null || echo "localhost"' + ); if (minikubeIPOutput.trim()) { minikubeIP = minikubeIPOutput.trim(); } } catch (ipError) { console.log('Could not get minikube IP, using localhost'); } - + console.log('Using IP:', minikubeIP); - + // Get all namespaces const { stdout: namespacesOutput } = await execAsync('kubectl get namespaces -o json'); const namespaces = JSON.parse(namespacesOutput); - + // Filter for eVault namespaces const evaultNamespaces = namespaces.items .filter((ns: any) => ns.metadata.name.startsWith('evault-')) .map((ns: any) => ns.metadata.name); - + console.log('Found eVault namespaces:', evaultNamespaces); - + let allEVaults: EVault[] = []; // Get services and pods from each eVault namespace for (const namespace of evaultNamespaces) { try { // Get services in this namespace as JSON - const { stdout: servicesOutput } = await execAsync(`kubectl get services -n ${namespace} -o json`); + const { stdout: servicesOutput } = await execAsync( + `kubectl get services -n ${namespace} -o json` + ); const services = JSON.parse(servicesOutput); - + // Get pods in this namespace as JSON - const { stdout: podsOutput } = await execAsync(`kubectl get pods -n ${namespace} -o json`); + const { stdout: podsOutput } = await execAsync( + `kubectl get pods -n ${namespace} -o json` + ); const pods = JSON.parse(podsOutput); - + console.log(`=== SERVICES FOR ${namespace} ===`); console.log(JSON.stringify(services, null, 2)); console.log(`=== PODS FOR ${namespace} ===`); console.log(JSON.stringify(pods, null, 2)); console.log(`=== END DATA ===`); - + if (services.items && services.items.length > 0) { for (const service of services.items) { const serviceName = service.metadata.name; const serviceType = service.spec.type; const ports = service.spec.ports; - + console.log(`Service: ${serviceName}, Type: ${serviceType}, Ports:`, ports); - + // Find NodePort for NodePort services let nodePort = null; if (serviceType === 'NodePort' && ports) { @@ -84,9 +90,9 @@ export const GET: RequestHandler = async () => { } } } - + console.log(`NodePort: ${nodePort}`); - + // Get pod data for this service let podData = { status: 'Unknown', @@ -98,28 +104,39 @@ export const GET: RequestHandler = async () => { node: 'N/A', podName: 'N/A' }; - + if (pods.items && pods.items.length > 0) { // Find pod that matches this service (usually same name or has service label) - const matchingPod = pods.items.find((pod: any) => - pod.metadata.name.includes(serviceName.replace('-service', '')) || - pod.metadata.labels?.app === 'evault' + const matchingPod = pods.items.find( + (pod: any) => + pod.metadata.name.includes( + serviceName.replace('-service', '') + ) || pod.metadata.labels?.app === 'evault' ); - + if (matchingPod) { const pod = matchingPod; - const readyCount = pod.status.containerStatuses?.filter((cs: any) => cs.ready).length || 0; + const readyCount = + pod.status.containerStatuses?.filter((cs: any) => cs.ready) + .length || 0; const totalCount = pod.status.containerStatuses?.length || 0; - const restarts = pod.status.containerStatuses?.reduce((sum: number, cs: any) => sum + (cs.restartCount || 0), 0) || 0; - + const restarts = + pod.status.containerStatuses?.reduce( + (sum: number, cs: any) => sum + (cs.restartCount || 0), + 0 + ) || 0; + // Calculate age const creationTime = new Date(pod.metadata.creationTimestamp); const now = new Date(); const ageMs = now.getTime() - creationTime.getTime(); const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24)); - const ageHours = Math.floor((ageMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); - const age = ageDays > 0 ? `${ageDays}d${ageHours}h` : `${ageHours}h`; - + const ageHours = Math.floor( + (ageMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60) + ); + const age = + ageDays > 0 ? `${ageDays}d${ageHours}h` : `${ageHours}h`; + podData = { status: pod.status.phase || 'Unknown', age: age, @@ -132,18 +149,18 @@ export const GET: RequestHandler = async () => { }; } } - + // Extract the eVault ID from the namespace const evaultId = namespace.replace('evault-', ''); - + // Generate service URL let serviceUrl = ''; if (nodePort) { serviceUrl = `http://${minikubeIP}:${nodePort}`; } - + console.log(`Service URL: ${serviceUrl}`); - + allEVaults.push({ id: serviceName, name: serviceName, @@ -172,4 +189,4 @@ export const GET: RequestHandler = async () => { console.error('Error fetching eVaults:', error); return json({ error: 'Failed to fetch eVaults', evaults: [] }, { status: 500 }); } -}; \ No newline at end of file +}; diff --git a/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/details/+server.ts b/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/details/+server.ts index 27bd353c..ceb226ec 100644 --- a/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/details/+server.ts +++ b/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/details/+server.ts @@ -11,14 +11,18 @@ export const GET: RequestHandler = async ({ params }) => { try { // Get detailed pod information const { stdout: podInfo } = await execAsync(`kubectl describe pod -n ${namespace} ${pod}`); - + // Get pod YAML - const { stdout: podYaml } = await execAsync(`kubectl get pod -n ${namespace} ${pod} -o yaml`); - + const { stdout: podYaml } = await execAsync( + `kubectl get pod -n ${namespace} ${pod} -o yaml` + ); + // Get pod metrics if available let metrics = null; try { - const { stdout: metricsOutput } = await execAsync(`kubectl top pod -n ${namespace} ${pod}`); + const { stdout: metricsOutput } = await execAsync( + `kubectl top pod -n ${namespace} ${pod}` + ); metrics = metricsOutput.trim(); } catch (metricsError) { // Metrics might not be available @@ -34,4 +38,4 @@ export const GET: RequestHandler = async ({ params }) => { console.error('Error fetching pod details:', error); return json({ error: 'Failed to fetch pod details' }, { status: 500 }); } -}; \ No newline at end of file +}; diff --git a/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/logs/+server.ts b/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/logs/+server.ts index 73502994..624376aa 100644 --- a/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/logs/+server.ts +++ b/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/logs/+server.ts @@ -10,12 +10,17 @@ export const GET: RequestHandler = async ({ params, url }) => { const tail = url.searchParams.get('tail') || '100'; try { - const { stdout } = await execAsync(`kubectl logs -n ${namespace} ${pod} -c evault --tail=${tail}`); - const logs = stdout.trim().split('\n').filter(line => line.trim()); - + const { stdout } = await execAsync( + `kubectl logs -n ${namespace} ${pod} -c evault --tail=${tail}` + ); + const logs = stdout + .trim() + .split('\n') + .filter((line) => line.trim()); + return json({ logs }); } catch (error) { console.error('Error fetching logs:', error); return json({ error: 'Failed to fetch logs', logs: [] }, { status: 500 }); } -}; \ No newline at end of file +}; diff --git a/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/metrics/+server.ts b/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/metrics/+server.ts index 29a605f9..431f3643 100644 --- a/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/metrics/+server.ts +++ b/infrastructure/control-panel/src/routes/api/evaults/[namespace]/[pod]/metrics/+server.ts @@ -7,7 +7,7 @@ const execAsync = promisify(exec); export const GET: RequestHandler = async ({ params }) => { const { namespace, pod } = params; - + console.log('Metrics API called with namespace:', namespace, 'pod:', pod); try { @@ -22,23 +22,34 @@ export const GET: RequestHandler = async ({ params }) => { topOutput = 'No metrics available'; } console.log('kubectl top pod output:', topOutput); - + // Get pod status details console.log('Running kubectl describe pod...'); - const { stdout: describeOutput } = await execAsync(`kubectl describe pod ${pod} -n ${namespace} 2>/dev/null || echo "No pod details available"`); + const { stdout: describeOutput } = await execAsync( + `kubectl describe pod ${pod} -n ${namespace} 2>/dev/null || echo "No pod details available"` + ); console.log('kubectl describe pod output length:', describeOutput?.length || 0); - + // Get container logs count (last 100 lines) console.log('Running kubectl logs...'); - const { stdout: logsOutput } = await execAsync(`kubectl logs -n ${namespace} ${pod} -c evault --tail=100 2>/dev/null || echo ""`); + const { stdout: logsOutput } = await execAsync( + `kubectl logs -n ${namespace} ${pod} -c evault --tail=100 2>/dev/null || echo ""` + ); console.log('kubectl logs output length:', logsOutput?.length || 0); - - const logLines = logsOutput.trim().split('\n').filter(line => line.trim()); - + + const logLines = logsOutput + .trim() + .split('\n') + .filter((line) => line.trim()); + // Parse top output for CPU and Memory let cpu = 'N/A'; let memory = 'N/A'; - if (topOutput && !topOutput.includes('No metrics available') && !topOutput.includes('Metrics API not available')) { + if ( + topOutput && + !topOutput.includes('No metrics available') && + !topOutput.includes('Metrics API not available') + ) { console.log('Parsing top output...'); const lines = topOutput.trim().split('\n'); console.log('Top output lines:', lines); @@ -54,18 +65,18 @@ export const GET: RequestHandler = async ({ params }) => { } } } - + console.log('Final CPU:', cpu, 'Memory:', memory); - + // Parse describe output for events and conditions const events: string[] = []; const conditions: string[] = []; - + if (describeOutput && !describeOutput.includes('No pod details available')) { const lines = describeOutput.split('\n'); let inEvents = false; let inConditions = false; - + for (const line of lines) { if (line.includes('Events:')) { inEvents = true; @@ -82,7 +93,7 @@ export const GET: RequestHandler = async ({ params }) => { inConditions = false; continue; } - + if (inEvents && line.trim() && !line.startsWith(' ')) { // Handle case where Events shows "" if (line.trim() === '') { @@ -96,37 +107,45 @@ export const GET: RequestHandler = async ({ params }) => { } } } - + // Calculate basic stats const totalLogLines = logLines.length; - const errorLogs = logLines.filter(line => - line.toLowerCase().includes('error') || - line.toLowerCase().includes('fail') || - line.toLowerCase().includes('exception') + const errorLogs = logLines.filter( + (line) => + line.toLowerCase().includes('error') || + line.toLowerCase().includes('fail') || + line.toLowerCase().includes('exception') ).length; - const warningLogs = logLines.filter(line => - line.toLowerCase().includes('warn') || - line.toLowerCase().includes('warning') + const warningLogs = logLines.filter( + (line) => line.toLowerCase().includes('warn') || line.toLowerCase().includes('warning') ).length; - + // Get additional pod info for alternative metrics let podAge = 'N/A'; let podStatus = 'Unknown'; try { - const { stdout: getPodOutput } = await execAsync(`kubectl get pod ${pod} -n ${namespace} -o json`); + const { stdout: getPodOutput } = await execAsync( + `kubectl get pod ${pod} -n ${namespace} -o json` + ); const podInfo = JSON.parse(getPodOutput); - podAge = podInfo.metadata?.creationTimestamp ? - Math.floor((Date.now() - new Date(podInfo.metadata.creationTimestamp).getTime()) / (1000 * 60 * 60 * 24)) + 'd' : 'N/A'; + podAge = podInfo.metadata?.creationTimestamp + ? Math.floor( + (Date.now() - new Date(podInfo.metadata.creationTimestamp).getTime()) / + (1000 * 60 * 60 * 24) + ) + 'd' + : 'N/A'; podStatus = podInfo.status?.phase || 'Unknown'; } catch (podError) { console.log('Failed to get pod info:', podError); } - + const metrics = { resources: { cpu, memory, - note: topOutput.includes('Metrics API not available') ? 'Metrics server not enabled' : undefined + note: topOutput.includes('Metrics API not available') + ? 'Metrics server not enabled' + : undefined }, logs: { totalLines: totalLogLines, @@ -141,15 +160,23 @@ export const GET: RequestHandler = async ({ params }) => { podStatus } }; - + return json(metrics); } catch (error) { console.error('Error fetching metrics:', error); - return json({ - error: 'Failed to fetch metrics', - resources: { cpu: 'N/A', memory: 'N/A' }, - logs: { totalLines: 0, errorCount: 0, warningCount: 0, lastUpdate: new Date().toISOString() }, - status: { events: [], conditions: [] } - }, { status: 500 }); + return json( + { + error: 'Failed to fetch metrics', + resources: { cpu: 'N/A', memory: 'N/A' }, + logs: { + totalLines: 0, + errorCount: 0, + warningCount: 0, + lastUpdate: new Date().toISOString() + }, + status: { events: [], conditions: [] } + }, + { status: 500 } + ); } -}; \ No newline at end of file +}; diff --git a/infrastructure/control-panel/src/routes/monitoring/[namespace]/[service]/+page.ts b/infrastructure/control-panel/src/routes/monitoring/[namespace]/[service]/+page.ts index 74c868ca..d4a96c50 100644 --- a/infrastructure/control-panel/src/routes/monitoring/[namespace]/[service]/+page.ts +++ b/infrastructure/control-panel/src/routes/monitoring/[namespace]/[service]/+page.ts @@ -4,4 +4,4 @@ export const load = ({ params }) => { namespace: params.namespace, service: params.service }; -}; \ No newline at end of file +}; diff --git a/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte b/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte index f1261fe2..a0063862 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/+layout.svelte @@ -1,49 +1,48 @@ diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte index 89ef3601..44cbb142 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte @@ -1,521 +1,511 @@ - - - - - - - -

- Point the camera at the code -

+ + + + + + + +

+ Point the camera at the code +

+ - import { goto } from "$app/navigation"; - import { onMount, getContext } from "svelte"; - import AppNav from "$lib/fragments/AppNav/AppNav.svelte"; - import type { GlobalState } from "$lib/global"; - import { Drawer } from "$lib/ui"; - import * as Button from "$lib/ui/Button"; - - const globalState = getContext<() => GlobalState>("globalState")(); - - interface SigningData { - session: string; - data: string; - redirect_uri: string; +import { goto } from "$app/navigation"; +import { onMount, getContext } from "svelte"; +import AppNav from "$lib/fragments/AppNav/AppNav.svelte"; +import type { GlobalState } from "$lib/global"; +import { Drawer } from "$lib/ui"; +import * as Button from "$lib/ui/Button"; + +const globalState = getContext<() => GlobalState>("globalState")(); + +interface SigningData { + session: string; + data: string; + redirect_uri: string; +} + +let signingData: SigningData | null = $state(null); +let decodedData: any = $state(null); +let signingStatus: "pending" | "signing" | "success" | "error" = + $state("pending"); +let errorMessage = $state(""); + +onMount(() => { + // Get signing data from URL parameters + const urlParams = new URLSearchParams(window.location.search); + const session = urlParams.get("session"); + const data = urlParams.get("data"); + const redirect_uri = urlParams.get("redirect_uri"); + + if (!session || !data || !redirect_uri) { + errorMessage = "Invalid signing request. Missing required parameters."; + signingStatus = "error"; + return; } - let signingData: SigningData | null = $state(null); - let decodedData: any = $state(null); - let signingStatus: "pending" | "signing" | "success" | "error" = - $state("pending"); - let errorMessage = $state(""); - - onMount(() => { - // Get signing data from URL parameters - const urlParams = new URLSearchParams(window.location.search); - const session = urlParams.get("session"); - const data = urlParams.get("data"); - const redirect_uri = urlParams.get("redirect_uri"); - - if (!session || !data || !redirect_uri) { - errorMessage = - "Invalid signing request. Missing required parameters."; - signingStatus = "error"; - return; - } + try { + // Decode base64 data + const decodedString = atob(data); + decodedData = JSON.parse(decodedString); + + signingData = { session, data, redirect_uri }; + signingStatus = "pending"; + } catch (error) { + console.error("Error decoding signing data:", error); + errorMessage = "Invalid signing data format."; + signingStatus = "error"; + } +}); + +async function handleSign() { + if (!signingData || !decodedData) return; + + try { + signingStatus = "signing"; - try { - // Decode base64 data - const decodedString = atob(data); - decodedData = JSON.parse(decodedString); - - signingData = { session, data, redirect_uri }; - signingStatus = "pending"; - } catch (error) { - console.error("Error decoding signing data:", error); - errorMessage = "Invalid signing data format."; - signingStatus = "error"; + // Get the vault for signing + const vault = await globalState.vaultController.vault; + if (!vault) { + throw new Error("No vault available for signing"); } - }); - - async function handleSign() { - if (!signingData || !decodedData) return; - - try { - signingStatus = "signing"; - - // Get the vault for signing - const vault = await globalState.vaultController.vault; - if (!vault) { - throw new Error("No vault available for signing"); - } - - // Create the message to sign - const messageToSign = JSON.stringify({ - pollId: decodedData.pollId, - voteData: decodedData.voteData, - userId: decodedData.userId, - timestamp: Date.now(), - }); - - // In a real implementation, you would use the vault's signing capabilities - // For now, we'll simulate the signing process - await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate signing delay - - // Create the signed payload - const signedPayload = { - sessionId: signingData.session, - signature: "simulated_signature_" + Date.now(), // In real implementation, this would be the actual signature - publicKey: vault.ename, // Use eName as public key for now - message: messageToSign, - }; - - // Send the signed payload to the redirect URI - const response = await fetch(signingData.redirect_uri, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(signedPayload), - }); - - if (!response.ok) { - throw new Error("Failed to submit signed payload"); - } - - signingStatus = "success"; - - // Redirect back to the main app after a short delay - setTimeout(() => { - goto("/main"); - }, 3000); - } catch (error) { - console.error("Error during signing:", error); - errorMessage = - error instanceof Error ? error.message : "Signing failed"; - signingStatus = "error"; + + // Create the message to sign + const messageToSign = JSON.stringify({ + pollId: decodedData.pollId, + voteData: decodedData.voteData, + userId: decodedData.userId, + timestamp: Date.now(), + }); + + // In a real implementation, you would use the vault's signing capabilities + // For now, we'll simulate the signing process + await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate signing delay + + // Create the signed payload + const signedPayload = { + sessionId: signingData.session, + signature: "simulated_signature_" + Date.now(), // In real implementation, this would be the actual signature + publicKey: vault.ename, // Use eName as public key for now + message: messageToSign, + }; + + // Send the signed payload to the redirect URI + const response = await fetch(signingData.redirect_uri, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(signedPayload), + }); + + if (!response.ok) { + throw new Error("Failed to submit signed payload"); } - } - function handleCancel() { - goto("/main"); + signingStatus = "success"; + + // Redirect back to the main app after a short delay + setTimeout(() => { + goto("/main"); + }, 3000); + } catch (error) { + console.error("Error during signing:", error); + errorMessage = + error instanceof Error ? error.message : "Signing failed"; + signingStatus = "error"; } +} - function handleRetry() { - signingStatus = "pending"; - errorMessage = ""; - } +function handleCancel() { + goto("/main"); +} + +function handleRetry() { + signingStatus = "pending"; + errorMessage = ""; +} diff --git a/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte index 07c860b1..3f2477bf 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/login/+page.svelte @@ -1,152 +1,144 @@
- import SplashScreen from "$lib/fragments/SplashScreen/SplashScreen.svelte"; - import { onMount, setContext, getContext } from "svelte"; - import "../app.css"; - import { onNavigate, goto } from "$app/navigation"; - import { GlobalState } from "$lib/global/state"; - - import { runtime } from "$lib/global/runtime.svelte"; - import { type Status, checkStatus } from "@tauri-apps/plugin-biometric"; - - const { children } = $props(); - - let globalState: GlobalState | undefined = $state(undefined); - - let showSplashScreen = $state(false); - let previousRoute = null; - let navigationStack: string[] = []; - - setContext("globalState", () => globalState); - - // replace with actual data loading logic - async function loadData() { - await new Promise((resolve) => setTimeout(resolve, 1500)); +import SplashScreen from "$lib/fragments/SplashScreen/SplashScreen.svelte"; +import { onMount, setContext, getContext } from "svelte"; +import "../app.css"; +import { onNavigate, goto } from "$app/navigation"; +import { GlobalState } from "$lib/global/state"; + +import { runtime } from "$lib/global/runtime.svelte"; +import { type Status, checkStatus } from "@tauri-apps/plugin-biometric"; + +const { children } = $props(); + +let globalState: GlobalState | undefined = $state(undefined); + +let showSplashScreen = $state(false); +let previousRoute = null; +let navigationStack: string[] = []; + +setContext("globalState", () => globalState); + +// replace with actual data loading logic +async function loadData() { + await new Promise((resolve) => setTimeout(resolve, 1500)); +} + +async function ensureMinimumDelay() { + await new Promise((resolve) => setTimeout(resolve, 500)); +} + +onMount(async () => { + let status: Status | undefined = undefined; + try { + status = await checkStatus(); + } catch (error) { + status = { + biometryType: 0, + isAvailable: false, + }; } - - async function ensureMinimumDelay() { - await new Promise((resolve) => setTimeout(resolve, 500)); + runtime.biometry = status.biometryType; + try { + globalState = await GlobalState.create(); + } catch (error) { + console.error("Failed to initialize global state:", error); + // Consider adding fallback behavior or user notification } - onMount(async () => { - let status: Status | undefined = undefined; - try { - status = await checkStatus(); - } catch (error) { - status = { - biometryType: 0, - isAvailable: false, - }; - } - runtime.biometry = status.biometryType; - try { - globalState = await GlobalState.create(); - } catch (error) { - console.error("Failed to initialize global state:", error); - // Consider adding fallback behavior or user notification + // Handle deep links + try { + const { onOpenUrl, getCurrent } = await import( + "@tauri-apps/plugin-deep-link" + ); + + // Check if app was started via deep link + const initialUrls = await getCurrent(); + if (initialUrls && initialUrls.length > 0) { + handleDeepLink(initialUrls[0]); } - // Handle deep links + // Listen for future deep links + await onOpenUrl((urls) => { + if (urls && urls.length > 0) { + handleDeepLink(urls[0]); + } + }); + } catch (error) { + console.error("Failed to initialize deep link listener:", error); + } + + function handleDeepLink(urlString: string) { + console.log("Deep link received:", urlString); try { - const { onOpenUrl, getCurrent } = await import( - "@tauri-apps/plugin-deep-link" + const url = new URL(urlString); + const path = url.pathname; + const params = url.searchParams; + + console.log("Deep link path:", path); + console.log("Deep link hostname:", url.hostname); + console.log("Deep link protocol:", url.protocol); + console.log("Deep link full URL object:", url); + console.log( + "Deep link params:", + Object.fromEntries(params.entries()), ); - // Check if app was started via deep link - const initialUrls = await getCurrent(); - if (initialUrls && initialUrls.length > 0) { - handleDeepLink(initialUrls[0]); - } + // Check if we're already on the scan-qr page + const currentPath = window.location.pathname; + const isOnScanPage = currentPath === "/scan-qr"; + console.log( + "Current path:", + currentPath, + "Is on scan page:", + isOnScanPage, + ); - // Listen for future deep links - await onOpenUrl((urls) => { - if (urls && urls.length > 0) { - handleDeepLink(urls[0]); - } - }); - } catch (error) { - console.error("Failed to initialize deep link listener:", error); - } + // For w3ds:// URLs, we need to check the hostname instead of pathname + // w3ds://auth becomes hostname: "auth", pathname: "" + const action = url.hostname || path; + console.log("Deep link action (hostname):", action); - function handleDeepLink(urlString: string) { - console.log("Deep link received:", urlString); - try { - const url = new URL(urlString); - const path = url.pathname; - const params = url.searchParams; - - console.log("Deep link path:", path); - console.log("Deep link hostname:", url.hostname); - console.log("Deep link protocol:", url.protocol); - console.log("Deep link full URL object:", url); - console.log( - "Deep link params:", - Object.fromEntries(params.entries()), - ); + // Example: w3ds://auth?session=123&platform=example&redirect=https://example.com + if (action === "auth") { + // Handle authentication deep link + const sessionId = params.get("session"); + const platform = params.get("platform"); + const redirect = params.get("redirect"); - // Check if we're already on the scan-qr page - const currentPath = window.location.pathname; - const isOnScanPage = currentPath === "/scan-qr"; console.log( - "Current path:", - currentPath, - "Is on scan page:", - isOnScanPage, + "Auth deep link - session:", + sessionId, + "platform:", + platform, + "redirect:", + redirect, ); - // For w3ds:// URLs, we need to check the hostname instead of pathname - // w3ds://auth becomes hostname: "auth", pathname: "" - const action = url.hostname || path; - console.log("Deep link action (hostname):", action); - - // Example: w3ds://auth?session=123&platform=example&redirect=https://example.com - if (action === "auth") { - // Handle authentication deep link - const sessionId = params.get("session"); - const platform = params.get("platform"); - const redirect = params.get("redirect"); - - console.log( - "Auth deep link - session:", - sessionId, - "platform:", - platform, - "redirect:", - redirect, - ); - - if (sessionId && platform && redirect) { - // Always store the deep link data first - const deepLinkData = { - type: "auth", - session: sessionId, - platform: platform, - redirect: redirect, - }; - - // Check if user is authenticated by looking for vault data - const checkAuth = async () => { - try { - // Try to access vault data - if this fails, user is not authenticated - const globalState = - getContext<() => GlobalState>( - "globalState", - )(); - if (globalState) { - const vault = - await globalState.vaultController.vault; - if (vault) { - // User is authenticated, go to scan page - console.log( - "User authenticated, navigating to scan-qr", - ); - sessionStorage.setItem( - "deepLinkData", - JSON.stringify(deepLinkData), - ); - goto("/scan-qr"); - return; - } + if (sessionId && platform && redirect) { + // Always store the deep link data first + const deepLinkData = { + type: "auth", + session: sessionId, + platform: platform, + redirect: redirect, + }; + + // Check if user is authenticated by looking for vault data + const checkAuth = async () => { + try { + // Try to access vault data - if this fails, user is not authenticated + const globalState = + getContext<() => GlobalState>("globalState")(); + if (globalState) { + const vault = + await globalState.vaultController.vault; + if (vault) { + // User is authenticated, go to scan page + console.log( + "User authenticated, navigating to scan-qr", + ); + sessionStorage.setItem( + "deepLinkData", + JSON.stringify(deepLinkData), + ); + goto("/scan-qr"); + return; } - } catch (error) { - console.log( - "User not authenticated, redirecting to login", - ); } - - // User not authenticated, store deep link data and redirect to login + } catch (error) { console.log( - "User not authenticated, storing deep link data and redirecting to login", + "User not authenticated, redirecting to login", ); - sessionStorage.setItem( - "pendingDeepLink", - JSON.stringify(deepLinkData), - ); - goto("/login"); - }; - - checkAuth(); - } else { - console.log("Missing required auth parameters"); - } - } else if (action === "sign") { - // Handle signing deep link - const sessionId = params.get("session"); - const data = params.get("data"); - const redirectUri = params.get("redirect_uri"); - - console.log( - "Sign deep link - session:", - sessionId, - "data:", - data, - "redirect_uri:", - redirectUri, - ); - - if (sessionId && data && redirectUri) { - // Always store the deep link data first - const deepLinkData = { - type: "sign", - session: sessionId, - data: data, - redirect_uri: redirectUri, - }; - - // Check if user is authenticated by looking for vault data - const checkAuth = async () => { - try { - // Try to access vault data - if this fails, user is not authenticated - const globalState = - getContext<() => GlobalState>( - "globalState", - )(); - if (globalState) { - const vault = - await globalState.vaultController.vault; - if (vault) { - // User is authenticated, go to scan page - console.log( - "User authenticated, navigating to scan-qr", - ); - sessionStorage.setItem( - "deepLinkData", - JSON.stringify(deepLinkData), - ); - goto("/scan-qr"); - return; - } + } + + // User not authenticated, store deep link data and redirect to login + console.log( + "User not authenticated, storing deep link data and redirecting to login", + ); + sessionStorage.setItem( + "pendingDeepLink", + JSON.stringify(deepLinkData), + ); + goto("/login"); + }; + + checkAuth(); + } else { + console.log("Missing required auth parameters"); + } + } else if (action === "sign") { + // Handle signing deep link + const sessionId = params.get("session"); + const data = params.get("data"); + const redirectUri = params.get("redirect_uri"); + + console.log( + "Sign deep link - session:", + sessionId, + "data:", + data, + "redirect_uri:", + redirectUri, + ); + + if (sessionId && data && redirectUri) { + // Always store the deep link data first + const deepLinkData = { + type: "sign", + session: sessionId, + data: data, + redirect_uri: redirectUri, + }; + + // Check if user is authenticated by looking for vault data + const checkAuth = async () => { + try { + // Try to access vault data - if this fails, user is not authenticated + const globalState = + getContext<() => GlobalState>("globalState")(); + if (globalState) { + const vault = + await globalState.vaultController.vault; + if (vault) { + // User is authenticated, go to scan page + console.log( + "User authenticated, navigating to scan-qr", + ); + sessionStorage.setItem( + "deepLinkData", + JSON.stringify(deepLinkData), + ); + goto("/scan-qr"); + return; } - } catch (error) { - console.log( - "User not authenticated, redirecting to login", - ); } - - // User not authenticated, store deep link data and redirect to login + } catch (error) { console.log( - "User not authenticated, storing deep link data and redirecting to login", + "User not authenticated, redirecting to login", ); - sessionStorage.setItem( - "pendingDeepLink", - JSON.stringify(deepLinkData), - ); - goto("/login"); - }; - - checkAuth(); - } else { - console.log("Missing required signing parameters"); - } + } + + // User not authenticated, store deep link data and redirect to login + console.log( + "User not authenticated, storing deep link data and redirecting to login", + ); + sessionStorage.setItem( + "pendingDeepLink", + JSON.stringify(deepLinkData), + ); + goto("/login"); + }; + + checkAuth(); } else { - console.log("Unknown deep link path:", path); + console.log("Missing required signing parameters"); } - } catch (error) { - console.error("Failed to parse deep link URL:", error); + } else { + console.log("Unknown deep link path:", path); } + } catch (error) { + console.error("Failed to parse deep link URL:", error); } + } - showSplashScreen = true; // Can't set up the original state to true or animation won't start - navigationStack.push(window.location.pathname); + showSplashScreen = true; // Can't set up the original state to true or animation won't start + navigationStack.push(window.location.pathname); - await Promise.all([loadData(), ensureMinimumDelay()]); + await Promise.all([loadData(), ensureMinimumDelay()]); - showSplashScreen = false; - }); + showSplashScreen = false; +}); - const safeAreaTop = $derived.by( - () => - Number.parseFloat( - getComputedStyle(document.documentElement).getPropertyValue( - "--safe-top", - ), - ) || 0, - ); +const safeAreaTop = $derived.by( + () => + Number.parseFloat( + getComputedStyle(document.documentElement).getPropertyValue( + "--safe-top", + ), + ) || 0, +); - $effect(() => console.log("top", safeAreaTop)); +$effect(() => console.log("top", safeAreaTop)); - onNavigate((navigation) => { - if (!document.startViewTransition) return; +onNavigate((navigation) => { + if (!document.startViewTransition) return; - const from = navigation.from?.url.pathname; - const to = navigation.to?.url.pathname; + const from = navigation.from?.url.pathname; + const to = navigation.to?.url.pathname; - if (!from || !to || from === to) return; + if (!from || !to || from === to) return; - let direction: "left" | "right" = "right"; + let direction: "left" | "right" = "right"; - const fromIndex = navigationStack.lastIndexOf(from); - const toIndex = navigationStack.lastIndexOf(to); + const fromIndex = navigationStack.lastIndexOf(from); + const toIndex = navigationStack.lastIndexOf(to); - if (toIndex !== -1 && toIndex < fromIndex) { - // Backward navigation - direction = "left"; - navigationStack = navigationStack.slice(0, toIndex + 1); - } else { - // Forward navigation (or new path) - direction = "right"; - navigationStack.push(to); - } + if (toIndex !== -1 && toIndex < fromIndex) { + // Backward navigation + direction = "left"; + navigationStack = navigationStack.slice(0, toIndex + 1); + } else { + // Forward navigation (or new path) + direction = "right"; + navigationStack.push(to); + } - document.documentElement.setAttribute("data-transition", direction); - previousRoute = to; + document.documentElement.setAttribute("data-transition", direction); + previousRoute = to; - return new Promise((resolve) => { - document.startViewTransition(async () => { - resolve(); - await navigation.complete; - }); + return new Promise((resolve) => { + document.startViewTransition(async () => { + resolve(); + await navigation.complete; }); }); +}); {#if showSplashScreen} diff --git a/platforms/blabsy/src/components/login/login-main.tsx b/platforms/blabsy/src/components/login/login-main.tsx index f2d0baec..1bd32f38 100644 --- a/platforms/blabsy/src/components/login/login-main.tsx +++ b/platforms/blabsy/src/components/login/login-main.tsx @@ -35,6 +35,7 @@ export function LoginMain(): JSX.Element { process.env.NEXT_PUBLIC_BASE_URL ).toString() ); + console.log('getting offer data'); setQr(data.uri); watchEventStream( new URL(data.uri).searchParams.get('session') as string diff --git a/platforms/blabsy/src/lib/utils/mobile-detection.ts b/platforms/blabsy/src/lib/utils/mobile-detection.ts index d381195b..81d924a4 100644 --- a/platforms/blabsy/src/lib/utils/mobile-detection.ts +++ b/platforms/blabsy/src/lib/utils/mobile-detection.ts @@ -1,10 +1,13 @@ export function isMobileDevice(): boolean { - if (typeof window === 'undefined') return false; - - return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || - (window.innerWidth <= 768); + if (typeof window === 'undefined') return false; + + return ( + /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + navigator.userAgent + ) || window.innerWidth <= 768 + ); } export function getDeepLinkUrl(qrData: string): string { - return qrData; -} \ No newline at end of file + return qrData; +} diff --git a/platforms/pictique/src/lib/fragments/ActionMenu/ActionMenu.svelte b/platforms/pictique/src/lib/fragments/ActionMenu/ActionMenu.svelte index 63e62b01..6ce4af7b 100644 --- a/platforms/pictique/src/lib/fragments/ActionMenu/ActionMenu.svelte +++ b/platforms/pictique/src/lib/fragments/ActionMenu/ActionMenu.svelte @@ -54,7 +54,7 @@