mirror of
https://github.com/portainer/portainer.git
synced 2025-08-04 21:35:23 +02:00
fix: 2.24 regressions (#190)
Co-authored-by: andres-portainer <andres-portainer@users.noreply.github.com> Co-authored-by: LP B <xAt0mZ@users.noreply.github.com> Co-authored-by: testA113 <aliharriss1995@gmail.com> Co-authored-by: oscarzhou <oscar.zhou@portainer.io>
This commit is contained in:
parent
5d311031e3
commit
b46bff06c6
7 changed files with 224 additions and 105 deletions
|
@ -14,13 +14,8 @@ type StringPortBinding = {
|
|||
containerPort: number;
|
||||
};
|
||||
|
||||
type NumericPortBinding = {
|
||||
hostPort: number;
|
||||
protocol: Protocol;
|
||||
containerPort: number;
|
||||
};
|
||||
|
||||
type RangePortBinding = {
|
||||
hostIp: string;
|
||||
hostPort: Range;
|
||||
protocol: Protocol;
|
||||
containerPort: Range;
|
||||
|
@ -42,9 +37,7 @@ export function toViewModel(portBindings: PortMap): Values {
|
|||
return value === 'tcp' || value === 'udp';
|
||||
}
|
||||
|
||||
function parsePorts(
|
||||
portBindings: PortMap
|
||||
): Array<StringPortBinding | NumericPortBinding> {
|
||||
function parsePorts(portBindings: PortMap): Array<StringPortBinding> {
|
||||
return Object.entries(portBindings).flatMap(([key, bindings]) => {
|
||||
const [containerPort, protocol] = key.split('/');
|
||||
|
||||
|
@ -63,15 +56,24 @@ export function toViewModel(portBindings: PortMap): Values {
|
|||
}
|
||||
|
||||
return bindings.map((binding) => {
|
||||
let port = '';
|
||||
if (binding.HostPort) {
|
||||
port = binding.HostPort;
|
||||
}
|
||||
if (binding.HostIp) {
|
||||
port = `${binding.HostIp}:${port}`;
|
||||
}
|
||||
|
||||
if (binding.HostPort?.includes('-')) {
|
||||
// Range port
|
||||
return {
|
||||
hostPort: binding.HostPort,
|
||||
hostPort: port,
|
||||
protocol,
|
||||
containerPort: containerPortNumber,
|
||||
};
|
||||
}
|
||||
return {
|
||||
hostPort: parseInt(binding.HostPort || '0', 10),
|
||||
hostPort: port,
|
||||
protocol,
|
||||
containerPort: containerPortNumber,
|
||||
};
|
||||
|
@ -79,9 +81,9 @@ export function toViewModel(portBindings: PortMap): Values {
|
|||
});
|
||||
}
|
||||
|
||||
function sortPorts(ports: Array<StringPortBinding | NumericPortBinding>) {
|
||||
const rangePorts = ports.filter(isStringPortBinding);
|
||||
const nonRangePorts = ports.filter(isNumericPortBinding);
|
||||
function sortPorts(ports: Array<StringPortBinding>) {
|
||||
const rangePorts = ports.filter(isRangePortBinding);
|
||||
const nonRangePorts = ports.filter((port) => !isRangePortBinding(port));
|
||||
|
||||
return {
|
||||
rangePorts,
|
||||
|
@ -93,27 +95,40 @@ export function toViewModel(portBindings: PortMap): Values {
|
|||
};
|
||||
}
|
||||
|
||||
function combinePorts(ports: Array<NumericPortBinding>) {
|
||||
function combinePorts(ports: Array<StringPortBinding>) {
|
||||
return ports
|
||||
.reduce((acc, port) => {
|
||||
let hostIp = '';
|
||||
let hostPort = 0;
|
||||
if (port.hostPort.includes(':')) {
|
||||
const [ipStr, portStr] = port.hostPort.split(':');
|
||||
hostIp = ipStr;
|
||||
hostPort = parseInt(portStr || '0', 10);
|
||||
} else {
|
||||
hostPort = parseInt(port.hostPort || '0', 10);
|
||||
}
|
||||
|
||||
const lastPort = acc[acc.length - 1];
|
||||
if (
|
||||
lastPort &&
|
||||
lastPort.hostIp === hostIp &&
|
||||
lastPort.containerPort.end === port.containerPort - 1 &&
|
||||
lastPort.hostPort.end === port.hostPort - 1 &&
|
||||
lastPort.hostPort.end === hostPort - 1 &&
|
||||
lastPort.protocol === port.protocol
|
||||
) {
|
||||
lastPort.hostIp = hostIp;
|
||||
lastPort.containerPort.end = port.containerPort;
|
||||
lastPort.hostPort.end = port.hostPort;
|
||||
lastPort.hostPort.end = hostPort;
|
||||
return acc;
|
||||
}
|
||||
|
||||
return [
|
||||
...acc,
|
||||
{
|
||||
hostIp,
|
||||
hostPort: {
|
||||
start: port.hostPort,
|
||||
end: port.hostPort,
|
||||
start: hostPort,
|
||||
end: hostPort,
|
||||
},
|
||||
containerPort: {
|
||||
start: port.containerPort,
|
||||
|
@ -123,34 +138,32 @@ export function toViewModel(portBindings: PortMap): Values {
|
|||
},
|
||||
];
|
||||
}, [] as Array<RangePortBinding>)
|
||||
.map(({ protocol, containerPort, hostPort }) => ({
|
||||
hostPort: getRange(hostPort.start, hostPort.end),
|
||||
.map(({ protocol, containerPort, hostPort, hostIp }) => ({
|
||||
hostPort: getRange(hostPort.start, hostPort.end, hostIp),
|
||||
containerPort: getRange(containerPort.start, containerPort.end),
|
||||
protocol,
|
||||
}));
|
||||
|
||||
function getRange(start: number, end: number): string {
|
||||
function getRange(start: number, end: number, hostIp?: string): string {
|
||||
if (start === end) {
|
||||
if (start === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (hostIp) {
|
||||
return `${hostIp}:${start}`;
|
||||
}
|
||||
return start.toString();
|
||||
}
|
||||
|
||||
if (hostIp) {
|
||||
return `${hostIp}:${start}-${end}`;
|
||||
}
|
||||
return `${start}-${end}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNumericPortBinding(
|
||||
port: StringPortBinding | NumericPortBinding
|
||||
): port is NumericPortBinding {
|
||||
return port.hostPort !== 'string';
|
||||
}
|
||||
|
||||
function isStringPortBinding(
|
||||
port: StringPortBinding | NumericPortBinding
|
||||
): port is StringPortBinding {
|
||||
return port.hostPort === 'string';
|
||||
function isRangePortBinding(port: StringPortBinding): boolean {
|
||||
return port.hostPort.includes('-');
|
||||
}
|
||||
|
|
|
@ -57,10 +57,15 @@ export async function buildImageFromDockerfileContentAndFiles(
|
|||
const dockerfile = new Blob([content], { type: 'text/plain' });
|
||||
const uploadFiles = [dockerfile, ...files];
|
||||
|
||||
const formData = new FormData();
|
||||
uploadFiles.forEach((file, index) => {
|
||||
formData.append(`file${index}`, file);
|
||||
});
|
||||
|
||||
return buildImage(
|
||||
environmentId,
|
||||
{ t: names },
|
||||
{ file: uploadFiles },
|
||||
formData,
|
||||
'multipart/form-data'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
export interface ActivityLog {
|
||||
interface BaseActivityLog {
|
||||
timestamp: number;
|
||||
action: string;
|
||||
context: string;
|
||||
id: number;
|
||||
payload: object;
|
||||
username: string;
|
||||
}
|
||||
export interface ActivityLogResponse extends BaseActivityLog {
|
||||
payload: string;
|
||||
}
|
||||
|
||||
export interface ActivityLog extends BaseActivityLog {
|
||||
payload: string | object;
|
||||
}
|
||||
|
||||
export interface ActivityLogsResponse {
|
||||
logs: Array<ActivityLogResponse>;
|
||||
totalCount: number;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import axios, { parseAxiosError } from '@/portainer/services/axios';
|
|||
|
||||
import { isBE } from '../../feature-flags/feature-flags.service';
|
||||
|
||||
import { ActivityLog } from './types';
|
||||
import { ActivityLogResponse, ActivityLogsResponse } from './types';
|
||||
|
||||
export const sortKeys = ['Context', 'Action', 'Timestamp', 'Username'] as const;
|
||||
export type SortKey = (typeof sortKeys)[number];
|
||||
|
@ -30,19 +30,18 @@ export function useActivityLogs(query: Query) {
|
|||
queryKey: ['activityLogs', query] as const,
|
||||
queryFn: () => fetchActivityLogs(query),
|
||||
keepPreviousData: true,
|
||||
select: (data) => ({
|
||||
...data,
|
||||
logs: decorateLogs(data.logs),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
interface ActivityLogsResponse {
|
||||
logs: Array<ActivityLog>;
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
async function fetchActivityLogs(query: Query): Promise<ActivityLogsResponse> {
|
||||
try {
|
||||
if (!isBE) {
|
||||
return {
|
||||
logs: [{}, {}, {}, {}, {}] as Array<ActivityLog>,
|
||||
logs: [{}, {}, {}, {}, {}] as Array<ActivityLogResponse>,
|
||||
totalCount: 5,
|
||||
};
|
||||
}
|
||||
|
@ -56,3 +55,40 @@ async function fetchActivityLogs(query: Query): Promise<ActivityLogsResponse> {
|
|||
throw parseAxiosError(err, 'Failed loading user activity logs csv');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates logs with the payload parsed from base64
|
||||
*/
|
||||
function decorateLogs(logs?: ActivityLogResponse[]) {
|
||||
if (!logs || logs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return logs.map((log) => ({
|
||||
...log,
|
||||
payload: parseBase64AsObject(log.payload),
|
||||
}));
|
||||
}
|
||||
|
||||
function parseBase64AsObject(value: string): string | object {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(safeAtob(value));
|
||||
} catch (err) {
|
||||
return safeAtob(value);
|
||||
}
|
||||
}
|
||||
|
||||
function safeAtob(value: string) {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return window.atob(value);
|
||||
} catch (err) {
|
||||
// If the payload is not base64 encoded, return the original value
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue