1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-07 14:55:27 +02:00

feat(app): limit the docker API version supported by the frontend (#11855)
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run

This commit is contained in:
LP B 2024-06-10 20:54:31 +02:00 committed by GitHub
parent 4ba16f1b04
commit 6a8e6734f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
212 changed files with 4439 additions and 3281 deletions

View file

@ -0,0 +1,116 @@
import { EnvironmentId } from '@/react/portainer/environments/types';
import { EndpointProviderInterface } from './endpointProvider';
// see async.js
type AsyncInterface = (
asyncFunc: AsyncFunction,
...args: unknown[]
) => Promise<unknown>;
type AsyncFunction = (...params: unknown[]) => Promise<unknown>;
type AxiosFunction = (
environmentId: EnvironmentId | undefined,
...params: unknown[]
) => Promise<unknown>;
/* @ngInject */
export function AngularToReact(
EndpointProvider: EndpointProviderInterface,
$async: AsyncInterface
) {
return { useAxios, injectEnvironmentId };
/**
* Wraps the async axios function with `$async` to ensures the request runs inside the AngularJS digest cycle
*
* See `$async` (async.js) implementation and notes
*
* See `AngularToReact.injectEnvironmentId` to solve `environmentId` injection for services functions relying
* on `EndpointProvider.endpointID()` in their `$resource()` definition
*
* @example
* **Old AngularJS service**
* ```
* // file:: AngularJS service.js
*
* // ngInject
* function ServiceServiceFactory($q, Service) {
* return { getService };
*
* // the original signature doesn't have environmentId passed to it
* // it relies on EndpointProvider in $resource() definition
* // we will inject it on refactor
* // the function uses $q, which internally triggers a redraw of the UI when it resolves/rejects
* function getService(serviceId) {
* var deferred = $q.defer();
* [...]
* return deferred.promise;
* };
*
* // the original signature has environmentId passed to it
* // it doesn't rely on EndpointProvider in $resource() definition
* // we won't inject environmentId on refactor
* // the function uses $q, which internally triggers a redraw of the UI when it resolves/rejects
* function listServices(environmentId) {
* var deferred = $q.defer();
* [...]
* return deferred.promise;
* };
* }
* ```
*
* **New format**
* ```
* // file:: '@/react/.../useService.ts'
* // this function has `environmentId` as first parameter, which doesn't match the old AngularJS service signature
* export async function getService(environmentId: EnvironmentId, serviceId: ServiceId) {
* // axios.get()
* }
* // file:: '@/react/.../useServices.ts'
* // this function has `environmentId` as first parameter, which matches the old AngularJS service signature
* export async function listServices(environmentId: EnvironmentId, serviceId: ServiceId) {
* // axios.get()
* }
* // file:: AngularJS service.js
* import { getService } from '@/react/.../useService.ts';
* import { listServices } from '@/react/.../useServices.ts';
*
* // ngInject
* function ServiceServiceFactory(AngularToReact) {
* const { useAxios, injectEnvironmentId } = AngularToReact;
* return {
* // ask to inject environmentId to maintain the old signature
* getService: useAxios(injectEnvironmentId(getService)),
* // do not ask to inject environmentId as it was already in the old signature
* // and is already passed by the caller
* listServices: useAxios(listServices),
* };
* }
* ```
*/
function useAxios(axiosFunc: AxiosFunction) {
return (...params: unknown[]) =>
$async(axiosFunc as AsyncFunction, ...params);
}
/**
* Wraps the Axios function taking `endpointId` as first param to expose the old service format.
*
* Leverage injected `EndpointProvider` that was used in the rest file - `$resource()` definition.
*
* The axios function params **MUST** match the old AngularJS-service ones to use this helper without changing the service calls
*
* Should be used in conjunction with `AngularToReact.useAxios`
*
* @example
* See `AngularToReact.useAxios`
*
* @param {(environmentId: EnvironmentId, ...params: unknown[]) => Promise<unknown>} axiosFunc Axios function taking `environmentId` as first param
* @returns a function with the old AngularJS signature
*/
function injectEnvironmentId(axiosFunc: AxiosFunction) {
return async (...params: unknown[]) =>
axiosFunc(EndpointProvider.endpointID(), ...params);
}
}

View file

@ -54,11 +54,11 @@ angular.module('portainer.app').factory('StackService', [
SwarmService.swarm(targetEndpointId)
.then(function success(data) {
var swarm = data;
if (swarm.Id === stack.SwarmId) {
if (swarm.ID === stack.SwarmId) {
deferred.reject({ msg: 'Target environment is located in the same Swarm cluster as the current environment', err: null });
return;
}
return Stack.migrate({ id: stack.Id, endpointId: stack.EndpointId }, { EndpointID: targetEndpointId, SwarmID: swarm.Id, Name: newName }).$promise;
return Stack.migrate({ id: stack.Id, endpointId: stack.EndpointId }, { EndpointID: targetEndpointId, SwarmID: swarm.ID, Name: newName }).$promise;
})
.then(function success() {
deferred.resolve();
@ -182,10 +182,10 @@ angular.module('portainer.app').factory('StackService', [
service.swarmStacks = function (endpointId, includeExternalStacks, filters = {}) {
var deferred = $q.defer();
SwarmService.swarm()
SwarmService.swarm(endpointId)
.then(function success(data) {
var swarm = data;
filters = { SwarmID: swarm.Id, ...filters };
filters = { SwarmID: swarm.ID, ...filters };
return $q.all({
stacks: Stack.query({ filters: filters }).$promise,
@ -239,10 +239,10 @@ angular.module('portainer.app').factory('StackService', [
var deferred = $q.defer();
if (stack.Type == 1) {
SwarmService.swarm()
SwarmService.swarm(endpointId)
.then(function success(data) {
const swarm = data;
return Stack.associate({ id: stack.Id, endpointId: endpointId, swarmId: swarm.Id, orphanedRunning }).$promise;
return Stack.associate({ id: stack.Id, endpointId: endpointId, swarmId: swarm.ID, orphanedRunning }).$promise;
})
.then(function success(data) {
deferred.resolve(data);
@ -305,10 +305,10 @@ angular.module('portainer.app').factory('StackService', [
service.createSwarmStackFromFileUpload = function (name, stackFile, env, endpointId) {
var deferred = $q.defer();
SwarmService.swarm()
SwarmService.swarm(endpointId)
.then(function success(data) {
var swarm = data;
return FileUploadService.createSwarmStack(name, swarm.Id, stackFile, env, endpointId);
return FileUploadService.createSwarmStack(name, swarm.ID, stackFile, env, endpointId);
})
.then(function success(data) {
deferred.resolve(data.data);
@ -335,7 +335,7 @@ angular.module('portainer.app').factory('StackService', [
.then(function success(swarm) {
var payload = {
Name: name,
SwarmID: swarm.Id,
SwarmID: swarm.ID,
StackFileContent: stackFileContent,
Env: env,
};
@ -376,12 +376,12 @@ angular.module('portainer.app').factory('StackService', [
service.createSwarmStackFromGitRepository = function (name, repositoryOptions, env, endpointId) {
var deferred = $q.defer();
SwarmService.swarm()
SwarmService.swarm(endpointId)
.then(function success(data) {
var swarm = data;
var payload = {
Name: name,
SwarmID: swarm.Id,
SwarmID: swarm.ID,
RepositoryURL: repositoryOptions.RepositoryURL,
RepositoryReferenceName: repositoryOptions.RepositoryReferenceName,
ComposeFile: repositoryOptions.ComposeFilePathInRepository,

View file

@ -21,6 +21,8 @@ import {
portainerAgentManagerOperation,
portainerAgentTargetHeader,
} from './http-request.helper';
import { dockerMaxAPIVersionInterceptor } from './dockerMaxApiVersionInterceptor';
import { MAX_DOCKER_API_VERSION } from './dockerMaxApiVersion';
const portainerCacheHeader = 'X-Portainer-Cache';
@ -48,7 +50,10 @@ function headerInterpreter(
return 'not enough headers';
}
const axios = Axios.create({ baseURL: 'api' });
const axios = Axios.create({
baseURL: 'api',
maxDockerAPIVersion: MAX_DOCKER_API_VERSION,
});
axios.interceptors.request.use((req) => {
dispatchCacheRefreshEventIfNeeded(req);
return req;
@ -118,13 +123,14 @@ export function agentInterceptor(config: InternalAxiosRequestConfig) {
return newConfig;
}
axios.interceptors.request.use(dockerMaxAPIVersionInterceptor);
axios.interceptors.request.use(agentInterceptor);
axios.interceptors.response.use(undefined, (error) => {
if (
error.response?.status === 401 &&
!error.config.url.includes('/v2/') &&
!error.config.url.includes('/api/v4/') &&
!error.config.url.includes('/v2/') && // docker proxy through agent
!error.config.url.includes('/api/v4/') && // gitlab proxy
isTransitionRequiresAuthentication()
) {
// eslint-disable-next-line no-console
@ -188,6 +194,12 @@ export function defaultErrorParser(axiosError: AxiosError<unknown>) {
const error = new Error(message);
return { error, details };
}
if (isArrayResponse(axiosError.response?.data)) {
const message = axiosError.response?.data[0].message || '';
const details = axiosError.response?.data[0].details || message;
const error = new Error(message);
return { error, details };
}
const details = axiosError.response?.data
? axiosError.response?.data.toString()
@ -196,6 +208,16 @@ export function defaultErrorParser(axiosError: AxiosError<unknown>) {
return { error, details };
}
// handle jsonObjectsToArrayHandler transformation
function isArrayResponse(data: unknown): data is DefaultAxiosErrorType[] {
return (
!!data &&
Array.isArray(data) &&
'message' in data[0] &&
typeof data[0].message === 'string'
);
}
export function isDefaultResponse(
data: unknown
): data is DefaultAxiosErrorType {
@ -249,3 +271,18 @@ export function json2formData(json: Record<string, unknown>) {
return formData;
}
/**
* The Docker API often returns a list of JSON object.
* This handler wrap the JSON objects in an array.
* @param data Raw docker API response (stream of objects in a single string)
* @returns An array of parsed objects
*/
export function jsonObjectsToArrayHandler(data: string): unknown[] {
// catching empty data helps the function not to fail and prevents unwanted error message to user.
if (!data) {
return [];
}
const str = `[${data.replace(/\n/g, ' ').replace(/\}\s*\{/g, '}, {')}]`;
return JSON.parse(str);
}

View file

@ -0,0 +1,11 @@
// Key used in axios types definitions
export const MaxDockerAPIVersionKey = 'maxDockerAPIVersion' as const;
export type DockerAPIVersionType = number;
// this is the version we are using with the generated API types
export const MAX_DOCKER_API_VERSION: DockerAPIVersionType = 1.41;
// https://docs.docker.com/engine/api/#api-version-matrix
// Docker 26 = API 1.45
export const LATEST_DOCKER_API_VERSION: DockerAPIVersionType = 1.45;

View file

@ -0,0 +1,64 @@
import { SystemVersion } from 'docker-types/generated/1.41';
import Axios, { InternalAxiosRequestConfig } from 'axios';
import { setupCache, buildMemoryStorage } from 'axios-cache-interceptor';
import { buildDockerProxyUrl } from '@/react/docker/proxy/queries/buildDockerProxyUrl';
import PortainerError from '../error';
import { MAX_DOCKER_API_VERSION } from './dockerMaxApiVersion';
const envVersionAxios = Axios.create({
baseURL: 'api',
maxDockerAPIVersion: MAX_DOCKER_API_VERSION,
});
// setup a cache for the intermediary request sent by the interceptor
const envVersionCache = buildMemoryStorage();
setupCache(envVersionAxios, {
storage: envVersionCache,
ttl: 5 * 60 * 1000,
methods: ['get'],
});
export async function dockerMaxAPIVersionInterceptor(
rawConfig: InternalAxiosRequestConfig
) {
try {
const config = rawConfig;
const found = config.url?.match(
/endpoints\/(?<environmentId>\d+)\/docker\//
);
if (found && found.groups) {
const { environmentId } = found.groups;
const envId = parseInt(environmentId, 10);
// if we cannot parse the env ID, don't send a request that will fail,
// exit the interceptor and let the original request config pass through
if (Number.isNaN(envId)) {
return config;
}
const { data } = await envVersionAxios.get<SystemVersion>(
buildDockerProxyUrl(envId, 'version')
);
const apiVersion = parseFloat(data.ApiVersion ?? '0');
const { maxDockerAPIVersion } = config;
if (apiVersion > maxDockerAPIVersion) {
config.url = config.url?.replace(
/docker/,
`docker/v${maxDockerAPIVersion}`
);
}
}
return config;
} catch (err) {
throw new PortainerError(
'An error occurred while trying to limit request to the maximum supported Docker API version',
err
);
}
}

View file

@ -1,4 +1,4 @@
import { ping } from '@/docker/services/ping';
import { ping } from '@/react/docker/proxy/queries/usePing';
import { environmentStore } from '@/react/hooks/current-environment-store';
import {
Environment,

View file

@ -1,238 +1,209 @@
import { PortainerEndpointCreationTypes } from 'Portainer/models/endpoint/models';
import { genericHandler, jsonObjectsToArrayHandler } from '../../docker/rest/response/handlers';
angular.module('portainer.app').factory('FileUploadService', [
'$q',
'Upload',
'EndpointProvider',
function FileUploadFactory($q, Upload, EndpointProvider) {
'use strict';
angular.module('portainer.app').factory('FileUploadService', FileUploadFactory);
var service = {};
/* @ngInject */
function FileUploadFactory($q, Upload) {
var service = {
// createSchedule, // edge jobs service
// uploadBackup, // backup service
// createSwarmStack, // stack service
// createComposeStack, // stack service
// createEdgeStack, // edge stack service
// createCustomTemplate, // custom template service
// configureRegistry, // registry service
// createEndpoint, // endpoint service
// createAzureEndpoint, // endpoint service
// uploadLDAPTLSFiles, // auth settings controller
// uploadTLSFilesForEndpoint, // endpoint service
// uploadOwnershipVoucher, // import device controller
};
function uploadFile(url, file) {
return Upload.upload({ url: url, data: { file: file } });
function uploadFile(url, file) {
return Upload.upload({ url: url, data: { file: file } });
}
service.createSchedule = function (payload) {
return Upload.upload({
url: 'api/edge_jobs/create/file',
data: {
file: payload.File,
Name: payload.Name,
CronExpression: payload.CronExpression,
Image: payload.Image,
Endpoints: Upload.json(payload.Endpoints),
RetryCount: payload.RetryCount,
RetryInterval: payload.RetryInterval,
},
});
};
service.uploadBackup = function (file, password) {
return Upload.upload({
url: 'api/restore',
data: {
file,
password,
},
});
};
service.createSwarmStack = function (stackName, swarmId, file, env, endpointId, webhook) {
return Upload.upload({
url: `api/stacks/create/swarm/file?endpointId=${endpointId}`,
data: {
file: file,
Name: stackName,
SwarmID: swarmId,
Env: Upload.json(env),
Webhook: webhook,
},
ignoreLoadingBar: true,
});
};
service.createComposeStack = function (stackName, file, env, endpointId, webhook) {
return Upload.upload({
url: `api/stacks/create/standalone/file?endpointId=${endpointId}`,
data: {
file: file,
Name: stackName,
Env: Upload.json(env),
Webhook: webhook,
},
ignoreLoadingBar: true,
});
};
service.createEdgeStack = function createEdgeStack({ EdgeGroups, Registries, envVars, staggerConfig, ...payload }, file, dryrun) {
return Upload.upload({
url: `api/edge_stacks/create/file?dryrun=${dryrun}`,
data: {
file,
EdgeGroups: Upload.json(EdgeGroups),
Registries: Upload.json(Registries),
EnvVars: Upload.json(envVars),
StaggerConfig: Upload.json(staggerConfig),
...payload,
},
ignoreLoadingBar: true,
});
};
service.createCustomTemplate = function createCustomTemplate(data) {
return Upload.upload({
url: 'api/custom_templates/create/file',
data,
ignoreLoadingBar: true,
});
};
service.configureRegistry = function (registryId, registryManagementConfigurationModel) {
return Upload.upload({
url: 'api/registries/' + registryId + '/configure',
data: registryManagementConfigurationModel,
});
};
service.createEndpoint = function (
name,
creationType,
URL,
PublicURL,
groupID,
tagIds,
TLS,
TLSSkipVerify,
TLSSkipClientVerify,
TLSCAFile,
TLSCertFile,
TLSKeyFile,
checkinInterval,
EdgePingInterval,
EdgeSnapshotInterval,
EdgeCommandInterval
) {
return Upload.upload({
url: 'api/endpoints',
data: {
Name: name,
EndpointCreationType: creationType,
URL: URL,
PublicURL: PublicURL,
GroupID: groupID,
TagIds: Upload.json(tagIds),
TLS: TLS,
TLSSkipVerify: TLSSkipVerify,
TLSSkipClientVerify: TLSSkipClientVerify,
TLSCACertFile: TLSCAFile,
TLSCertFile: TLSCertFile,
TLSKeyFile: TLSKeyFile,
CheckinInterval: checkinInterval,
EdgePingInterval: EdgePingInterval,
EdgeSnapshotInterval: EdgeSnapshotInterval,
EdgeCommandInterval: EdgeCommandInterval,
},
ignoreLoadingBar: true,
});
};
service.createAzureEndpoint = function (name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
return Upload.upload({
url: 'api/endpoints',
data: {
Name: name,
EndpointCreationType: PortainerEndpointCreationTypes.AzureEnvironment,
GroupID: groupId,
TagIds: Upload.json(tagIds),
AzureApplicationID: applicationId,
AzureTenantID: tenantId,
AzureAuthenticationKey: authenticationKey,
},
ignoreLoadingBar: true,
});
};
service.uploadLDAPTLSFiles = function (TLSCAFile, TLSCertFile, TLSKeyFile) {
var queue = [];
if (TLSCAFile) {
queue.push(uploadFile('api/upload/tls/ca?folder=ldap', TLSCAFile));
}
if (TLSCertFile) {
queue.push(uploadFile('api/upload/tls/cert?folder=ldap', TLSCertFile));
}
if (TLSKeyFile) {
queue.push(uploadFile('api/upload/tls/key?folder=ldap', TLSKeyFile));
}
service.buildImage = function (endpointID, names, file, path) {
return Upload.http({
url: `api/endpoints/${endpointID}/docker/build`,
headers: {
'Content-Type': file.type,
},
data: file,
params: {
t: names,
dockerfile: path,
},
ignoreLoadingBar: true,
transformResponse: function (data) {
return jsonObjectsToArrayHandler(data);
},
});
};
return $q.all(queue);
};
service.buildImageFromFiles = function (endpointID, names, files) {
return Upload.upload({
url: `api/endpoints/${endpointID}/docker/build`,
headers: {
'Content-Type': 'multipart/form-data',
},
data: { file: files },
params: {
t: names,
},
transformResponse: function (data) {
return jsonObjectsToArrayHandler(data);
},
});
};
service.uploadTLSFilesForEndpoint = function (endpointID, TLSCAFile, TLSCertFile, TLSKeyFile) {
var queue = [];
service.loadImages = function (file) {
var endpointID = EndpointProvider.endpointID();
return Upload.http({
url: 'api/endpoints/' + endpointID + '/docker/images/load',
headers: {
'Content-Type': file.type,
},
data: file,
ignoreLoadingBar: true,
transformResponse: genericHandler,
});
};
if (TLSCAFile) {
queue.push(uploadFile('api/upload/tls/ca?folder=' + endpointID, TLSCAFile));
}
if (TLSCertFile) {
queue.push(uploadFile('api/upload/tls/cert?folder=' + endpointID, TLSCertFile));
}
if (TLSKeyFile) {
queue.push(uploadFile('api/upload/tls/key?folder=' + endpointID, TLSKeyFile));
}
service.createSchedule = function (payload) {
return Upload.upload({
url: 'api/edge_jobs/create/file',
data: {
file: payload.File,
Name: payload.Name,
CronExpression: payload.CronExpression,
Image: payload.Image,
Endpoints: Upload.json(payload.Endpoints),
RetryCount: payload.RetryCount,
RetryInterval: payload.RetryInterval,
},
});
};
return $q.all(queue);
};
service.uploadBackup = function (file, password) {
return Upload.upload({
url: 'api/restore',
data: {
file,
password,
},
});
};
service.uploadOwnershipVoucher = function (voucherFile) {
return Upload.upload({
url: 'api/fdo/register',
data: {
voucher: voucherFile,
},
ignoreLoadingBar: true,
});
};
service.createSwarmStack = function (stackName, swarmId, file, env, endpointId) {
return Upload.upload({
url: `api/stacks/create/swarm/file?endpointId=${endpointId}`,
data: {
file: file,
Name: stackName,
SwarmID: swarmId,
Env: Upload.json(env),
},
ignoreLoadingBar: true,
});
};
service.createComposeStack = function (stackName, file, env, endpointId) {
return Upload.upload({
url: `api/stacks/create/standalone/file?endpointId=${endpointId}`,
data: {
file: file,
Name: stackName,
Env: Upload.json(env),
},
ignoreLoadingBar: true,
});
};
service.createEdgeStack = function createEdgeStack({ EdgeGroups, envVars, ...payload }, file) {
return Upload.upload({
url: `api/edge_stacks/create/file`,
data: {
file,
EdgeGroups: Upload.json(EdgeGroups),
EnvVars: Upload.json(envVars),
...payload,
},
ignoreLoadingBar: true,
});
};
service.createCustomTemplate = function createCustomTemplate(data) {
return Upload.upload({
url: 'api/custom_templates/create/file',
data,
ignoreLoadingBar: true,
});
};
service.configureRegistry = function (registryId, registryManagementConfigurationModel) {
return Upload.upload({
url: 'api/registries/' + registryId + '/configure',
data: registryManagementConfigurationModel,
});
};
service.createEndpoint = function (
name,
creationType,
URL,
PublicURL,
groupID,
tagIds,
TLS,
TLSSkipVerify,
TLSSkipClientVerify,
TLSCAFile,
TLSCertFile,
TLSKeyFile,
checkinInterval
) {
return Upload.upload({
url: 'api/endpoints',
data: {
Name: name,
EndpointCreationType: creationType,
URL: URL,
PublicURL: PublicURL,
GroupID: groupID,
TagIds: Upload.json(tagIds),
TLS: TLS,
TLSSkipVerify: TLSSkipVerify,
TLSSkipClientVerify: TLSSkipClientVerify,
TLSCACertFile: TLSCAFile,
TLSCertFile: TLSCertFile,
TLSKeyFile: TLSKeyFile,
CheckinInterval: checkinInterval,
},
ignoreLoadingBar: true,
});
};
service.createAzureEndpoint = function (name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
return Upload.upload({
url: 'api/endpoints',
data: {
Name: name,
EndpointCreationType: PortainerEndpointCreationTypes.AzureEnvironment,
GroupID: groupId,
TagIds: Upload.json(tagIds),
AzureApplicationID: applicationId,
AzureTenantID: tenantId,
AzureAuthenticationKey: authenticationKey,
},
ignoreLoadingBar: true,
});
};
service.uploadLDAPTLSFiles = function (TLSCAFile, TLSCertFile, TLSKeyFile) {
var queue = [];
if (TLSCAFile) {
queue.push(uploadFile('api/upload/tls/ca?folder=ldap', TLSCAFile));
}
if (TLSCertFile) {
queue.push(uploadFile('api/upload/tls/cert?folder=ldap', TLSCertFile));
}
if (TLSKeyFile) {
queue.push(uploadFile('api/upload/tls/key?folder=ldap', TLSKeyFile));
}
return $q.all(queue);
};
service.uploadTLSFilesForEndpoint = function (endpointID, TLSCAFile, TLSCertFile, TLSKeyFile) {
var queue = [];
if (TLSCAFile) {
queue.push(uploadFile('api/upload/tls/ca?folder=' + endpointID, TLSCAFile));
}
if (TLSCertFile) {
queue.push(uploadFile('api/upload/tls/cert?folder=' + endpointID, TLSCertFile));
}
if (TLSKeyFile) {
queue.push(uploadFile('api/upload/tls/key?folder=' + endpointID, TLSKeyFile));
}
return $q.all(queue);
};
service.uploadOwnershipVoucher = function (voucherFile) {
return Upload.upload({
url: 'api/fdo/register',
data: {
voucher: voucherFile,
},
ignoreLoadingBar: true,
});
};
return service;
},
]);
return service;
}

View file

@ -4,9 +4,11 @@ import { apiServicesModule } from './api';
import { Notifications } from './notifications';
import { HttpRequestHelperAngular } from './http-request.helper';
import { EndpointProvider } from './endpointProvider';
import { AngularToReact } from './angularToReact';
export default angular
.module('portainer.app.services', [apiServicesModule])
.factory('Notifications', Notifications)
.factory('EndpointProvider', EndpointProvider)
.factory('HttpRequestHelper', HttpRequestHelperAngular).name;
.factory('HttpRequestHelper', HttpRequestHelperAngular)
.factory('AngularToReact', AngularToReact).name;