diff --git a/.env.example b/.env.example index d81932bd..dc88458d 100644 --- a/.env.example +++ b/.env.example @@ -71,3 +71,13 @@ EREPUTATION_MAPPING_DB_PATH="/path/to/erep/mapping/db" VITE_EREPUTATION_BASE_URL=http://localhost:8765 LOAD_TEST_USER_COUNT=6 + +PUBLIC_EID_WALLET_TOKEN=obtained-from-post-registry-service-/platforms/certification + +LOKI_URL=http://localhost:3100 +LOKI_USERNAME=admin +LOKI_PASSWORD=admin + +LOKI_URL=http://146.190.29.56:3100 +LOKI_USERNAME=admin +LOKI_PASSWORD=admin diff --git a/asd b/asd new file mode 100644 index 00000000..e69de29b diff --git a/db/init-multiple-databases.sh b/db/init-multiple-databases.sh new file mode 100755 index 00000000..299ad717 --- /dev/null +++ b/db/init-multiple-databases.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Get the list of databases from environment variable +# Default to empty if not set +POSTGRES_MULTIPLE_DATABASES=${POSTGRES_MULTIPLE_DATABASES:-} + +# If no databases specified, exit +if [ -z "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "No databases specified in POSTGRES_MULTIPLE_DATABASES" + exit 0 +fi + +echo "Creating multiple databases..." + +# Split the comma-separated list and create each database +IFS=',' read -ra DATABASES <<< "$POSTGRES_MULTIPLE_DATABASES" +for db in "${DATABASES[@]}"; do + # Trim whitespace + db=$(echo "$db" | xargs) + + if [ -n "$db" ]; then + # Check if database exists + DB_EXISTS=$(psql -v ON_ERROR_STOP=0 --username "$POSTGRES_USER" --dbname postgres -tAc "SELECT 1 FROM pg_database WHERE datname='$db'" 2>/dev/null || echo "") + + if [ "$DB_EXISTS" = "1" ]; then + echo "Database $db already exists, skipping..." + else + echo "Creating database: $db" + # Create the database directly (not inside a function) + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres <<-EOSQL + CREATE DATABASE "$db"; +EOSQL + fi + fi +done + +echo "Multiple databases created successfully!" + diff --git a/dev-docker-compose.yaml b/dev-docker-compose.yaml index f17b6dbc..28f8b5ce 100644 --- a/dev-docker-compose.yaml +++ b/dev-docker-compose.yaml @@ -126,6 +126,19 @@ services: networks: - metastate-network <<: *common-host-access + entrypoint: ["/bin/sh", "-c"] + command: + - | + # Remove any stale PID files before starting Neo4j + # Neo4j stores PID files in /var/lib/neo4j/run/neo4j.pid + rm -f /var/lib/neo4j/run/neo4j.pid 2>/dev/null || true + rm -f /var/lib/neo4j/data/run/neo4j.pid 2>/dev/null || true + rm -f /var/lib/neo4j/data/neo4j.pid 2>/dev/null || true + # Also clean up any other PID files + find /var/lib/neo4j -name "*.pid" -type f -delete 2>/dev/null || true + find /var/lib/neo4j/data -name "*.pid" -type f -delete 2>/dev/null || true + # Start Neo4j with the original entrypoint + exec /startup/docker-entrypoint.sh neo4j healthcheck: test: [ "CMD-SHELL", "cypher-shell -u neo4j -p ${NEO4J_PASSWORD:-neo4j} 'RETURN 1' || exit 1" ] interval: 10s @@ -557,6 +570,18 @@ services: - metastate-network <<: *common-host-access + # Loki for log aggregation + loki: + profiles: + - all + image: grafana/loki:latest + ports: + - "3100:3100" + command: -config.file=/etc/loki/local-config.yaml + networks: + - metastate-network + <<: *common-host-access + volumes: postgres_data: neo4j_data: diff --git a/infrastructure/control-panel/src/lib/components/EVaultList.svelte b/infrastructure/control-panel/src/lib/components/EVaultList.svelte index 0d141d0f..8656c638 100644 --- a/infrastructure/control-panel/src/lib/components/EVaultList.svelte +++ b/infrastructure/control-panel/src/lib/components/EVaultList.svelte @@ -138,7 +138,7 @@

No eVaults found

- Try refreshing or check your Kubernetes connection + Try refreshing or check your registry connection

{:else} @@ -155,7 +155,7 @@ - Namespace + eName (w3id) - Age - - - Service URL + URI @@ -180,38 +175,35 @@ - {evault.name} + {evault.name || evault.ename || evault.evault} - {evault.namespace} + {evault.ename || 'N/A'} - {evault.status} + {evault.status || 'Unknown'} - {evault.age || 'Unknown'} - - - {#if evault.serviceUrl} + {#if evault.uri || evault.serviceUrl} - {evault.serviceUrl} + {evault.uri || evault.serviceUrl} {:else} - No external access + No URI available {/if} diff --git a/infrastructure/control-panel/src/lib/services/evaultService.ts b/infrastructure/control-panel/src/lib/services/evaultService.ts index 5e11675c..216b0f48 100644 --- a/infrastructure/control-panel/src/lib/services/evaultService.ts +++ b/infrastructure/control-panel/src/lib/services/evaultService.ts @@ -75,16 +75,15 @@ export class EVaultService { } /** - * Get logs for a specific eVault pod + * Get logs for a specific eVault by evaultId */ - /** - * Get logs for a specific eVault pod - */ - static async getEVaultLogs(namespace: string, podName: string): Promise { + static async getEVaultLogs(evaultId: string, tail?: number): Promise { try { - const response = await fetch( - `/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/logs` - ); + const url = new URL(`/api/evaults/${encodeURIComponent(evaultId)}/logs`, window.location.origin); + if (tail) { + url.searchParams.set('tail', tail.toString()); + } + const response = await fetch(url.toString()); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } @@ -97,20 +96,20 @@ export class EVaultService { } /** - * Get metrics for a specific eVault pod + * Get details for a specific eVault by evaultId */ - static async getEVaultMetrics(namespace: string, podName: string): Promise { + static async getEVaultDetails(evaultId: string): Promise { try { const response = await fetch( - `/api/evaults/${encodeURIComponent(namespace)}/${encodeURIComponent(podName)}/metrics` + `/api/evaults/${encodeURIComponent(evaultId)}/details` ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); - return data.metrics || {}; + return data.evault || {}; } catch (error) { - console.error('Failed to fetch eVault metrics:', error); + console.error('Failed to fetch eVault details:', error); throw error; } } diff --git a/infrastructure/control-panel/src/lib/services/loki.ts b/infrastructure/control-panel/src/lib/services/loki.ts index 34cb6f1a..361249dc 100644 --- a/infrastructure/control-panel/src/lib/services/loki.ts +++ b/infrastructure/control-panel/src/lib/services/loki.ts @@ -98,6 +98,49 @@ export class LokiService { .filter((event): event is FlowEvent => event !== null); } + /** + * Query logs for a specific evault by identifier + * Supports querying by evault field or ename (w3id) in log labels + */ + async getEVaultLogs( + evaultId: string, + ename?: string, + limit: number = 100, + start?: string, + end?: string + ): Promise { + // Try multiple query patterns to find logs for this evault + // First try by evault field, then by ename/w3id + const queries = [ + `{evault="${evaultId}"}`, + ...(ename ? [`{ename="${ename}"}`, `{w3id="${ename}"}`] : []) + ]; + + const allLogs: LogEntry[] = []; + + // Try each query pattern + for (const query of queries) { + try { + const logs = await this.queryLogs(query, start, end); + allLogs.push(...logs); + } catch (error) { + console.log(`Query ${query} failed, trying next pattern`); + } + } + + // Remove duplicates and sort by timestamp + const uniqueLogs = Array.from( + new Map(allLogs.map((log) => [`${log.timestamp}-${log.line}`, log])).values() + ).sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); + + // Extract log lines and limit to requested number + const logLines = uniqueLogs + .map((log) => log.line) + .slice(-limit); // Get last N lines + + return logLines; + } + parseLogEntry(log: LogEntry): FlowEvent | null { try { // Parse the JSON log line diff --git a/infrastructure/control-panel/src/lib/services/registry.ts b/infrastructure/control-panel/src/lib/services/registry.ts index 99b1c8b0..28696b75 100644 --- a/infrastructure/control-panel/src/lib/services/registry.ts +++ b/infrastructure/control-panel/src/lib/services/registry.ts @@ -1,95 +1,160 @@ -import { env } from '$env/dynamic/public'; +import { env } from "$env/dynamic/public"; export interface Platform { - name: string; - url: string; - status: 'Active' | 'Inactive'; - uptime: string; + name: string; + url: string; + status: "Active" | "Inactive"; + uptime: string; +} + +export interface RegistryVault { + ename: string; + uri: string; + evault: string; + originalUri?: string; + resolved?: boolean; } export class RegistryService { - private baseUrl: string; - - constructor() { - this.baseUrl = env.PUBLIC_REGISTRY_URL || 'https://registry.staging.metastate.foundation'; - } - - async getPlatforms(): Promise { - try { - const response = await fetch(`${this.baseUrl}/platforms`); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const platformUrls: string[] = await response.json(); - - // Convert URLs to platform objects with friendly names - const platforms = platformUrls.map((url) => { - // Ensure URL has protocol - const urlWithProtocol = - url.startsWith('http://') || url.startsWith('https://') ? url : `http://${url}`; - const urlObj = new URL(urlWithProtocol); - const hostname = urlObj.hostname; - const port = urlObj.port; - const protocol = urlObj.protocol; - - // Extract platform name from hostname - let name = hostname.split('.')[0]; - - // Capitalize and format the name - if (name === 'pictique') name = 'Pictique'; - else if (name === 'blabsy') name = 'Blabsy'; - else if (name === 'charter') name = 'Group Charter'; - else if (name === 'cerberus') name = 'Cerberus'; - else if (name === 'evoting') name = 'eVoting'; - else name = name.charAt(0).toUpperCase() + name.slice(1); - - // Build the full URL with protocol and port - const fullUrl = port ? `${hostname}:${port}` : hostname; - const displayUrl = `${protocol}//${fullUrl}`; - - return { - name, - url: displayUrl, - status: 'Active' as const, - uptime: '24h' - }; - }); - - return platforms; - } catch (error) { - console.error('Error fetching platforms from registry:', error); - - // Return fallback platforms if registry is unavailable - return [ - { - name: 'Blabsy', - url: 'http://192.168.0.235:4444', - status: 'Active', - uptime: '24h' - }, - { - name: 'Pictique', - url: 'http://192.168.0.235:1111', - status: 'Active', - uptime: '24h' - }, - { - name: 'Group Charter', - url: 'http://192.168.0.235:5555', - status: 'Active', - uptime: '24h' - }, - { - name: 'Cerberus', - url: 'http://192.168.0.235:6666', - status: 'Active', - uptime: '24h' - } - ]; - } - } + private baseUrl: string; + + constructor() { + this.baseUrl = + env.PUBLIC_REGISTRY_URL || + "https://registry.staging.metastate.foundation"; + } + + async getEVaults(): Promise { + try { + const response = await fetch(`${this.baseUrl}/list`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const vaults: RegistryVault[] = await response.json(); + return vaults; + } catch (error) { + console.error("Error fetching evaults from registry:", error); + return []; + } + } + + async getPlatforms(): Promise { + try { + const response = await fetch(`${this.baseUrl}/platforms`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const platformUrls: (string | null | undefined)[] = + await response.json(); + + // Filter out null/undefined values and convert URLs to platform objects + const platforms = platformUrls + .filter( + (url): url is string => url != null && url.trim() !== "", + ) + .map((url) => { + // Use the original URL from the registry (it already has the correct format) + let displayUrl = url.trim(); + + // Ensure URL has protocol if it doesn't + if ( + !displayUrl.startsWith("http://") && + !displayUrl.startsWith("https://") + ) { + displayUrl = `http://${displayUrl}`; + } + + // Parse URL to extract platform name + let name = "Unknown"; + try { + const urlObj = new URL(displayUrl); + const hostname = urlObj.hostname; + // Extract platform name from hostname (remove port if present) + const hostnameWithoutPort = hostname.split(":")[0]; + const namePart = hostnameWithoutPort.split(".")[0]; + + // Capitalize and format the name + if (namePart === "pictique") name = "Pictique"; + else if (namePart === "blabsy") name = "Blabsy"; + else if (namePart === "charter") name = "Group Charter"; + else if (namePart === "cerberus") name = "Cerberus"; + else if (namePart === "evoting") name = "eVoting"; + else if (namePart === "dreamsync") name = "DreamSync"; + else if (namePart === "ereputation") + name = "eReputation"; + else + name = + namePart.charAt(0).toUpperCase() + + namePart.slice(1); + } catch { + // If URL parsing fails, try to extract name from the URL string + const match = displayUrl.match( + /(?:https?:\/\/)?([^:./]+)/, + ); + if (match) { + const namePart = match[1].toLowerCase(); + if (namePart === "pictique") name = "Pictique"; + else if (namePart === "blabsy") name = "Blabsy"; + else if (namePart === "charter") + name = "Group Charter"; + else if (namePart === "cerberus") name = "Cerberus"; + else if (namePart === "evoting") name = "eVoting"; + else if (namePart === "dreamsync") + name = "DreamSync"; + else if (namePart === "ereputation") + name = "eReputation"; + else + name = + namePart.charAt(0).toUpperCase() + + namePart.slice(1); + } + } + + return { + name, + url: displayUrl, + status: "Active" as const, + uptime: "24h", + }; + }); + + return platforms; + } catch (error) { + console.error("Error fetching platforms from registry:", error); + + // Return fallback platforms if registry is unavailable + return [ + { + name: "Blabsy", + url: "http://192.168.0.235:4444", + status: "Active", + uptime: "24h", + }, + { + name: "Pictique", + url: "http://192.168.0.235:1111", + status: "Active", + uptime: "24h", + }, + { + name: "Group Charter", + url: "http://192.168.0.235:5555", + status: "Active", + uptime: "24h", + }, + { + name: "Cerberus", + url: "http://192.168.0.235:6666", + status: "Active", + uptime: "24h", + }, + ]; + } + } } export const registryService = new RegistryService(); diff --git a/infrastructure/control-panel/src/routes/+page.svelte b/infrastructure/control-panel/src/routes/+page.svelte index 34a76626..34c14a3c 100644 --- a/infrastructure/control-panel/src/routes/+page.svelte +++ b/infrastructure/control-panel/src/routes/+page.svelte @@ -1,13 +1,13 @@