Refactor Indeehub integration and enhance deployment documentation
- Updated Indeehub references throughout the codebase, changing the name from "IndeedHub" to "Indeehub" for consistency. - Implemented a virtual app structure for Indeehub, allowing it to open an external URL without requiring a container. - Enhanced deployment scripts and documentation to clarify SSH access and password management for Indeehub. - Improved error handling and retry logic in various components to ensure better user experience during onboarding and app interactions. - Updated CSS for visual enhancements and added new buttons for improved navigation in the AppLauncherOverlay.
This commit is contained in:
@@ -643,8 +643,8 @@ function launchApp() {
|
||||
prod: 'http://localhost:8103' // Self-hosted splash screen
|
||||
},
|
||||
'indeedhub': {
|
||||
dev: 'http://localhost:7777',
|
||||
prod: 'http://localhost:7777' // Containerized indeehub prototype
|
||||
dev: 'https://archipelago.indeehub.studio',
|
||||
prod: 'https://archipelago.indeehub.studio'
|
||||
},
|
||||
// Dummy apps - replace with real URLs when packaged
|
||||
'bitcoin': {
|
||||
|
||||
@@ -245,8 +245,8 @@ function launchApp(id: string) {
|
||||
prod: 'http://localhost:8103' // Self-hosted splash screen
|
||||
},
|
||||
'indeedhub': {
|
||||
dev: 'http://localhost:7777',
|
||||
prod: 'http://localhost:7777' // Containerized indeehub prototype
|
||||
dev: 'https://archipelago.indeehub.studio',
|
||||
prod: 'https://archipelago.indeehub.studio'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -701,8 +701,14 @@ function getIconPath(iconName: string): string[] {
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
await store.logout()
|
||||
router.push('/login')
|
||||
try {
|
||||
await store.logout()
|
||||
} catch {
|
||||
/* proceed to login regardless */
|
||||
}
|
||||
router.push('/login').catch(() => {
|
||||
window.location.href = '/login'
|
||||
})
|
||||
}
|
||||
|
||||
// Track previous route for transition logic
|
||||
|
||||
@@ -136,7 +136,7 @@ import AnimatedLogo from '@/components/AnimatedLogo.vue'
|
||||
import { useAppStore } from '../stores/app'
|
||||
import { useLoginTransitionStore } from '../stores/loginTransition'
|
||||
import { rpcClient } from '../api/rpc-client'
|
||||
import { startSynthwave, stopSynthwave, playLoginSuccessWhoosh, playPop } from '@/composables/useLoginSounds'
|
||||
import { resumeAudioContext, startSynthwave, stopSynthwave, playLoginSuccessWhoosh, playPop } from '@/composables/useLoginSounds'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useAppStore()
|
||||
@@ -155,17 +155,25 @@ const isSetupMode = computed(() => {
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
if (sessionStorage.getItem('archipelago_from_splash') !== '1') {
|
||||
startSynthwave()
|
||||
} else {
|
||||
sessionStorage.removeItem('archipelago_from_splash')
|
||||
const fromSplash = sessionStorage.getItem('archipelago_from_splash') === '1'
|
||||
if (fromSplash) sessionStorage.removeItem('archipelago_from_splash')
|
||||
const unlock = () => {
|
||||
if (!fromSplash) {
|
||||
resumeAudioContext()
|
||||
startSynthwave()
|
||||
}
|
||||
document.removeEventListener('click', unlock)
|
||||
document.removeEventListener('touchstart', unlock)
|
||||
document.removeEventListener('keydown', unlock)
|
||||
}
|
||||
document.addEventListener('click', unlock, { once: true })
|
||||
document.addEventListener('touchstart', unlock, { once: true })
|
||||
document.addEventListener('keydown', unlock, { once: true })
|
||||
if (isSetupMode.value) {
|
||||
try {
|
||||
const result = await rpcClient.call<boolean>({ method: 'auth.isSetup', params: {} })
|
||||
const result = await rpcClient.call<boolean>({ method: 'auth.isSetup', params: {}, timeout: 8000 })
|
||||
isSetup.value = Boolean(result)
|
||||
} catch (err) {
|
||||
console.error('Failed to check setup status:', err)
|
||||
} catch {
|
||||
isSetup.value = false
|
||||
}
|
||||
} else {
|
||||
@@ -207,10 +215,17 @@ async function handleSetup() {
|
||||
loginTransition.setJustLoggedIn(true)
|
||||
await store.login(password.value)
|
||||
await new Promise(r => setTimeout(r, 520))
|
||||
router.replace({ name: 'home' })
|
||||
await router.replace({ name: 'home' }).catch(() => {
|
||||
window.location.href = '/dashboard'
|
||||
})
|
||||
} catch (err) {
|
||||
whooshAway.value = false
|
||||
error.value = err instanceof Error ? err.message : 'Setup failed. Please try again.'
|
||||
const msg = err instanceof Error ? err.message : ''
|
||||
if (/502|503|Bad Gateway|timeout|fetch|network/i.test(msg)) {
|
||||
error.value = 'Server is starting up. Please try again in a moment.'
|
||||
} else {
|
||||
error.value = msg || 'Setup failed. Please try again.'
|
||||
}
|
||||
startSynthwave()
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -237,10 +252,17 @@ async function handleLogin() {
|
||||
playLoginSuccessWhoosh()
|
||||
loginTransition.setJustLoggedIn(true)
|
||||
await new Promise(r => setTimeout(r, 520))
|
||||
router.replace({ name: 'home' })
|
||||
await router.replace({ name: 'home' }).catch(() => {
|
||||
window.location.href = '/dashboard'
|
||||
})
|
||||
} catch (err) {
|
||||
whooshAway.value = false
|
||||
error.value = err instanceof Error ? err.message : 'Login failed. Please check your password.'
|
||||
const msg = err instanceof Error ? err.message : ''
|
||||
if (/502|503|Bad Gateway|timeout|fetch|network/i.test(msg)) {
|
||||
error.value = 'Server is starting up. Please try again in a moment.'
|
||||
} else {
|
||||
error.value = msg || 'Login failed. Please check your password.'
|
||||
}
|
||||
startSynthwave()
|
||||
} finally {
|
||||
loading.value = false
|
||||
|
||||
@@ -851,6 +851,17 @@ function getCuratedAppList() {
|
||||
dockerImage: 'docker.io/fedimint/fedimintd:v0.10.0',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/fedimint/fedimint'
|
||||
},
|
||||
{
|
||||
id: 'indeedhub',
|
||||
title: 'Indeehub',
|
||||
version: '0.1.0',
|
||||
description: 'Bitcoin documentary streaming platform. Stream God Bless Bitcoin and other educational content about Bitcoin, sovereignty, and decentralized technology.',
|
||||
icon: 'https://indeehub.studio/favicon.ico',
|
||||
author: 'Indeehub Team',
|
||||
dockerImage: 'localhost/indeedhub:latest',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/indeedhub/indeedhub'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -139,11 +139,11 @@ async function downloadBackup() {
|
||||
}
|
||||
|
||||
function proceed() {
|
||||
router.push('/onboarding/verify')
|
||||
router.push('/onboarding/verify').catch(() => {})
|
||||
}
|
||||
|
||||
function skipForNow() {
|
||||
router.push('/onboarding/verify')
|
||||
router.push('/onboarding/verify').catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -101,30 +101,33 @@ async function generateDid() {
|
||||
isGenerating.value = true
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
const { did, pubkey } = await rpcClient.getNodeDid()
|
||||
generatedDid.value = did
|
||||
localStorage.setItem('neode_did', did)
|
||||
localStorage.setItem('neode_did_state', JSON.stringify({ did, kid: pubkey }))
|
||||
} catch (err) {
|
||||
errorMessage.value = err instanceof Error ? err.message : 'Failed to load node identity'
|
||||
// Fallback: show placeholder if backend unavailable (e.g. mock mode)
|
||||
if (!generatedDid.value) {
|
||||
generatedDid.value = 'did:key:z6Mk... (connect to server)'
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
try {
|
||||
const { did, pubkey } = await rpcClient.getNodeDid()
|
||||
generatedDid.value = did
|
||||
localStorage.setItem('neode_did', did)
|
||||
localStorage.setItem('neode_did_state', JSON.stringify({ did, kid: pubkey }))
|
||||
break
|
||||
} catch (err) {
|
||||
errorMessage.value = err instanceof Error ? err.message : 'Server unavailable. Retrying...'
|
||||
if (attempt < 2) {
|
||||
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)))
|
||||
} else {
|
||||
generatedDid.value = 'did:key:z6Mk... (connect to server)'
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isGenerating.value = false
|
||||
}
|
||||
isGenerating.value = false
|
||||
}
|
||||
|
||||
function proceed() {
|
||||
if (generatedDid.value && !generatedDid.value.includes('...')) {
|
||||
router.push('/onboarding/backup')
|
||||
router.push('/onboarding/backup').catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
function skipForNow() {
|
||||
router.push('/onboarding/backup')
|
||||
router.push('/onboarding/backup').catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
function goToLogin() {
|
||||
router.push('/login')
|
||||
router.push('/login').catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
function goToOptions() {
|
||||
router.push('/onboarding/path')
|
||||
router.push('/onboarding/path').catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -99,8 +99,12 @@ function selectOption(option: string) {
|
||||
|
||||
async function proceed() {
|
||||
if (selected.value) {
|
||||
await completeOnboarding()
|
||||
router.push('/login')
|
||||
try {
|
||||
await completeOnboarding()
|
||||
} catch {
|
||||
/* localStorage fallback ensures onboarding is marked complete */
|
||||
}
|
||||
router.push('/login').catch(() => {})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -139,15 +139,12 @@ function toggleOption(option: string) {
|
||||
}
|
||||
|
||||
function proceed() {
|
||||
// Save selected options to localStorage
|
||||
localStorage.setItem('neode_selected_paths', JSON.stringify(selectedOptions.value))
|
||||
// Don't mark onboarding complete yet - continue to DID creation
|
||||
router.push('/onboarding/did')
|
||||
router.push('/onboarding/did').catch(() => {})
|
||||
}
|
||||
|
||||
function skipForNow() {
|
||||
// Skip to DID creation
|
||||
router.push('/onboarding/did')
|
||||
router.push('/onboarding/did').catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -113,13 +113,21 @@ function generateMockSignature(): string {
|
||||
}
|
||||
|
||||
async function proceed() {
|
||||
await completeOnboarding()
|
||||
router.push('/login')
|
||||
try {
|
||||
await completeOnboarding()
|
||||
} catch {
|
||||
/* localStorage fallback ensures we can proceed */
|
||||
}
|
||||
router.push('/login').catch(() => {})
|
||||
}
|
||||
|
||||
async function skipForNow() {
|
||||
await completeOnboarding()
|
||||
router.push('/login')
|
||||
try {
|
||||
await completeOnboarding()
|
||||
} catch {
|
||||
/* localStorage fallback ensures we can proceed */
|
||||
}
|
||||
router.push('/login').catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -322,16 +322,18 @@ onMounted(() => {
|
||||
isTransitioning.value = false
|
||||
isGlitching.value = false
|
||||
document.body.classList.add('video-background-active')
|
||||
if (sessionStorage.getItem('archipelago_from_splash') !== '1') {
|
||||
startSynthwave()
|
||||
}
|
||||
const unlock = () => {
|
||||
resumeAudioContext()
|
||||
if (sessionStorage.getItem('archipelago_from_splash') !== '1') {
|
||||
startSynthwave()
|
||||
}
|
||||
document.removeEventListener('click', unlock)
|
||||
document.removeEventListener('touchstart', unlock)
|
||||
document.removeEventListener('keydown', unlock)
|
||||
}
|
||||
document.addEventListener('click', unlock, { once: true })
|
||||
document.addEventListener('touchstart', unlock, { once: true })
|
||||
document.addEventListener('keydown', unlock, { once: true })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -20,11 +20,20 @@ const router = useRouter()
|
||||
onMounted(async () => {
|
||||
const devMode = import.meta.env.VITE_DEV_MODE
|
||||
if (devMode === 'setup' || devMode === 'existing') {
|
||||
router.replace('/login')
|
||||
router.replace('/login').catch(() => {})
|
||||
return
|
||||
}
|
||||
|
||||
const seenOnboarding = await isOnboardingComplete()
|
||||
router.replace(seenOnboarding ? '/login' : '/onboarding/intro')
|
||||
let seenOnboarding = false
|
||||
try {
|
||||
const result = await Promise.race([
|
||||
isOnboardingComplete(),
|
||||
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 5000)),
|
||||
])
|
||||
seenOnboarding = result
|
||||
} catch {
|
||||
seenOnboarding = localStorage.getItem('neode_onboarding_complete') === '1'
|
||||
}
|
||||
router.replace(seenOnboarding ? '/login' : '/onboarding/intro').catch(() => {})
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user