fixed app location
This commit is contained in:
parent
1ebfd22380
commit
09d5639ab9
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue