fixed app location

This commit is contained in:
1dabread 2026-08-11 13:13:23 -05:00
parent 1ebfd22380
commit 09d5639ab9
4 changed files with 78 additions and 4 deletions

View File

@ -470,19 +470,23 @@ export default class SystemController {
message: `Docker container ${payload.container_name} not found.`,
})
}
const publishedHostPort = DockerService.getFirstPublishedHostPort(inspect)
await Service.create({
service_name: payload.container_name,
friendly_name: payload.friendly_name,
container_image: inspect.Config?.Image || '',
container_config: null,
ui_location: payload.container_name,
// Published existing apps are launchable from the Command Center. Containers without
// a published host port remain manageable in Supply Depot but do not get a dead tile.
ui_location: publishedHostPort,
icon: payload.icon || 'IconBrandDocker',
installed: true,
installation_status: 'idle',
is_dependency_service: false,
is_custom: true,
category: payload.category ?? 'custom',
display_order: publishedHostPort ? 49 : null,
depends_on: null,
})
@ -845,4 +849,4 @@ export default class SystemController {
cpus: hostConfig.NanoCpus ? hostConfig.NanoCpus / 1e9 : undefined,
}
}
}
}

View File

@ -2040,6 +2040,26 @@ export class DockerService {
return container.inspect()
}
/**
* Return the first host port published by a Docker container inspect payload.
* Existing apps are already running, so their launch target comes from Docker's
* active port bindings rather than NOMAD's generated container config.
*/
static getFirstPublishedHostPort(inspect: any): string | null {
const ports = inspect?.NetworkSettings?.Ports ?? {}
const bindings = Object.values(ports).flat() as Array<{
HostIp?: string
HostPort?: string
} | null>
const published = bindings
.filter((binding): binding is { HostIp?: string; HostPort: string } =>
Boolean(binding?.HostPort)
)
.sort((a, b) => Number.parseInt(a.HostPort, 10) - Number.parseInt(b.HostPort, 10))
return published[0]?.HostPort ?? null
}
/**
* Decode the multiplexed stream Docker returns for non-TTY container logs. Each frame is an
* 8-byte header ([streamType, 0,0,0, big-endian payloadSize]) followed by the payload.

View File

@ -319,6 +319,7 @@ export class SystemService {
async getServices({ installedOnly = true }: { installedOnly?: boolean }): Promise<ServiceSlim[]> {
const statuses = await this._syncContainersWithDatabase() // Sync and reuse the fetched status list
await this._syncExistingPublishedAppLinks()
const query = Service.query()
.orderBy('display_order', 'asc')
@ -388,6 +389,55 @@ export class SystemService {
return toReturn
}
/**
* Backfill launch metadata for existing Docker containers added before published ports were
* detected. A published existing app gets a Command Center link and a pre-system sort order;
* unpublished containers stay manageable in Supply Depot without a dead dashboard tile.
*/
private async _syncExistingPublishedAppLinks(): Promise<void> {
try {
const existingApps = await Service.query()
.where('installed', true)
.where('is_custom', true)
.where('is_dependency_service', false)
.whereNull('container_config')
for (const service of existingApps) {
const inspect = await this.dockerService.inspectContainerByName(service.service_name)
if (!inspect) continue
const publishedHostPort = DockerService.getFirstPublishedHostPort(inspect)
let changed = false
if (publishedHostPort && service.ui_location !== publishedHostPort) {
service.ui_location = publishedHostPort
changed = true
}
if (
publishedHostPort &&
(service.display_order === null || service.display_order >= 50)
) {
service.display_order = 49
changed = true
}
if (!publishedHostPort && service.ui_location === service.service_name) {
service.ui_location = null
changed = true
}
if (changed) {
await service.save()
}
}
} catch (error) {
logger.warn(
`[SystemService] Existing app launch metadata sync failed: ${
error instanceof Error ? error.message : error
}`
)
}
}
static getAppVersion(): string {
try {
if (this.appVersion) {

View File

@ -153,8 +153,8 @@ export default function ExistingAppModal({
</div>
<p className="text-xs text-text-muted">
Add an existing Docker container by its name so it appears in the Supply Depot and
on the home dashboard.
Add an existing Docker container by its name so it appears in the Supply Depot.
Published containers also appear on the home dashboard.
</p>
</div>
</StyledModal>