1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-24 07:49:41 +02:00

feat(app): limit the docker API version supported by the frontend (#12295)
Some checks failed
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
ci / build_images (map[arch:arm platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Has been cancelled
/ triage (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Test / test-client (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:linux]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
Test / test-server (map[arch:arm64 platform:linux]) (push) Has been cancelled
ci / build_manifests (push) Has been cancelled

This commit is contained in:
LP B 2024-10-08 17:13:14 +02:00 committed by GitHub
parent 8cbd23c059
commit ac5491e864
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
227 changed files with 4702 additions and 3411 deletions

View file

@ -1,15 +0,0 @@
import { EnvironmentId } from '@/react/portainer/environments/types';
export function buildUrl(
environmentId: EnvironmentId,
action: string,
subAction = ''
) {
let url = `/endpoints/${environmentId}/docker/${action}`;
if (subAction) {
url += `/${subAction}`;
}
return url;
}

View file

@ -0,0 +1,50 @@
import { compact } from 'lodash';
import { EnvironmentId } from '@/react/portainer/environments/types';
/**
* Build docker proxy URL for Environment
*
* @param environmentId
* @param action
* @param subSegments Sub segments are only added to the URL when they are not `undefined`.
* @returns `/endpoints/{environmentId}/docker/{action}/{subSegments[0]}/{subSegments[1]}/...`
*
* @example
* // all calls return /endpoints/1/docker/action/sub1/sub2
* buildDockerProxyUrl(1, 'action', 'sub1', 'sub2');
* buildDockerProxyUrl(1, 'action', undefined, 'sub1', undefined, 'sub2');
*
* @example
* function buildUrl(endpointId: EnvironmentId, id?: ServiceId, action?: string) {
* return buildDockerProxyUrl(endpointId, 'services', id, action);
*}
*
* // returns /endpoints/1/docker/services/ubx3r/update
* buildUrl(1, 'ubx3r', 'update')
*
* // returns /endpoints/1/docker/services/update
* buildUrl(1, undefined, 'update')
*
* // returns /endpoints/1/docker/services/ubx3r
* buildUrl(1, 'ubx3r') // = buildUrl(1, 'ubx3r', undefined)
*
* // returns /endpoints/1/docker/services
* buildUrl(1) // = buildUrl(1, undefined, undefined)
*
*/
export function buildDockerProxyUrl(
environmentId: EnvironmentId,
action: string,
...subSegments: unknown[]
) {
let url = `/endpoints/${environmentId}/docker/${action}`;
const joined = compact(subSegments).join('/');
if (joined) {
url += `/${joined}`;
}
return url;
}

View file

@ -0,0 +1,31 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
import { formatArrayQueryParamsForDockerAPI } from '../utils';
/**
* Raw docker API proxy
*/
export async function downloadImages(
environmentId: EnvironmentId,
images: { tags: string[]; id: string }[]
) {
const names = images.map((image) =>
image.tags[0] !== '<none>:<none>' ? image.tags[0] : image.id
);
try {
const { data } = await axios.get(
buildDockerProxyUrl(environmentId, 'images', 'get'),
{
params: { names },
responseType: 'blob',
paramsSerializer: formatArrayQueryParamsForDockerAPI,
}
);
return data;
} catch (e) {
throw parseAxiosError(e, 'Unable to download images');
}
}

View file

@ -0,0 +1,26 @@
import { ImageInspect } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param id
* @returns
*/
export async function getImage(
environmentId: EnvironmentId,
id: Required<ImageInspect['Id']>
) {
try {
const { data } = await axios.get<ImageInspect>(
buildDockerProxyUrl(environmentId, 'images', id, 'json')
);
return data;
} catch (e) {
throw parseAxiosError(e, 'Unable to retrieve image');
}
}

View file

@ -0,0 +1,32 @@
import { EnvironmentId } from '@/react/portainer/environments/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
export type ImageLayer = {
Id: string;
Created: number;
CreatedBy: string;
Tags: string[];
Size: number;
Comment: string;
};
/**
* Raw docker API proxy
* @param environmentId
* @returns
*/
export async function getImageHistory(
environmentId: EnvironmentId,
id: ImageLayer['Id']
) {
try {
const { data } = await axios.get<ImageLayer[]>(
buildDockerProxyUrl(environmentId, 'images', id, 'history')
);
return data;
} catch (err) {
throw parseAxiosError(err as Error, 'Unable to retrieve image layers');
}
}

View file

@ -4,7 +4,7 @@ import { ImageSummary } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildUrl } from '../build-url';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
import { queryKeys } from './queryKeys';
@ -24,10 +24,15 @@ export function useImages<T = ImagesListResponse>(
);
}
async function getImages(environmentId: EnvironmentId) {
/**
* Raw docker API proxy
* @param environmentId
* @returns
*/
export async function getImages(environmentId: EnvironmentId) {
try {
const { data } = await axios.get<ImagesListResponse>(
buildUrl(environmentId, 'images', 'json')
buildDockerProxyUrl(environmentId, 'images', 'json')
);
return data;
} catch (err) {

View file

@ -0,0 +1,28 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { ImageId, ImageName } from '@/docker/models/image';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param id
* @param force
* @returns
*/
export async function removeImage(
environmentId: EnvironmentId,
id: ImageId | ImageName,
force?: boolean
) {
try {
const { data } = await axios.delete(
buildDockerProxyUrl(environmentId, 'images', id),
{ params: { force } }
);
return data;
} catch (e) {
throw parseAxiosError(e, 'Unable to remove image');
}
}

View file

@ -0,0 +1,29 @@
import { EnvironmentId } from '@/react/portainer/environments/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { ImageId, ImageName } from '@/docker/models/image';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param id
* @param repo string generated by `buildImageFullURIFromModel` or `buildImageFullURI`
* @returns
*/
export async function tagImage(
environmentId: EnvironmentId,
id: ImageId | ImageName,
repo: string
) {
try {
const { data } = await axios.post(
buildDockerProxyUrl(environmentId, 'images', id, 'tag'),
{},
{ params: { repo } }
);
return data;
} catch (e) {
throw parseAxiosError(e, 'Unable to tag image');
}
}

View file

@ -0,0 +1,26 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param file
* @returns
*/
export async function uploadImages(environmentId: EnvironmentId, file: File) {
try {
return await axios.post(
buildDockerProxyUrl(environmentId, 'images', 'load'),
file,
{
headers: {
'Content-Type': file.type, // 'application/x-tar',
},
}
);
} catch (e) {
throw parseAxiosError(e, 'Unable to upload image');
}
}

View file

@ -1,15 +0,0 @@
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildUrl as buildProxyUrl } from '../build-url';
export function buildUrl(
environmentId: EnvironmentId,
action?: string,
subAction = ''
) {
return buildProxyUrl(
environmentId,
'nodes',
subAction ? `${action}/${subAction}` : action
);
}

View file

@ -0,0 +1,26 @@
import { Node } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param id
* @returns
*/
export async function getNode(
environmentId: EnvironmentId,
id: NonNullable<Node['ID']>
) {
try {
const { data } = await axios.get<Node>(
buildDockerProxyUrl(environmentId, 'nodes', id)
);
return data;
} catch (error) {
throw parseAxiosError(error, 'Unable to retrieve node');
}
}

View file

@ -4,16 +4,24 @@ import { useQuery } from 'react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildUrl } from './build-url';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
import { queryKeys } from './query-keys';
export function useNodes(environmentId: EnvironmentId) {
return useQuery(queryKeys.base(environmentId), () => getNodes(environmentId));
}
async function getNodes(environmentId: EnvironmentId) {
/**
* Raw docker API proxy
* @param environmentId
* @returns
*/
export async function getNodes(environmentId: EnvironmentId) {
try {
const { data } = await axios.get<Array<Node>>(buildUrl(environmentId));
const { data } = await axios.get<Array<Node>>(
buildDockerProxyUrl(environmentId, 'nodes')
);
return data;
} catch (error) {
throw parseAxiosError(error, 'Unable to retrieve nodes');

View file

@ -0,0 +1,31 @@
import { Node, NodeSpec } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param id
* @param node
* @param version
*/
export async function updateNode(
environmentId: EnvironmentId,
id: NonNullable<Node['ID']>,
node: NodeSpec,
version: number
) {
try {
const { data } = await axios.post(
buildDockerProxyUrl(environmentId, 'nodes', id, 'update'),
node,
{ params: { version } }
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to update node');
}
}

View file

@ -0,0 +1,21 @@
import { SecretSpec } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
export async function createSecret(
environmentId: EnvironmentId,
secret: SecretSpec
) {
try {
const { data } = await axios.post(
buildDockerProxyUrl(environmentId, 'secrets', 'create'),
secret
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to create secret');
}
}

View file

@ -0,0 +1,17 @@
import { Secret } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
export async function removeSecret(
environmentId: EnvironmentId,
id: NonNullable<Secret['ID']>
) {
try {
await axios.delete(buildDockerProxyUrl(environmentId, 'secrets', id));
} catch (err) {
throw parseAxiosError(err, 'Unable to remove secret');
}
}

View file

@ -0,0 +1,21 @@
import { Secret } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { PortainerResponse } from '@/react/docker/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
export async function getSecret(
environmentId: EnvironmentId,
id: NonNullable<Secret['ID']>
) {
try {
const { data } = await axios.get<PortainerResponse<Secret>>(
buildDockerProxyUrl(environmentId, 'secrets', id)
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve secret');
}
}

View file

@ -0,0 +1,15 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
export async function getSecrets(environmentId: EnvironmentId) {
try {
const { data } = await axios.get(
buildDockerProxyUrl(environmentId, 'secrets')
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve secrets');
}
}

View file

@ -0,0 +1,35 @@
import { Task } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
import { withFiltersQueryParam } from '../utils';
type Filters = {
'desired-state'?: 'running' | 'shutdown' | 'accepted';
id?: Task['ID'];
label?: Task['Labels'];
name?: Task['Name'];
node?: Task['NodeID'];
service?: Task['ServiceID'];
};
export async function getTasks(
environmentId: EnvironmentId,
filters?: Filters
) {
try {
const { data } = await axios.get<Task[]>(
buildDockerProxyUrl(environmentId, 'tasks'),
{
params: {
...withFiltersQueryParam(filters),
},
}
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve tasks');
}
}

View file

@ -0,0 +1,32 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
type CommitParams = {
container?: string; // The ID or name of the container to commit
repo?: string; // Repository name for the created image
tag?: string; // Tag name for the create image
comment?: string; // Commit message
author?: string; // Author of the image (e.g., John Hannibal Smith <hannibal@a-team.com>)
pause?: boolean; // Default: true Whether to pause the container before committing
changes?: string; // Dockerfile instructions to apply while committing
};
export async function commitContainer(
environmentId: EnvironmentId,
params: CommitParams
) {
try {
const { data } = await axios.post<{ Id: string }>(
buildDockerProxyUrl(environmentId, 'commit'),
{},
{
params,
}
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to commit container');
}
}

View file

@ -0,0 +1,30 @@
import { EventMessage } from 'docker-types/generated/1.41';
import axios, {
jsonObjectsToArrayHandler,
parseAxiosError,
} from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param param1
* @returns
*/
export async function getEvents(
environmentId: EnvironmentId,
{ since, until }: { since: string; until: string }
) {
try {
const { data } = await axios.get<EventMessage[]>(
buildDockerProxyUrl(environmentId, 'events'),
{ params: { since, until }, transformResponse: jsonObjectsToArrayHandler }
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve engine events');
}
}

View file

@ -0,0 +1,30 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
/**
* Raw docker API proxy
* @param environmentId
* @param id exec instance id
*/
export async function resizeTTY(
environmentId: EnvironmentId,
id: string,
{ width, height }: { width: number; height: number }
) {
try {
await axios.post(
buildDockerProxyUrl(environmentId, 'exec', id, 'resize'),
{},
{
params: {
h: height,
w: width,
},
}
);
} catch (err) {
throw parseAxiosError(err, 'Unable to resize tty of exec');
}
}

View file

@ -4,16 +4,16 @@ import { SystemInfo } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildUrl } from './build-url';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
export async function getInfo(environmentId: EnvironmentId) {
try {
const { data } = await axios.get<SystemInfo>(
buildUrl(environmentId, 'info')
buildDockerProxyUrl(environmentId, 'info')
);
return data;
} catch (err) {
throw parseAxiosError(err as Error, 'Unable to retrieve version');
throw parseAxiosError(err, 'Unable to retrieve system info');
}
}

View file

@ -0,0 +1,12 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
export async function ping(environmentId: EnvironmentId) {
try {
await axios.get(buildDockerProxyUrl(environmentId, '_ping'));
} catch (error) {
throw parseAxiosError(error);
}
}

View file

@ -10,13 +10,19 @@ import { EnvironmentId } from '@/react/portainer/environments/types';
import { queryKeys } from '../../queries/utils/root';
import { buildUrl } from './build-url';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
import { useInfo } from './useInfo';
const pluginTypeToVersionMap: { [k in keyof PluginsInfo]: string } = {
Volume: 'docker.volumedriver/1.0',
Network: 'docker.networkdriver/1.0',
Log: 'docker.logdriver/1.0',
};
export async function getPlugins(environmentId: EnvironmentId) {
try {
const { data } = await axios.get<Array<Plugin>>(
buildUrl(environmentId, 'plugins')
buildDockerProxyUrl(environmentId, 'plugins')
);
return data;
} catch (e) {
@ -38,77 +44,74 @@ function usePlugins(
export function useServicePlugins(
environmentId: EnvironmentId,
systemOnly: boolean,
pluginType: keyof PluginsInfo,
pluginVersion: string
pluginType: keyof PluginsInfo
) {
const systemPluginsQuery = useInfo(environmentId, (info) => info.Plugins);
const pluginsQuery = usePlugins(environmentId, { enabled: !systemOnly });
return {
data: aggregateData(),
data: aggregateData(
systemPluginsQuery.data,
pluginsQuery.data,
systemOnly,
pluginType
),
isLoading: systemPluginsQuery.isLoading || pluginsQuery.isLoading,
};
}
function aggregateData() {
if (!systemPluginsQuery.data) {
return null;
}
const systemPlugins = systemPluginsQuery.data[pluginType] || [];
if (systemOnly) {
return systemPlugins;
}
const plugins =
pluginsQuery.data
?.filter(
(plugin) =>
plugin.Enabled &&
// docker has an error in their types, so we need to cast to unknown first
// see https://docs.docker.com/engine/api/v1.41/#tag/Plugin/operation/PluginList
plugin.Config.Interface.Types.includes(
pluginVersion as unknown as PluginInterfaceType
)
)
.map((plugin) => plugin.Name) || [];
return [...systemPlugins, ...plugins];
/**
* @private Exported only for AngularJS `PluginService` factory `app/docker/services/pluginService.js`
*/
export function aggregateData(
systemPluginsData: PluginsInfo | undefined,
pluginsData: Plugin[] | undefined,
systemOnly: boolean,
pluginType: keyof PluginsInfo
) {
if (!systemPluginsData) {
return null;
}
const systemPlugins = systemPluginsData[pluginType] || [];
if (systemOnly) {
return systemPlugins;
}
const plugins =
pluginsData
?.filter(
(plugin) =>
plugin.Enabled &&
// docker has an error in their types, so we need to cast to unknown first
// see https://docs.docker.com/engine/api/v1.41/#tag/Plugin/operation/PluginList
plugin.Config.Interface.Types.includes(
pluginTypeToVersionMap[pluginType] as unknown as PluginInterfaceType
)
)
.map((plugin) => plugin.Name) || [];
return [...systemPlugins, ...plugins];
}
export function useLoggingPlugins(
environmentId: EnvironmentId,
systemOnly: boolean
) {
return useServicePlugins(
environmentId,
systemOnly,
'Log',
'docker.logdriver/1.0'
);
return useServicePlugins(environmentId, systemOnly, 'Log');
}
export function useVolumePlugins(
environmentId: EnvironmentId,
systemOnly: boolean
) {
return useServicePlugins(
environmentId,
systemOnly,
'Volume',
'docker.volumedriver/1.0'
);
return useServicePlugins(environmentId, systemOnly, 'Volume');
}
export function useNetworkPlugins(
environmentId: EnvironmentId,
systemOnly: boolean
) {
return useServicePlugins(
environmentId,
systemOnly,
'Network',
'docker.networkdriver/1.0'
);
return useServicePlugins(environmentId, systemOnly, 'Network');
}

View file

@ -0,0 +1,40 @@
import { useQuery } from 'react-query';
import { Swarm } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { queryKeys } from './query-keys';
import { useIsSwarm } from './useInfo';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
export function useSwarm<T = Swarm>(
environmentId: EnvironmentId,
{ select }: { select?(value: Swarm): T } = {}
) {
const isSwarm = useIsSwarm(environmentId);
return useQuery({
queryKey: [...queryKeys.base(environmentId), 'swarm'] as const,
queryFn: () => getSwarm(environmentId),
select,
enabled: isSwarm,
});
}
export async function getSwarm(environmentId: EnvironmentId) {
try {
const { data } = await axios.get<Swarm>(
buildDockerProxyUrl(environmentId, 'swarm')
);
return data;
} catch (err) {
throw parseAxiosError(err, 'Unable to retrieve swarm information');
}
}
export function useSwarmId(environmentId: EnvironmentId) {
return useSwarm(environmentId, {
select: (swarm) => swarm.ID,
});
}

View file

@ -4,12 +4,12 @@ import { SystemVersion } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { buildUrl } from './build-url';
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
export async function getVersion(environmentId: EnvironmentId) {
try {
const { data } = await axios.get<SystemVersion>(
buildUrl(environmentId, 'version')
buildDockerProxyUrl(environmentId, 'version')
);
return data;
} catch (err) {

View file

@ -0,0 +1,67 @@
import { agentTargetHeader } from '@/portainer/services/axios';
import { RegistryId } from '@/react/portainer/registries/types/registry';
/**
* Generates the `filters` query param entry for docker API list actions
*
* @param filters map[string][]string
* @returns `{ filters: filters && JSON.stringify(filters) }`
*/
export function withFiltersQueryParam<T = unknown>(filters?: T) {
return { filters: filters && JSON.stringify(filters) };
}
/**
* Encodes the registry credentials in base64
* @param registryId
* @returns
*/
function encodeRegistryCredentials(registryId: RegistryId) {
return window.btoa(JSON.stringify({ registryId }));
}
/**
* Generates the `X-Registry-Auth` header for docker API
* @param registryId The registry Id to use
* @returns
*/
export function withRegistryAuthHeader(registryId?: RegistryId) {
return registryId !== undefined
? { 'X-Registry-Auth': encodeRegistryCredentials(registryId) }
: {};
}
/**
* Generates the `X-PortainerAgent-Target` header
* @param nodeName The node name to target
* @returns
*/
export function withAgentTargetHeader(nodeName?: string) {
return nodeName ? { [agentTargetHeader]: nodeName } : {};
}
/**
* Generates the `'X-PortainerAgent-ManagerOperation'` header
* @param managerOperation
* @returns
*/
export function withAgentManagerOperationHeader(managerOperation?: boolean) {
return managerOperation ? { 'X-PortainerAgent-ManagerOperation': '1' } : {};
}
/**
* The Docker API expects array query params as `param = value_1 & param = value_2`
* axios default serializer generates `names[] = value_1 & names[] = value_2`
* which are ignored by the Docker API
* @param params
* @returns the concatenated string of query params with Docker expected format
*/
export function formatArrayQueryParamsForDockerAPI(
params: Record<string, unknown>
) {
return Object.entries(params)
.flatMap(([param, value]) =>
(Array.isArray(value) ? value : [value]).map((v) => `${param}=${v}`)
)
.join('&');
}