diff --git a/.env.example b/.env.example index 186825c..081c397 100644 --- a/.env.example +++ b/.env.example @@ -21,14 +21,8 @@ NUXT_DOCUMENSO_BASE_URL=https://signatures.portnimara.dev # Webhook Configuration for Embedded Signing WEBHOOK_SECRET_SIGNING=96BQQRiKkTIN2w0rHbqo7yHggV/sT8702HtHih3uNSY= -# Keycloak Configuration -KEYCLOAK_ISSUER=https://auth.portnimara.dev/realms/client-portal -KEYCLOAK_CLIENT_ID=client-portal -KEYCLOAK_CLIENT_SECRET=your-keycloak-client-secret -KEYCLOAK_CALLBACK_URL=https://client.portnimara.dev/auth/callback -# For local development, use: http://localhost:3000/auth/callback - -# OIDC Session Configuration -OIDC_SESSION_SECRET=your-32-character-session-secret -OIDC_ENCRYPT_KEY=your-32-character-encryption-key -OIDC_ENCRYPT_IV=16-char-enc-iv! +# nuxt-oidc-auth Configuration +NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_SECRET=your-keycloak-client-secret +NUXT_OIDC_TOKEN_KEY=base64_encoded_32_byte_key +NUXT_OIDC_SESSION_SECRET=48_character_random_string_for_session_security +NUXT_OIDC_AUTH_SESSION_SECRET=48_character_random_string_for_auth_session diff --git a/composables/useKeycloak.ts b/composables/useKeycloak.ts deleted file mode 100644 index b49e099..0000000 --- a/composables/useKeycloak.ts +++ /dev/null @@ -1,211 +0,0 @@ -import type Keycloak from 'keycloak-js' - -export const useKeycloak = () => { - const config = useRuntimeConfig() - const keycloak = ref(null) - const isAuthenticated = ref(false) - const user = ref(null) - const token = ref(null) - const isInitialized = ref(false) - - // Get the correct base URL considering proxy setup - const getBaseUrl = () => { - if (process.server) { - // In production, always use the configured HTTPS base URL - return config.public.baseUrl - } - - // Client-side: detect if we're behind a proxy - const currentOrigin = window.location.origin - const isProduction = !currentOrigin.includes('localhost') && !currentOrigin.includes('127.0.0.1') - - if (isProduction) { - // Force HTTPS in production environments - return config.public.baseUrl - } - - // Development: use current origin - return currentOrigin - } - - const logDebug = (message: string, data?: any) => { - if (config.public.keycloakDebug) { - console.log(`[KEYCLOAK] ${message}`, data) - } - } - - const initKeycloak = async () => { - if (process.server) return false - - try { - const baseUrl = getBaseUrl() - logDebug('Initializing Keycloak', { - baseUrl, - keycloakUrl: config.public.keycloak.url, - realm: config.public.keycloak.realm, - clientId: config.public.keycloak.clientId - }) - - // Dynamically import keycloak-js - const KeycloakModule = await import('keycloak-js') - const KeycloakConstructor = KeycloakModule.default - - keycloak.value = new KeycloakConstructor({ - url: config.public.keycloak.url, - realm: config.public.keycloak.realm, - clientId: config.public.keycloak.clientId, - }) - - const authenticated = await keycloak.value.init({ - onLoad: 'check-sso', - // Use proper HTTPS redirect URI - redirectUri: `${baseUrl}/auth/callback`, - // Disable all iframe-based features that cause CORS issues - checkLoginIframe: false, - silentCheckSsoRedirectUri: undefined, // Disable silent SSO check - enableLogging: false, // Reduce console noise - // Use standard flow compatible with proxy setups - flow: 'standard', - responseMode: 'query', // Use query params instead of fragments - // Disable third-party cookie checks - checkLoginIframeInterval: 0, - // PKCE for security - pkceMethod: 'S256', - // Timeout settings - messageReceiveTimeout: 10000, - // Disable adapter features that can cause issues in proxied environments - adapter: 'default' - }) - - logDebug('Keycloak initialization result', { - authenticated, - redirectUri: `${baseUrl}/auth/callback`, - checkLoginIframe: false, - silentSso: 'disabled' - }) - - isAuthenticated.value = authenticated - isInitialized.value = true - - if (authenticated && keycloak.value.token) { - token.value = keycloak.value.token || null - user.value = { - id: keycloak.value.subject, - username: keycloak.value.tokenParsed?.preferred_username, - email: keycloak.value.tokenParsed?.email, - firstName: keycloak.value.tokenParsed?.given_name, - lastName: keycloak.value.tokenParsed?.family_name, - fullName: keycloak.value.tokenParsed?.name, - roles: keycloak.value.tokenParsed?.realm_access?.roles || [], - } - - // Set up token refresh - keycloak.value.onTokenExpired = () => { - keycloak.value?.updateToken(30).then((refreshed) => { - if (refreshed) { - token.value = keycloak.value?.token || null - console.log('Token refreshed') - } else { - console.log('Token still valid') - } - }).catch(() => { - console.log('Failed to refresh token') - logout() - }) - } - } - - return authenticated - } catch (error) { - console.error('Failed to initialize Keycloak:', error) - isInitialized.value = true - return false - } - } - - const login = async () => { - console.log('[KEYCLOAK] Login function called') - - if (!keycloak.value) { - console.log('[KEYCLOAK] Keycloak not initialized, initializing now...') - await initKeycloak() - } - - if (keycloak.value) { - try { - const baseUrl = getBaseUrl() - console.log('[KEYCLOAK] Starting login', { - redirectUri: `${baseUrl}/dashboard`, - keycloakInitialized: !!keycloak.value, - keycloakAuthenticated: keycloak.value.authenticated - }) - - await keycloak.value.login({ - redirectUri: `${baseUrl}/dashboard` - }) - - console.log('[KEYCLOAK] Login method completed successfully') - } catch (error) { - console.error('[KEYCLOAK] Login failed:', error) - - if (error instanceof Error) { - console.error('[KEYCLOAK] Error details:', { - message: error.message, - stack: error.stack, - name: error.name - }) - } else { - console.error('[KEYCLOAK] Unknown error type:', error) - } - - throw error - } - } else { - console.error('[KEYCLOAK] No keycloak instance available for login') - throw new Error('Keycloak not available') - } - } - - const logout = async () => { - if (keycloak.value) { - try { - await keycloak.value.logout({ - redirectUri: window.location.origin - }) - } catch (error) { - console.error('Logout failed:', error) - } - } - - // Clear local state - isAuthenticated.value = false - user.value = null - token.value = null - } - - const getToken = () => { - return token.value - } - - const hasRole = (role: string) => { - return user.value?.roles?.includes(role) || false - } - - const hasAnyRole = (roles: string[]) => { - return roles.some(role => hasRole(role)) - } - - return { - keycloak: readonly(keycloak), - isAuthenticated: readonly(isAuthenticated), - user: readonly(user), - token: readonly(token), - isInitialized: readonly(isInitialized), - initKeycloak, - login, - logout, - getToken, - hasRole, - hasAnyRole, - } -} diff --git a/composables/useUnifiedAuth.ts b/composables/useUnifiedAuth.ts index 6309401..6194d81 100644 --- a/composables/useUnifiedAuth.ts +++ b/composables/useUnifiedAuth.ts @@ -11,29 +11,27 @@ export const useUnifiedAuth = () => { // Get both auth systems const directusAuth = useDirectusAuth(); const directusUser = useDirectusUser(); - const keycloak = useKeycloak(); + const oidc = useOidcAuth(); // Create unified user object const user = computed(() => { - // Check Keycloak user first - if (keycloak.user?.value) { - const keycloakUser = keycloak.user.value; + // Check OIDC (Keycloak) user first + if (oidc.loggedIn?.value && oidc.user?.value) { + const oidcUser = oidc.user.value as any; // Type cast for flexibility + // Construct name from available fields - let name = keycloakUser.fullName; - if (!name && (keycloakUser.firstName || keycloakUser.lastName)) { - name = `${keycloakUser.firstName || ''} ${keycloakUser.lastName || ''}`.trim(); - } - if (!name) { - name = keycloakUser.username || keycloakUser.email; + let name = oidcUser.name || oidcUser.preferred_username || oidcUser.email || 'User'; + if (!name && (oidcUser.given_name || oidcUser.family_name)) { + name = `${oidcUser.given_name || ''} ${oidcUser.family_name || ''}`.trim(); } return { - id: keycloakUser.id, - email: keycloakUser.email, + id: oidcUser.sub || oidcUser.id || 'unknown', + email: oidcUser.email || '', name: name, tier: 'basic', // Could be enhanced with Keycloak attributes authSource: 'keycloak', - raw: keycloakUser + raw: oidcUser }; } @@ -55,8 +53,8 @@ export const useUnifiedAuth = () => { // Unified logout function const logout = async () => { if (user.value?.authSource === 'keycloak') { - // Keycloak logout - await keycloak.logout(); + // OIDC logout + await oidc.logout(); } else if (user.value?.authSource === 'directus') { // Directus logout await directusAuth.logout(); diff --git a/middleware/authentication.ts b/middleware/authentication.ts index 97b2db0..64c0af6 100644 --- a/middleware/authentication.ts +++ b/middleware/authentication.ts @@ -5,8 +5,12 @@ export default defineNuxtRouteMiddleware(async (to) => { // Check if auth is required (default true unless explicitly set to false) const isAuthRequired = to.meta.auth !== false; + if (!isAuthRequired) { + return; + } + try { - // Check Directus auth first (most reliable) + // Check Directus auth first const { fetchUser, setUser } = useDirectusAuth(); const directusUser = useDirectusUser(); @@ -15,10 +19,7 @@ export default defineNuxtRouteMiddleware(async (to) => { const user = await fetchUser(); setUser(user.value); } catch (error) { - // Ignore directus auth errors for public pages - if (!isAuthRequired) { - return; - } + // Directus auth failed, continue to check OIDC } } @@ -27,37 +28,19 @@ export default defineNuxtRouteMiddleware(async (to) => { return; } - // Check Keycloak auth if authentication is required - if (isAuthRequired) { - const keycloak = useKeycloak(); - - // Initialize Keycloak if not already done - if (!keycloak.isInitialized.value) { - try { - const authenticated = await keycloak.initKeycloak(); - if (authenticated) { - // User authenticated with Keycloak - return; - } - } catch (error) { - console.error('Keycloak initialization failed:', error); - // Continue to login redirect - } - } else if (keycloak.isAuthenticated.value) { - // User already authenticated with Keycloak - return; - } + // Check OIDC auth (Keycloak) + const { user: oidcUser, loggedIn } = useOidcAuth(); + + if (loggedIn.value && oidcUser.value) { + // User authenticated with Keycloak via OIDC + return; } - // No authentication found - if (isAuthRequired) { - // Redirect to login page - return navigateTo('/login'); - } + // No authentication found, redirect to login + return navigateTo('/login'); + } catch (error) { console.error('Auth middleware error:', error); - if (isAuthRequired) { - return navigateTo('/login'); - } + return navigateTo('/login'); } }); diff --git a/nuxt.config.ts b/nuxt.config.ts index 48e4aa6..ccbe9c8 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -2,7 +2,7 @@ export default defineNuxtConfig({ ssr: false, compatibilityDate: "2024-11-01", devtools: { enabled: true }, - modules: ["nuxt-directus", "vuetify-nuxt-module", "@vite-pwa/nuxt"], + modules: ["nuxt-directus", "nuxt-oidc-auth", "vuetify-nuxt-module", "@vite-pwa/nuxt"], app: { head: { titleTemplate: "%s • Port Nimara Portal", @@ -111,6 +111,21 @@ export default defineNuxtConfig({ wasm: true } }, + oidc: { + providers: { + keycloak: { + audience: 'account', + baseUrl: 'https://auth.portnimara.dev/realms/client-portal', + clientId: 'client-portal', + clientSecret: '', // Will be injected via environment variable + redirectUri: 'https://client.portnimara.dev/auth/keycloak/callback', + userNameClaim: 'preferred_username', + logoutRedirectUri: 'https://client.portnimara.dev', + validateAccessToken: false, // Disable for Keycloak compatibility + exposeIdToken: true, + } + } + }, runtimeConfig: { nocodb: { url: "", @@ -128,15 +143,6 @@ export default defineNuxtConfig({ directus: { url: "https://cms.portnimara.dev", }, - keycloak: { - url: "https://auth.portnimara.dev", - realm: "client-portal", - clientId: "client-portal", - }, - // Force HTTPS base URL for production - baseUrl: "https://client.portnimara.dev", - // Enable debug mode for troubleshooting (temporarily enabled for production) - keycloakDebug: true, }, }, vuetify: { diff --git a/package-lock.json b/package-lock.json index a258f7c..73804fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "@vite-pwa/nuxt": "^0.10.6", "formidable": "^3.5.4", "imap": "^0.8.19", - "keycloak-js": "^26.2.0", "libphonenumber-js": "^1.12.9", "lodash-es": "^4.17.21", "mailparser": "^3.7.3", @@ -21,6 +20,7 @@ "nodemailer": "^7.0.3", "nuxt": "^3.15.4", "nuxt-directus": "^5.7.0", + "nuxt-oidc-auth": "^1.0.0-beta.5", "v-phone-input": "^4.4.2", "vue": "latest", "vue-router": "latest", @@ -47,6 +47,31 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/install-pkg/node_modules/package-manager-detector": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", + "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", + "license": "MIT" + }, + "node_modules/@antfu/install-pkg/node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "license": "MIT" + }, "node_modules/@antfu/utils": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", @@ -2053,6 +2078,119 @@ "node": ">=18" } }, + "node_modules/@iconify-json/carbon": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@iconify-json/carbon/-/carbon-1.2.9.tgz", + "integrity": "sha512-RyeSAuFTzhs1GX4yrzVEKEbNQGt95p9zMR4S2F63vbThtNoUr5OKwaWbhO/GbHQCSgdbKuZv2ApAOsY2fLxLbQ==", + "license": "Apache-2.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify-json/logos": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.2.4.tgz", + "integrity": "sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==", + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify-json/ri": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@iconify-json/ri/-/ri-1.2.5.tgz", + "integrity": "sha512-kWGimOXMZrlYusjBKKXYOWcKhbOHusFsmrmRGmjS7rH0BpML5A9/fy8KHZqFOwZfC4M6amObQYbh8BqO5cMC3w==", + "license": "Apache-2.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify-json/tabler": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@iconify-json/tabler/-/tabler-1.2.19.tgz", + "integrity": "sha512-JDeQTQxHD8KE12pAbPVHX1WFVOPq8D0XfRb/LwYHwGwYE0HP9OIjJ//TKxS1Gt++RirYu6Xsx+Jm5LA5KbykoA==", + "license": "MIT", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.14.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" + } + }, + "node_modules/@iconify/utils/node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@iconify/utils/node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT" + }, + "node_modules/@iconify/utils/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@iconify/utils/node_modules/local-pkg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", + "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.0.1", + "quansync": "^0.2.8" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@iconify/utils/node_modules/pkg-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz", + "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.1", + "exsolve": "^1.0.1", + "pathe": "^2.0.3" + } + }, "node_modules/@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", @@ -2543,6 +2681,37 @@ "vite": "*" } }, + "node_modules/@nuxt/devtools-ui-kit": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@nuxt/devtools-ui-kit/-/devtools-ui-kit-1.7.0.tgz", + "integrity": "sha512-pYjwCP3FHz/rrEoJpb8plMinnPHRh+fFc90O+MncMC0aIGydtu4SGwAE3fZsg//JXqkvlY+JyozxqtF9IRA7rA==", + "license": "MIT", + "dependencies": { + "@iconify-json/carbon": "^1.2.5", + "@iconify-json/logos": "^1.2.4", + "@iconify-json/ri": "^1.2.5", + "@iconify-json/tabler": "^1.2.13", + "@nuxt/devtools-kit": "1.7.0", + "@nuxt/kit": "^3.15.0", + "@unocss/core": "^0.65.3", + "@unocss/nuxt": "^0.65.3", + "@unocss/preset-attributify": "^0.65.3", + "@unocss/preset-icons": "^0.65.3", + "@unocss/preset-mini": "^0.65.3", + "@unocss/reset": "^0.65.3", + "@vueuse/core": "^12.2.0", + "@vueuse/integrations": "^12.2.0", + "@vueuse/nuxt": "^12.2.0", + "defu": "^6.1.4", + "focus-trap": "^7.6.2", + "splitpanes": "^3.1.5", + "unocss": "^0.65.3", + "v-lazy-show": "^0.3.0" + }, + "peerDependencies": { + "@nuxt/devtools": "1.7.0" + } + }, "node_modules/@nuxt/devtools-wizard": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@nuxt/devtools-wizard/-/devtools-wizard-1.7.0.tgz", @@ -3693,6 +3862,28 @@ "node": ">=10.13.0" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -3728,6 +3919,13 @@ "@types/node": "*" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT", + "peer": true + }, "node_modules/@types/lodash": { "version": "4.17.17", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz", @@ -3804,6 +4002,12 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, "node_modules/@unhead/dom": { "version": "1.11.18", "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.11.18.tgz", @@ -3874,6 +4078,636 @@ "vue": ">=2.7 || >=3" } }, + "node_modules/@unocss/astro": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/astro/-/astro-0.65.4.tgz", + "integrity": "sha512-ex1CJOQ6yeftBEPcbA9/W47/YoV+mhQnrAoc8MA1VVrvvFKDitICFU62+nSt3NWRe53XL/fXnQbcbCb8AAgKlA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "@unocss/reset": "0.65.4", + "@unocss/vite": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/@unocss/cli": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/cli/-/cli-0.65.4.tgz", + "integrity": "sha512-D/4hY5Hezh3QETscl4i+ojb+q8YU9Cl9AYJ8v3gsjc/GjTmEuIOD5V4x+/aN25vY5wjqgoApOgaIDGCV3b+2Ig==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@rollup/pluginutils": "^5.1.4", + "@unocss/config": "0.65.4", + "@unocss/core": "0.65.4", + "@unocss/preset-uno": "0.65.4", + "cac": "^6.7.14", + "chokidar": "^3.6.0", + "colorette": "^2.0.20", + "consola": "^3.3.1", + "magic-string": "^0.30.17", + "pathe": "^1.1.2", + "perfect-debounce": "^1.0.0", + "tinyglobby": "^0.2.10" + }, + "bin": { + "unocss": "bin/unocss.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/cli/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@unocss/cli/node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/@unocss/cli/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "license": "MIT" + }, + "node_modules/@unocss/cli/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@unocss/cli/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@unocss/config": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/config/-/config-0.65.4.tgz", + "integrity": "sha512-/vCt4AXnJ4p4Ow6xqsYwdrelF9533yhZjzkg3SQmL3rKeSkicPayKpeq8nkYECdhDI03VTCVD+6oh5Y/26Hg7A==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "unconfig": "~0.6.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/config/node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/config/node_modules/importx": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/importx/-/importx-0.5.2.tgz", + "integrity": "sha512-YEwlK86Ml5WiTxN/ECUYC5U7jd1CisAVw7ya4i9ZppBoHfFkT2+hChhr3PE2fYxUKLkNyivxEQpa5Ruil1LJBQ==", + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "debug": "^4.4.0", + "esbuild": "^0.20.2 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", + "jiti": "^2.4.2", + "pathe": "^2.0.3", + "tsx": "^4.19.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/config/node_modules/unconfig": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/unconfig/-/unconfig-0.6.1.tgz", + "integrity": "sha512-cVU+/sPloZqOyJEAfNwnQSFCzFrZm85vcVkryH7lnlB/PiTycUkAjt5Ds79cfIshGOZ+M5v3PBDnKgpmlE5DtA==", + "license": "MIT", + "dependencies": { + "@antfu/utils": "^8.1.0", + "defu": "^6.1.4", + "importx": "^0.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/core": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/core/-/core-0.65.4.tgz", + "integrity": "sha512-a2JOoFutrhqd5RgPhIR5FIXrDoHDU3gwCbPrpT6KYTjsqlSc/fv02yZ+JGOZFN3MCFhCmaPTs+idDFtwb3xU8g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/extractor-arbitrary-variants": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/extractor-arbitrary-variants/-/extractor-arbitrary-variants-0.65.4.tgz", + "integrity": "sha512-GbvTgsDaHplfWfsQtOY8RrvEZvptmvR9k9NwQ5NsZBNIG1JepYVel93CVQvsxT5KioKcoWngXxTYLNOGyxLs0g==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/inspector": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/inspector/-/inspector-0.65.4.tgz", + "integrity": "sha512-byg9x549Ul17U4Ety7ufDwC0UOygypoq4QnLEPzhlZ0KJG1f7WmXKYanOhupeg3h4qCj6Nc/xdZYMGbHl9QRIg==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "@unocss/rule-utils": "0.65.4", + "colorette": "^2.0.20", + "gzip-size": "^6.0.0", + "sirv": "^3.0.0", + "vue-flow-layout": "^0.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/inspector/node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/@unocss/inspector/node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@unocss/nuxt": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/nuxt/-/nuxt-0.65.4.tgz", + "integrity": "sha512-dEJdqgvrukgZJk1szxRW6MiIozUZDLeFyyxmnO+iW3loPlji9hu95j2KBVHaQWIzi39XqVSORi4lH5flvAz3Pg==", + "license": "MIT", + "dependencies": { + "@nuxt/kit": "^3.15.0", + "@unocss/config": "0.65.4", + "@unocss/core": "0.65.4", + "@unocss/preset-attributify": "0.65.4", + "@unocss/preset-icons": "0.65.4", + "@unocss/preset-tagify": "0.65.4", + "@unocss/preset-typography": "0.65.4", + "@unocss/preset-uno": "0.65.4", + "@unocss/preset-web-fonts": "0.65.4", + "@unocss/preset-wind": "0.65.4", + "@unocss/reset": "0.65.4", + "@unocss/vite": "0.65.4", + "@unocss/webpack": "0.65.4", + "unocss": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/postcss": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/postcss/-/postcss-0.65.4.tgz", + "integrity": "sha512-8peDRo0+rNQsnKh/H2uZEVy67sV2cC16rAeSLpgbVJUMNfZlmF0rC2DNGsOV17uconUXSwz7+mGcHKNiv+8YlQ==", + "license": "MIT", + "dependencies": { + "@unocss/config": "0.65.4", + "@unocss/core": "0.65.4", + "@unocss/rule-utils": "0.65.4", + "css-tree": "^3.1.0", + "postcss": "^8.4.49", + "tinyglobby": "^0.2.10" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/@unocss/postcss/node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@unocss/postcss/node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "license": "CC0-1.0" + }, + "node_modules/@unocss/preset-attributify": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-attributify/-/preset-attributify-0.65.4.tgz", + "integrity": "sha512-zxE9hJJ5b37phjdzDdZsxX559ZlmH9rFlY5LVEcQySTnsfY0znviHxPbD2iRpCBCRd+YC5HfFd2jb3XlnTKMJQ==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-icons": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-icons/-/preset-icons-0.65.4.tgz", + "integrity": "sha512-5sSzTN72X2Ag3VH48xY1pYudeWnql9jqdMiwgZuLJcmvETBNGelXy2wGxm7tsUUEx/l40Yr04Ck8XRPGT9jLBw==", + "license": "MIT", + "dependencies": { + "@iconify/utils": "^2.2.1", + "@unocss/core": "0.65.4", + "ofetch": "^1.4.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-mini": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-mini/-/preset-mini-0.65.4.tgz", + "integrity": "sha512-dcO2PzSl87qN1KdQWcfZDIKEhpdFeImWbYfiXtE7k6pi1393FJkdHEopgI/1ZciIQN1CkTvQJ5c7EpEVWftYRA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "@unocss/extractor-arbitrary-variants": "0.65.4", + "@unocss/rule-utils": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-tagify": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-tagify/-/preset-tagify-0.65.4.tgz", + "integrity": "sha512-qll6koqdFEkvmz594vKnxj9+3nfM3ugkJxYHrTkqtwx7DAnTgtM8fInFFGZelvjwUzR3o3+Zw6uMhFkLTVTfvg==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-typography": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-typography/-/preset-typography-0.65.4.tgz", + "integrity": "sha512-Dl940ATrviWD9Vh+4fcN0QZXb6wA7al+c7QkdVAzW7I+NtdN2ELvLcN0cY22KnLRpwztzmg52Qp2J/1QnqrLTw==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "@unocss/preset-mini": "0.65.4" + } + }, + "node_modules/@unocss/preset-uno": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-uno/-/preset-uno-0.65.4.tgz", + "integrity": "sha512-56bdBtf476i+soQCQmT36uGzcF2z+7DGCnG1hwWiw6XAbL6gmRMQsubwi1c8z8TcTQNBsOFUnOziFil0gbWufw==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "@unocss/preset-mini": "0.65.4", + "@unocss/preset-wind": "0.65.4", + "@unocss/rule-utils": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-web-fonts": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-web-fonts/-/preset-web-fonts-0.65.4.tgz", + "integrity": "sha512-UB/MvXHUTqMNVH1bbiKZ/ZtZUI5tsYlTYAvBrnXPO1Cztuwr8hJKSi4RCfI9g+YYtKHX4uYuxUbW5bcN85gmBQ==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "ofetch": "^1.4.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-wind": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/preset-wind/-/preset-wind-0.65.4.tgz", + "integrity": "sha512-0rbNbw5E8Lvh2yf4R1Mq+lxI/wL5Tm6+r+crE0uAAhCPe9kxPHW4k+x1cWKDIwq6Vudlm3cNX85N49wN5tYgdA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "@unocss/preset-mini": "0.65.4", + "@unocss/rule-utils": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/reset": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/reset/-/reset-0.65.4.tgz", + "integrity": "sha512-m685H0KFvVMz6R2i5GDIFv4RS9Z7y2G8hJK7xg2OWli+7w8l2ZMihYvXKofPsst4q/ms8EgKXpWc/qqUOTucvA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/rule-utils": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/rule-utils/-/rule-utils-0.65.4.tgz", + "integrity": "sha512-+EzdJEWcqGcO6HwbBTe7vEdBRpuKkBiz4MycQeLD6GEio04T45y6VHHO7/WTqxltbO4YwwW9/s2TKRMxKtoG8g==", + "license": "MIT", + "dependencies": { + "@unocss/core": "^0.65.4", + "magic-string": "^0.30.17" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-attributify-jsx": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/transformer-attributify-jsx/-/transformer-attributify-jsx-0.65.4.tgz", + "integrity": "sha512-n438EzWdTKlLCOlAUSpFjmH6FflctqzIReMzMZSJDkmkorymc+C5GpjN3Nty2cKRJXIl6Vwq0oxPuB59RT+FIw==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-compile-class": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/transformer-compile-class/-/transformer-compile-class-0.65.4.tgz", + "integrity": "sha512-n1yHDC/iIbcj/9fBUTXkSoASKfLBuRoCN7P1a0ecPc8Gu+uOGfoxafOhrlqC+tpD3hlQGoL+0h74BHSKh+L23Q==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-directives": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/transformer-directives/-/transformer-directives-0.65.4.tgz", + "integrity": "sha512-zkoDEwzPkgXi6ohW7P11gbArwfTRMZ9knYSUYoPEltQz+UZYzeRQ85exiAmdz5MsbCAuhQEr577Kd/CWfhjEuA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4", + "@unocss/rule-utils": "0.65.4", + "css-tree": "^3.1.0" + } + }, + "node_modules/@unocss/transformer-directives/node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@unocss/transformer-directives/node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "license": "CC0-1.0" + }, + "node_modules/@unocss/transformer-variant-group": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/transformer-variant-group/-/transformer-variant-group-0.65.4.tgz", + "integrity": "sha512-ggO6xMGeOeoD5GHS2xXBJrYFuzqyiZ25tM0zHAMJn9QU9GIu1NwWvcXluvLCF/MRIygBJGPpAE98aEICI6ifEA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "0.65.4" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/vite": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/vite/-/vite-0.65.4.tgz", + "integrity": "sha512-02pRcVLfb5UUxMJwudnjS/0ZQdSlskjuXVHdpZpLBZCA8hhoru2uEOsPbUOBRNNMjDj6ld00pmgk/+im07M35Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@rollup/pluginutils": "^5.1.4", + "@unocss/config": "0.65.4", + "@unocss/core": "0.65.4", + "@unocss/inspector": "0.65.4", + "chokidar": "^3.6.0", + "magic-string": "^0.30.17", + "tinyglobby": "^0.2.10" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0" + } + }, + "node_modules/@unocss/vite/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@unocss/vite/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@unocss/vite/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@unocss/webpack": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/@unocss/webpack/-/webpack-0.65.4.tgz", + "integrity": "sha512-cnd0qnJdSxYlQ+zuF0Qad3xZk2X0/p70XLzlA4TaBZuKa2OPJOyulfJwJSqcrSc4PXYOd9B/B8nXJk8WQ1yBHQ==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@rollup/pluginutils": "^5.1.4", + "@unocss/config": "0.65.4", + "@unocss/core": "0.65.4", + "chokidar": "^3.6.0", + "magic-string": "^0.30.17", + "tinyglobby": "^0.2.10", + "unplugin": "^2.1.0", + "webpack-sources": "^3.2.3" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "webpack": "^4 || ^5" + } + }, + "node_modules/@unocss/webpack/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@unocss/webpack/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@unocss/webpack/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/@vercel/nft": { "version": "0.27.10", "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.27.10.tgz", @@ -4233,6 +5067,336 @@ "vuetify": "^3.0.0" } }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/nuxt": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/nuxt/-/nuxt-12.8.2.tgz", + "integrity": "sha512-jDsMli+MmxlhzaMwu8a2varKlkiBTPCdb+I457F7bTb1GazC6HDbGbLmhkpVQ8bNA1FzqfhwhAsOEsESF7wOkw==", + "license": "MIT", + "dependencies": { + "@nuxt/kit": "^3.15.4", + "@vueuse/core": "12.8.2", + "@vueuse/metadata": "12.8.2", + "local-pkg": "^1.1.1", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "nuxt": "^3.0.0 || ^4.0.0-0" + } + }, + "node_modules/@vueuse/nuxt/node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT" + }, + "node_modules/@vueuse/nuxt/node_modules/local-pkg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", + "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.0.1", + "quansync": "^0.2.8" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/nuxt/node_modules/pkg-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz", + "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.1", + "exsolve": "^1.0.1", + "pathe": "^2.0.3" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/@zxing/text-encoding": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", @@ -4307,6 +5471,37 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -5119,6 +6314,16 @@ "node": ">=10" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -6322,6 +7527,53 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -6396,6 +7648,12 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exsolve": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", + "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "license": "MIT" + }, "node_modules/externality": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/externality/-/externality-1.0.2.tgz", @@ -6584,6 +7842,15 @@ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "license": "ISC" }, + "node_modules/focus-trap": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz", + "integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==", + "license": "MIT", + "dependencies": { + "tabbable": "^6.2.0" + } + }, "node_modules/for-each": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", @@ -6965,6 +8232,13 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause", + "peer": true + }, "node_modules/global-directory": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", @@ -8552,6 +9826,37 @@ "node": ">=10" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", @@ -8571,6 +9876,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", @@ -8610,6 +9924,13 @@ "node": ">=6" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT", + "peer": true + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -8655,12 +9976,6 @@ "node": ">=0.10.0" } }, - "node_modules/keycloak-js": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.2.0.tgz", - "integrity": "sha512-CrFcXTN+d6J0V/1v3Zpioys6qHNWE6yUzVVIsCUAmFn9H14GZ0vuYod+lt+SSpMgWGPuneDZBSGBAeLBFuqjsw==", - "license": "Apache-2.0" - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -8857,6 +10172,16 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, "node_modules/local-pkg": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", @@ -9261,6 +10586,13 @@ "integrity": "sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ==", "license": "MIT" }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT", + "peer": true + }, "node_modules/nitropack": { "version": "2.10.4", "resolved": "https://registry.npmjs.org/nitropack/-/nitropack-2.10.4.tgz", @@ -9729,6 +11061,35 @@ "@nuxt/kit": "^3.0.0" } }, + "node_modules/nuxt-oidc-auth": { + "version": "1.0.0-beta.5", + "resolved": "https://registry.npmjs.org/nuxt-oidc-auth/-/nuxt-oidc-auth-1.0.0-beta.5.tgz", + "integrity": "sha512-/bZ3yBfKmT40Kk66RZ9757SGMvqcZqLYRX6HK3l2o4N1dPvz1gW50t6cZ6UFJ63G7FWdHUuoRyo2INEpvbXe4g==", + "license": "MIT", + "dependencies": { + "@nuxt/devtools-kit": "^1.7.0", + "@nuxt/devtools-ui-kit": "^1.7.0", + "consola": "^3.4.0", + "defu": "^6.1.4", + "h3": "^1.13.1", + "jose": "^5.9.6", + "ofetch": "^1.4.1", + "scule": "^1.3.0", + "sirv": "^3.0.0", + "ufo": "^1.5.4", + "uncrypto": "^0.1.3", + "undici": "^7.2.3", + "undio": "^0.2.0" + }, + "peerDependencies": { + "undici": "^7.2.1" + }, + "peerDependenciesMeta": { + "undici": { + "optional": true + } + } + }, "node_modules/nypm": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.5.2.tgz", @@ -10084,9 +11445,9 @@ } }, "node_modules/pathe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", - "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, "node_modules/peberminta": { @@ -10743,6 +12104,22 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", + "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/query-string": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", @@ -11356,6 +12733,26 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "license": "ISC" }, + "node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/scule": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", @@ -11757,6 +13154,18 @@ "node": ">=6" } }, + "node_modules/splitpanes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-3.2.0.tgz", + "integrity": "sha512-K+WKxWdqtKShV33gPjQl769wHxB3glypTOReCvYu/AJd38J+abHlpiF8rK6uBNPMrgw5thHZCI5JkEwsAqa9XA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antoniandre" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -12133,6 +13542,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -12245,6 +13660,41 @@ "node": ">=10" } }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -12963,12 +14413,27 @@ "unplugin": "^2.1.0" } }, + "node_modules/undici": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz", + "integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, + "node_modules/undio": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/undio/-/undio-0.2.0.tgz", + "integrity": "sha512-1LH824ipsUNqX1qsO6qpcusv0oGPlfFWVykwWq5jJB0Mq6x4kEHO/izSq2KLjGZvOosEd91+HXoxYUSoVI0zPg==", + "license": "MIT" + }, "node_modules/unenv": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.10.0.tgz", @@ -13129,6 +14594,49 @@ "node": ">= 10.0.0" } }, + "node_modules/unocss": { + "version": "0.65.4", + "resolved": "https://registry.npmjs.org/unocss/-/unocss-0.65.4.tgz", + "integrity": "sha512-KUCW5OzI20Ik6j1zXkkrpWhxZ59TwSKl6+DvmYHEzMfaEcrHlBZaFSApAoSt2CYSvo6SluGiKyr+Im1UTkd4KA==", + "license": "MIT", + "dependencies": { + "@unocss/astro": "0.65.4", + "@unocss/cli": "0.65.4", + "@unocss/core": "0.65.4", + "@unocss/postcss": "0.65.4", + "@unocss/preset-attributify": "0.65.4", + "@unocss/preset-icons": "0.65.4", + "@unocss/preset-mini": "0.65.4", + "@unocss/preset-tagify": "0.65.4", + "@unocss/preset-typography": "0.65.4", + "@unocss/preset-uno": "0.65.4", + "@unocss/preset-web-fonts": "0.65.4", + "@unocss/preset-wind": "0.65.4", + "@unocss/transformer-attributify-jsx": "0.65.4", + "@unocss/transformer-compile-class": "0.65.4", + "@unocss/transformer-directives": "0.65.4", + "@unocss/transformer-variant-group": "0.65.4", + "@unocss/vite": "0.65.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@unocss/webpack": "0.65.4", + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0" + }, + "peerDependenciesMeta": { + "@unocss/webpack": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, "node_modules/unplugin": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.1.2.tgz", @@ -13548,6 +15056,18 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/v-lazy-show": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/v-lazy-show/-/v-lazy-show-0.3.0.tgz", + "integrity": "sha512-xpVALnvzB+RoDkI/5gqzVC2bL/Mh0Mw5/cPpSWJTTS6K4yDwFE2hZr5OsgFS74c6IHV6/k0jzSkAFXJttnhufg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/compiler-core": "^3.5" + } + }, "node_modules/v-phone-input": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/v-phone-input/-/v-phone-input-4.4.2.tgz", @@ -14038,6 +15558,15 @@ "integrity": "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==", "license": "MIT" }, + "node_modules/vue-flow-layout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/vue-flow-layout/-/vue-flow-layout-0.1.1.tgz", + "integrity": "sha512-JdgRRUVrN0Y2GosA0M68DEbKlXMqJ7FQgsK8CjQD2vxvNSqAU6PZEpi4cfcTVtfM2GVOMjHo7GKKLbXxOBqDqA==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.4.37" + } + }, "node_modules/vue-router": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", @@ -14108,6 +15637,20 @@ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "license": "MIT" }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "license": "MIT", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/web-encoding": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", @@ -14126,12 +15669,92 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, + "node_modules/webpack": { + "version": "5.99.9", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", + "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", "license": "MIT" }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index 80eea71..3c0506d 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "@vite-pwa/nuxt": "^0.10.6", "formidable": "^3.5.4", "imap": "^0.8.19", - "keycloak-js": "^26.2.0", "libphonenumber-js": "^1.12.9", "lodash-es": "^4.17.21", "mailparser": "^3.7.3", @@ -23,6 +22,7 @@ "nodemailer": "^7.0.3", "nuxt": "^3.15.4", "nuxt-directus": "^5.7.0", + "nuxt-oidc-auth": "^1.0.0-beta.5", "v-phone-input": "^4.4.2", "vue": "latest", "vue-router": "latest", diff --git a/pages/auth/callback.vue b/pages/auth/callback.vue deleted file mode 100644 index 321d7c5..0000000 --- a/pages/auth/callback.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - diff --git a/pages/login.vue b/pages/login.vue index 9453a07..d439687 100644 --- a/pages/login.vue +++ b/pages/login.vue @@ -113,10 +113,10 @@ definePageMeta({ }); // Directus auth -const { login } = useDirectusAuth(); +const { login: directusLogin } = useDirectusAuth(); -// Keycloak auth -const keycloak = useKeycloak(); +// OIDC auth (Keycloak) +const { login: oidcLogin } = useOidcAuth(); const loading = ref(false); const keycloakLoading = ref(false); @@ -128,24 +128,17 @@ const passwordVisible = ref(false); const valid = ref(false); -// Keycloak login function with proper error handling +// SSO login function using nuxt-oidc-auth const loginWithKeycloak = async () => { try { keycloakLoading.value = true; + console.log('[LOGIN] Starting SSO authentication via nuxt-oidc-auth...'); - console.log('[LOGIN] Starting Keycloak authentication...'); - - // Initialize Keycloak first if needed - if (!keycloak.isInitialized.value) { - console.log('[LOGIN] Initializing Keycloak...'); - await keycloak.initKeycloak(); - } - - // Perform login - await keycloak.login(); + // nuxt-oidc-auth handles everything server-side, no CORS issues! + await oidcLogin('keycloak'); } catch (error) { - console.error('[LOGIN] Keycloak login error:', error); + console.error('[LOGIN] SSO login error:', error); const toast = useToast(); toast.error('SSO login failed. Please try again or use email/password login.'); @@ -159,7 +152,7 @@ const loginWithKeycloak = async () => { const submit = async () => { try { loading.value = true; - await login({ email: emailAddress.value, password: password.value }); + await directusLogin({ email: emailAddress.value, password: password.value }); return navigateTo("/dashboard"); } catch (error) { errorThrown.value = true; diff --git a/server/api/debug/oidc-config.ts b/server/api/debug/oidc-config.ts deleted file mode 100644 index e117c36..0000000 --- a/server/api/debug/oidc-config.ts +++ /dev/null @@ -1,30 +0,0 @@ -export default defineEventHandler((event) => { - const config = useRuntimeConfig() - - // Return the OIDC configuration (without showing the actual secret) - return { - // Runtime config - runtime: { - issuer: config.openidConnect?.op?.issuer || 'NOT_SET', - clientId: config.openidConnect?.op?.clientId || 'NOT_SET', - clientSecret: config.openidConnect?.op?.clientSecret ? '***SET***' : 'NOT_SET', - secretLength: config.openidConnect?.op?.clientSecret?.length || 0, - }, - // Build-time config (what the module actually uses) - buildTime: { - issuer: process.env.KEYCLOAK_ISSUER || 'NOT_SET', - clientId: process.env.KEYCLOAK_CLIENT_ID || 'NOT_SET', - clientSecret: process.env.KEYCLOAK_CLIENT_SECRET ? '***SET***' : 'NOT_SET', - secretLength: process.env.KEYCLOAK_CLIENT_SECRET?.length || 0, - }, - environment: process.env.NODE_ENV, - envVars: { - KEYCLOAK_ISSUER: process.env.KEYCLOAK_ISSUER ? '***SET***' : 'NOT_SET', - KEYCLOAK_CLIENT_ID: process.env.KEYCLOAK_CLIENT_ID ? '***SET***' : 'NOT_SET', - KEYCLOAK_CLIENT_SECRET: process.env.KEYCLOAK_CLIENT_SECRET ? '***SET***' : 'NOT_SET', - OIDC_SESSION_SECRET: process.env.OIDC_SESSION_SECRET ? '***SET***' : 'NOT_SET', - OIDC_ENCRYPT_KEY: process.env.OIDC_ENCRYPT_KEY ? '***SET***' : 'NOT_SET', - OIDC_ENCRYPT_IV: process.env.OIDC_ENCRYPT_IV ? '***SET***' : 'NOT_SET', - } - } -}) diff --git a/server/api/debug/test-token-exchange.ts b/server/api/debug/test-token-exchange.ts deleted file mode 100644 index fafa4f5..0000000 --- a/server/api/debug/test-token-exchange.ts +++ /dev/null @@ -1,37 +0,0 @@ -export default defineEventHandler(async (event) => { - // Test the actual token exchange that's failing - const config = useRuntimeConfig() - - const testCode = "test-code-123" // We won't use this for real exchange, just test setup - - const tokenRequest = { - grant_type: 'authorization_code', - client_id: process.env.KEYCLOAK_CLIENT_ID, - client_secret: process.env.KEYCLOAK_CLIENT_SECRET, - code: testCode, - redirect_uri: 'https://client.portnimara.dev/oidc/cbt', - } - - return { - message: "Token exchange test configuration", - issuer: process.env.KEYCLOAK_ISSUER, - tokenEndpoint: `${process.env.KEYCLOAK_ISSUER}/protocol/openid-connect/token`, - clientId: process.env.KEYCLOAK_CLIENT_ID, - clientSecretLength: process.env.KEYCLOAK_CLIENT_SECRET?.length || 0, - redirectUri: 'https://client.portnimara.dev/oidc/cbt', - requestPayload: { - grant_type: tokenRequest.grant_type, - client_id: tokenRequest.client_id, - client_secret: tokenRequest.client_secret ? '***MASKED***' : 'NOT_SET', - code: 'test-code-will-be-replaced', - redirect_uri: tokenRequest.redirect_uri, - }, - // Test the actual HTTP vs HTTPS issue - environment: { - NODE_ENV: process.env.NODE_ENV, - headers: getHeaders(event), - host: getHeader(event, 'host'), - protocol: getHeader(event, 'x-forwarded-proto') || 'http', - } - } -}) diff --git a/server/utils/keycloak-oauth.ts b/server/utils/keycloak-oauth.ts deleted file mode 100644 index 9184731..0000000 --- a/server/utils/keycloak-oauth.ts +++ /dev/null @@ -1,326 +0,0 @@ -import { H3Event, getHeader, getCookie, setCookie } from 'h3' -import crypto from 'crypto' - -interface OAuthConfig { - issuer: string - clientId: string - clientSecret: string - scope: string[] -} - -interface PKCEChallenge { - codeVerifier: string - codeChallenge: string - state: string -} - -interface TokenResponse { - access_token: string - refresh_token: string - id_token: string - token_type: string - expires_in: number -} - -interface KeycloakUser { - sub: string - email: string - preferred_username: string - given_name?: string - family_name?: string - name?: string - realm_access?: { - roles: string[] - } -} - -/** - * Smart base URL detection that handles proxy environments - */ -export function getBaseUrl(event: H3Event): string { - // Check for proxy headers first (nginx/reverse proxy) - const forwardedProto = getHeader(event, 'x-forwarded-proto') - const forwardedHost = getHeader(event, 'x-forwarded-host') - - if (forwardedProto && forwardedHost) { - return `${forwardedProto}://${forwardedHost}` - } - - // Fallback to host header - const host = getHeader(event, 'host') - - // Force HTTPS in production - const proto = process.env.NODE_ENV === 'production' ? 'https' : 'http' - - return `${proto}://${host}` -} - -/** - * Get OAuth configuration from environment - */ -export function getOAuthConfig(): OAuthConfig { - const config = useRuntimeConfig() - - return { - issuer: config.public.keycloak.url + '/realms/' + config.public.keycloak.realm, - clientId: config.public.keycloak.clientId, - clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!, - scope: ['openid', 'email', 'profile'] - } -} - -/** - * Generate PKCE challenge for enhanced security - */ -export async function generatePKCEChallenge(): Promise { - // Generate code verifier (random string) - const codeVerifier = generateRandomString(128) - - // Create code challenge (SHA256 hash of verifier, base64url encoded) - const encoder = new TextEncoder() - const data = encoder.encode(codeVerifier) - const digest = await crypto.subtle.digest('SHA-256', data) - const codeChallenge = base64urlEncode(new Uint8Array(digest)) - - // Generate state parameter for CSRF protection - const state = generateRandomString(32) - - return { - codeVerifier, - codeChallenge, - state - } -} - -/** - * Generate Keycloak authorization URL - */ -export async function generateAuthUrl(event: H3Event): Promise { - const config = getOAuthConfig() - const baseUrl = getBaseUrl(event) - const pkce = await generatePKCEChallenge() - - // Store PKCE challenge and state in encrypted session - const sessionData = encrypt(JSON.stringify(pkce)) - setCookie(event, 'oauth_session', sessionData, { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', - maxAge: 600 // 10 minutes - }) - - // Build authorization URL - const authUrl = new URL(`${config.issuer}/protocol/openid-connect/auth`) - authUrl.searchParams.set('client_id', config.clientId) - authUrl.searchParams.set('redirect_uri', `${baseUrl}/api/auth/keycloak/callback`) - authUrl.searchParams.set('response_type', 'code') - authUrl.searchParams.set('scope', config.scope.join(' ')) - authUrl.searchParams.set('state', pkce.state) - authUrl.searchParams.set('code_challenge', pkce.codeChallenge) - authUrl.searchParams.set('code_challenge_method', 'S256') - - return authUrl.toString() -} - -/** - * Exchange authorization code for tokens - */ -export async function exchangeCodeForToken( - event: H3Event, - code: string, - state: string -): Promise<{ tokens: TokenResponse; user: KeycloakUser }> { - const config = getOAuthConfig() - const baseUrl = getBaseUrl(event) - - // Retrieve and validate session data - const sessionCookie = getCookie(event, 'oauth_session') - if (!sessionCookie) { - throw createError({ - statusCode: 400, - statusMessage: 'Missing OAuth session' - }) - } - - let sessionData: PKCEChallenge - try { - sessionData = JSON.parse(decrypt(sessionCookie)) - } catch (error) { - throw createError({ - statusCode: 400, - statusMessage: 'Invalid OAuth session' - }) - } - - // Validate state parameter (CSRF protection) - if (state !== sessionData.state) { - throw createError({ - statusCode: 400, - statusMessage: 'Invalid state parameter' - }) - } - - // Prepare token request - const tokenUrl = `${config.issuer}/protocol/openid-connect/token` - const tokenData = new URLSearchParams({ - grant_type: 'authorization_code', - client_id: config.clientId, - client_secret: config.clientSecret, - code: code, - redirect_uri: `${baseUrl}/api/auth/keycloak/callback`, - code_verifier: sessionData.codeVerifier - }) - - // Exchange code for tokens - const tokenResponse = await fetch(tokenUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'application/json' - }, - body: tokenData.toString() - }) - - if (!tokenResponse.ok) { - const errorText = await tokenResponse.text() - console.error('Token exchange failed:', errorText) - throw createError({ - statusCode: 400, - statusMessage: 'Token exchange failed' - }) - } - - const tokens: TokenResponse = await tokenResponse.json() - - // Decode user info from ID token - const user = decodeJWTPayload(tokens.id_token) - - // Clean up session cookie - setCookie(event, 'oauth_session', '', { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', - maxAge: 0 - }) - - return { tokens, user } -} - -/** - * Refresh access token using refresh token - */ -export async function refreshAccessToken(refreshToken: string): Promise { - const config = getOAuthConfig() - - const tokenUrl = `${config.issuer}/protocol/openid-connect/token` - const tokenData = new URLSearchParams({ - grant_type: 'refresh_token', - client_id: config.clientId, - client_secret: config.clientSecret, - refresh_token: refreshToken - }) - - const response = await fetch(tokenUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'application/json' - }, - body: tokenData.toString() - }) - - if (!response.ok) { - throw createError({ - statusCode: 401, - statusMessage: 'Token refresh failed' - }) - } - - return await response.json() -} - -/** - * Validate JWT token and extract payload - */ -export function decodeJWTPayload(token: string): T { - try { - const parts = token.split('.') - if (parts.length !== 3) { - throw new Error('Invalid JWT format') - } - - const payload = parts[1] - const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/'))) - - // Check if token is expired - if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) { - throw new Error('Token expired') - } - - return decoded - } catch (error) { - throw createError({ - statusCode: 401, - statusMessage: 'Invalid token' - }) - } -} - -/** - * Simple encryption for OAuth session data - */ -function encrypt(data: string): string { - const key = getEncryptionKey() - const iv = crypto.randomBytes(16) - const cipher = crypto.createCipher('aes-256-cbc', key) - cipher.setAutoPadding(true) - - let encrypted = cipher.update(data, 'utf8', 'base64') - encrypted += cipher.final('base64') - - return iv.toString('base64') + ':' + encrypted -} - -/** - * Simple decryption for OAuth session data - */ -function decrypt(encryptedData: string): string { - const key = getEncryptionKey() - const parts = encryptedData.split(':') - const iv = Buffer.from(parts[0], 'base64') - const encrypted = parts[1] - - const decipher = crypto.createDecipher('aes-256-cbc', key) - decipher.setAutoPadding(true) - - let decrypted = decipher.update(encrypted, 'base64', 'utf8') - decrypted += decipher.final('utf8') - - return decrypted -} - -/** - * Get encryption key from environment - */ -function getEncryptionKey(): string { - const key = process.env.OIDC_ENCRYPT_KEY || 'default-encrypt-key-change-in-prod' - return key.substring(0, 32).padEnd(32, '0') -} - -/** - * Generate cryptographically secure random string - */ -function generateRandomString(length: number): string { - const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~' - const array = new Uint8Array(length) - crypto.getRandomValues(array) - return Array.from(array, byte => charset[byte % charset.length]).join('') -} - -/** - * Base64URL encode (without padding) - */ -function base64urlEncode(buffer: Uint8Array): string { - const base64 = btoa(String.fromCharCode(...buffer)) - return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') -}