Integrate Docker support into Archipelago and Neode UI

- Added StateManager and data_model modules to manage application state.
- Updated ApiHandler to utilize StateManager for WebSocket connections.
- Enhanced Server initialization to include StateManager.
- Implemented Docker container querying in Neode UI to populate app data dynamically.
- Removed temporary dummy app configurations in favor of real Docker-based applications.
- Improved WebSocket reconnection logic and error handling in the UI.
- Updated package.json and package-lock.json to include dockerode dependency.
This commit is contained in:
Dorian
2026-01-27 23:06:18 +00:00
parent 7afefafec1
commit 3b3f70276f
15 changed files with 1318 additions and 329 deletions

View File

@@ -139,8 +139,8 @@ export class WebSocketClient {
// Always try to reconnect unless we've exceeded max attempts
// Code 1001 (Going Away) happens on HMR reloads - reconnect IMMEDIATELY
if (this.reconnectAttempts < this.maxReconnectAttempts) {
// Immediate reconnection for HMR (code 1001) - no delay
const isHMR = event.code === 1001 || event.code === 1006
// Only code 1001 is HMR, NOT 1006 (1006 is abnormal closure)
const isHMR = event.code === 1001
const delay = isHMR ? 0 : (this.reconnectAttempts === 0 ? 100 : Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts), 5000))
console.log(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts + 1}/${this.maxReconnectAttempts}, code: ${event.code}, HMR: ${isHMR})`)

View File

@@ -144,35 +144,19 @@
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { computed, ref } from 'vue'
import { useRouter, RouterLink } from 'vue-router'
import { useAppStore } from '../stores/app'
import { PackageState } from '../types/api'
import { dummyApps } from '../utils/dummyApps'
import { fetchMultipleAppInfo } from '../utils/githubAppInfo'
const router = useRouter()
const store = useAppStore()
// TEMPORARY: Always show dummy apps for now (until real apps are ready)
// TODO: Remove this and use real packages when they're available
// Use real packages from store - no more dummy apps
const packages = computed(() => {
const realPackages = store.packages
const packageKeys = realPackages ? Object.keys(realPackages) : []
console.log('[Apps] Real packages from store:', packageKeys.length, 'apps:', packageKeys)
console.log('[Apps] Dummy apps available:', Object.keys(dummyApps).length, 'apps:', Object.keys(dummyApps))
// FOR NOW: Always return dummy apps regardless of what's in store
// This ensures all dummy apps show up for development
console.log('[Apps] Returning dummy apps')
return dummyApps
// TODO: Uncomment this when ready to use real packages
// if (packageKeys.length === 0) {
// return dummyApps
// }
// return realPackages
console.log('[Apps] Real packages from store:', Object.keys(realPackages || {}).length, 'apps')
return realPackages || {}
})
// Sorted by manifest title, case-insensitive; order stable regardless of running/stopped
@@ -319,96 +303,6 @@ function handleImageError(e: Event) {
}
}
// Fetch GitHub app info for dummy apps on mount
const appInfoCache = ref<Record<string, any>>({})
// In development, skip external API calls to avoid rate limiting and noise
// App icons and descriptions are already defined in dummyApps.ts
const isDev = import.meta.env.DEV
// Watch for packages and fetch app info when showing dummy apps (DISABLED IN DEV)
watch(() => Object.keys(store.packages).length, async (packageCount) => {
// Skip external API calls in development to avoid 403/404 errors
if (isDev) {
console.log('[Apps] Using local app data (dev mode, external API calls disabled)')
return
}
// Only fetch if we're showing dummy apps (no real packages)
if (packageCount === 0) {
try {
// First try Start9 registry for icons
console.log('[Apps] Fetching app info from Start9 registry...')
try {
const registryResponse = await fetch('https://registry.start9.com/api/v1/packages')
if (registryResponse.ok) {
const registryData = await registryResponse.json()
// Update dummy apps with registry data
Object.entries(registryData).forEach(([id, pkg]: [string, any]) => {
if (dummyApps[id]) {
const latestVersion = pkg.versions ? Object.keys(pkg.versions).sort().reverse()[0] : null
const versionData = latestVersion ? pkg.versions[latestVersion] : {}
// Update icon from registry
if (versionData.icon) {
dummyApps[id]['static-files'].icon = versionData.icon
} else if (pkg.icon) {
dummyApps[id]['static-files'].icon = pkg.icon
}
// Update description
if (versionData.description) {
const desc = typeof versionData.description === 'string'
? versionData.description
: versionData.description.short || versionData.description.long || ''
if (desc) {
dummyApps[id].manifest.description.short = desc.substring(0, 100)
if (!dummyApps[id].manifest.description.long) {
dummyApps[id].manifest.description.long = desc
}
}
}
}
})
console.log('[Apps] Updated apps from Start9 registry')
return
}
} catch (registryErr) {
// Silently fail in production
console.debug('[Apps] Registry unavailable')
}
// Fallback to GitHub fetching
const appsToFetch = Object.entries(dummyApps).map(([id, pkg]) => ({
id,
'wrapper-repo': pkg.manifest['wrapper-repo']
}))
console.log('[Apps] Fetching GitHub info for dummy apps...')
const githubInfo = await fetchMultipleAppInfo(appsToFetch)
appInfoCache.value = githubInfo
// Update dummy apps with fetched info
Object.entries(githubInfo).forEach(([id, info]) => {
if (dummyApps[id] && info.icon) {
dummyApps[id]['static-files'].icon = info.icon
}
if (dummyApps[id] && info.description) {
dummyApps[id].manifest.description.short = info.description.substring(0, 100)
if (!dummyApps[id].manifest.description.long) {
dummyApps[id].manifest.description.long = info.description
}
}
})
console.log('[Apps] GitHub info fetched:', Object.keys(githubInfo).length, 'apps')
} catch (err) {
console.debug('[Apps] External API fetch skipped or failed')
}
}
}, { immediate: true })
</script>
<style scoped>

View File

@@ -788,174 +788,6 @@ function getCuratedAppList() {
]
}
// Helper function at end of script
function getCuratedAppList() {
return [
title: 'Nextcloud',
version: '29.0.0',
description: 'Self-hosted file sync and sharing platform. Your own private cloud storage with calendar, contacts, and office suite.',
icon: '/assets/img/nextcloud.png',
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/nextcloud-startos/releases/latest/download/nextcloud.s9pk',
repoUrl: 'https://github.com/Start9Labs/nextcloud-startos'
},
{
id: 'synapse',
title: 'Synapse (Matrix)',
version: '1.96.0',
description: 'Matrix homeserver for decentralized, encrypted communication. Host your own private messaging server.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/synapse-startos/releases/latest/download/synapse.s9pk',
repoUrl: 'https://github.com/Start9Labs/synapse-startos'
},
{
id: 'nostr-rs-relay',
title: 'Nostr Relay',
version: '0.8.0',
description: 'High-performance Nostr relay written in Rust. Host your own decentralized social media relay.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/nostr-rs-relay-startos/releases/latest/download/nostr-rs-relay.s9pk',
repoUrl: 'https://github.com/Start9Labs/nostr-rs-relay-startos'
},
{
id: 'bitcoinknots',
title: 'Bitcoin Knots',
version: '27.0',
description: 'Bitcoin Knots full node - a derivative of Bitcoin Core with additional features and enhancements.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/bitcoinknots-startos/releases/latest/download/bitcoinknots.s9pk',
repoUrl: 'https://github.com/Start9Labs/bitcoinknots-startos'
},
{
id: 'electrs',
title: 'Electrs',
version: '0.10.0',
description: 'Efficient Electrum Server implementation in Rust. Index Bitcoin blockchain for lightweight wallets.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/electrs-startos/releases/latest/download/electrs.s9pk',
repoUrl: 'https://github.com/Start9Labs/electrs-startos'
},
{
id: 'cups-messenger',
title: 'CUPS Messenger',
version: '2.0.0',
description: 'Private messaging over the Bitcoin Lightning Network. Censorship-resistant, encrypted communication.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/cups-messenger-startos/releases/latest/download/cups.s9pk',
repoUrl: 'https://github.com/Start9Labs/cups-messenger-startos'
},
{
id: 'ride-the-lightning',
title: 'Ride The Lightning',
version: '0.14.0',
description: 'Web UI for managing Lightning Network nodes (LND and CLN). Beautiful interface for node management.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/ride-the-lightning-startos/releases/latest/download/ride-the-lightning.s9pk',
repoUrl: 'https://github.com/Start9Labs/ride-the-lightning-startos'
},
{
id: 'thunderhub',
title: 'ThunderHub',
version: '0.13.0',
description: 'Lightning Network node management interface. Monitor channels, make payments, and manage your LND node.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/thunderhub-startos/releases/latest/download/thunderhub.s9pk',
repoUrl: 'https://github.com/Start9Labs/thunderhub-startos'
},
{
id: 'specter-desktop',
title: 'Specter Desktop',
version: '2.0.0',
description: 'Multi-signature Bitcoin wallet interface. Advanced wallet management with hardware wallet support.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/specter-desktop-startos/releases/latest/download/specter-desktop.s9pk',
repoUrl: 'https://github.com/Start9Labs/specter-desktop-startos'
},
{
id: 'mempool',
title: 'Mempool Explorer',
version: '2.5.0',
description: 'Self-hosted Bitcoin blockchain and mempool visualizer. Beautiful explorer for your node.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/mempool-startos/releases/latest/download/mempool.s9pk',
repoUrl: 'https://github.com/Start9Labs/mempool-startos'
},
{
id: 'vaultwarden',
title: 'Vaultwarden',
version: '1.30.0',
description: 'Self-hosted password manager (Bitwarden-compatible). Secure vault for all your passwords and secrets.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/vaultwarden-startos/releases/latest/download/vaultwarden.s9pk',
repoUrl: 'https://github.com/Start9Labs/vaultwarden-startos'
},
{
id: 'jellyfin',
title: 'Jellyfin',
version: '10.8.0',
description: 'Free media server system. Stream your movies, music, and photos to any device.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/jellyfin-startos/releases/latest/download/jellyfin.s9pk',
repoUrl: 'https://github.com/Start9Labs/jellyfin-startos'
},
{
id: 'photoprism',
title: 'PhotoPrism',
version: '231128',
description: 'AI-powered photo management. Organize and browse your photo collection with facial recognition.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/photoprism-startos/releases/latest/download/photoprism.s9pk',
repoUrl: 'https://github.com/Start9Labs/photoprism-startos'
},
{
id: 'immich',
title: 'Immich',
version: '1.90.0',
description: 'High-performance self-hosted photo and video backup solution. Mobile-first with ML features.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/immich-startos/releases/latest/download/immich.s9pk',
repoUrl: 'https://github.com/Start9Labs/immich-startos'
},
{
id: 'filebrowser',
title: 'File Browser',
version: '2.27.0',
description: 'Web-based file manager. Browse, upload, and manage files on your server through a web interface.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/filebrowser-startos/releases/latest/download/filebrowser.s9pk',
repoUrl: 'https://github.com/Start9Labs/filebrowser-startos'
},
{
id: 'home-assistant',
title: 'Home Assistant',
version: '2023.12.0',
description: 'Open-source home automation platform. Control and automate your smart home devices privately.',
icon: null,
author: 'Start9',
manifestUrl: 'https://github.com/Start9Labs/home-assistant-startos/releases/latest/download/home-assistant.s9pk',
repoUrl: 'https://github.com/Start9Labs/home-assistant-startos'
}
]
console.log(`📦 Loaded ${communityApps.value.length} Docker-based apps`)
} finally {
loadingCommunity.value = false
}
}
function viewAppDetails(app: any) {
console.log('[Marketplace] Navigating to app detail:', app)