Files
portainer/app/docker/views/containers/logs/containerLogsController.js
T
claude code agent 0bf4e71b79 fix(stacks): keep the stack breadcrumb trail on container attribute sub-tabs
When a container is opened from a stack, the detail tab kept the stack
trail (PR #7) but the attribute sub-tabs (Logs, Stats, Inspect, Console,
Attach) dropped it: those tabs were registered only under the global
docker.containers.container.* tree, so navigating to one left the stack
state (and its inherited params) behind, and each sub-view set a hardcoded
"Containers > ..." breadcrumb.

- Register stack-scoped child states docker.stacks.stack.container.{attach,
  exec,inspect,logs,stats} mirroring the global ones, so the inherited stack
  params survive and the trail can be kept.
- Centralize the breadcrumb logic in containerBreadcrumbs.ts (moved out of
  ItemView, which re-exports it) and add isStackContainerState +
  getContainerSubTabBreadcrumbs + buildStackContainerLinkParams.
- ActionLinksRow links sub-tabs into the stack tree (with stack+container
  params) when opened from a stack, else the global states unchanged.
- InspectView + the logs/stats/console controllers render the stack-aware
  trail; set up-front (no name) so it survives the load window and errors.

Covers regular/external/orphaned stacks and the non-stack fallback,
matching the existing ItemView breadcrumb behavior. New unit tests in
containerBreadcrumbs.test.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 02:25:04 +03:00

113 lines
3.7 KiB
JavaScript

import moment from 'moment';
import { trimContainerName } from '@/docker/filters/utils';
import { getContainerSubTabBreadcrumbs } from '@/react/docker/containers/ItemView/containerBreadcrumbs';
angular.module('portainer.docker').controller('ContainerLogsController', [
'$scope',
'$transition$',
'$interval',
'ContainerService',
'Notifications',
'HttpRequestHelper',
'endpoint',
function ($scope, $transition$, $interval, ContainerService, Notifications, HttpRequestHelper, endpoint) {
$scope.state = {
refreshRate: 3,
lineCount: 100,
sinceTimestamp: '',
displayTimestamps: false,
};
$scope.changeLogCollection = function (logCollectionStatus) {
if (!logCollectionStatus) {
stopRepeater();
} else {
setUpdateRepeater(!$scope.container.Config.Tty);
}
};
$scope.$on('$destroy', function () {
stopRepeater();
});
function stopRepeater() {
var repeater = $scope.repeater;
if (angular.isDefined(repeater)) {
$interval.cancel(repeater);
}
}
function setUpdateRepeater(skipHeaders) {
var refreshRate = $scope.state.refreshRate;
$scope.repeater = $interval(function () {
ContainerService.logs(
endpoint.Id,
$transition$.params().id,
1,
1,
$scope.state.displayTimestamps ? 1 : 0,
moment($scope.state.sinceTimestamp).unix(),
$scope.state.lineCount,
skipHeaders
)
.then(function success(data) {
$scope.logs = data;
})
.catch(function error(err) {
stopRepeater();
Notifications.error('Failure', err, 'Unable to retrieve container logs');
});
}, refreshRate * 1000);
}
function startLogPolling(skipHeaders) {
ContainerService.logs(
endpoint.Id,
$transition$.params().id,
1,
1,
$scope.state.displayTimestamps ? 1 : 0,
moment($scope.state.sinceTimestamp).unix(),
$scope.state.lineCount,
skipHeaders
)
.then(function success(data) {
$scope.logs = data;
setUpdateRepeater(skipHeaders);
})
.catch(function error(err) {
stopRepeater();
Notifications.error('Failure', err, 'Unable to retrieve container logs');
});
}
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
// Set the trail up-front (without the container name) so it survives the
// load window and a load error; the success path fills in the name.
$scope.breadcrumbs = getContainerSubTabBreadcrumbs($transition$.to().name, $transition$.params(), '', 'Logs');
ContainerService.container(endpoint.Id, $transition$.params().id)
.then(function success(data) {
var container = data;
$scope.container = container;
// Stack-aware breadcrumb: keeps the stack trail when the container was
// opened from a stack, falls back to the global Containers trail otherwise.
$scope.breadcrumbs = getContainerSubTabBreadcrumbs($transition$.to().name, $transition$.params(), trimContainerName(container.Name), 'Logs');
const logsEnabled = container.HostConfig && container.HostConfig.LogConfig && container.HostConfig.LogConfig.Type && container.HostConfig.LogConfig.Type !== 'none';
$scope.logsEnabled = logsEnabled;
if (logsEnabled) {
startLogPolling(!container.Config.Tty);
}
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve container information');
});
}
initView();
},
]);