From f29cb7eb0c218fd2adf0c3e671e358b68aa43e47 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Tue, 28 Jul 2020 15:41:53 +1200 Subject: [PATCH 01/22] feat(core/telemetry): add posthog --- app/index.html | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/app/index.html b/app/index.html index 89e9c87ea7ab0..4d07d0c06382d 100644 --- a/app/index.html +++ b/app/index.html @@ -19,6 +19,50 @@ + + From 3d69d4423ccc9a012774c3f6230e1ed156feb214 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Tue, 28 Jul 2020 15:48:01 +1200 Subject: [PATCH 02/22] feat(core/telemetry): add posthog --- app/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/index.html b/app/index.html index 4d07d0c06382d..ce7f8158c7c71 100644 --- a/app/index.html +++ b/app/index.html @@ -61,7 +61,7 @@ }), (e.__SV = 1)); })(document, window.posthog || []); - posthog.init('6MaYoed3QRr0sfxLSWQUF4YCSSBMX8PXrngO2q7K6z8', { api_host: 'http://188.166.250.54' }); + posthog.init('LCRjxEU0Td4RLzxjJgyN1V2N8GkmALlkc_1vmUdfVbY', { api_host: 'http://188.166.250.54' }); From d487dbde59087a4136c3248db0842988634a6853 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Tue, 28 Jul 2020 16:45:53 +1200 Subject: [PATCH 03/22] feat(core/telemetry): add matomo --- app/index.html | 61 ++++++++++++++++---------------------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/app/index.html b/app/index.html index ce7f8158c7c71..0692c024bdf29 100644 --- a/app/index.html +++ b/app/index.html @@ -20,49 +20,26 @@ - + From a8572d9596229b2f153212afff081a053da2f31a Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Wed, 29 Jul 2020 09:56:40 +1200 Subject: [PATCH 04/22] feat(core/telemetry): update matomo --- app/index.html | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/index.html b/app/index.html index 0692c024bdf29..2338ca3eda7bb 100644 --- a/app/index.html +++ b/app/index.html @@ -38,6 +38,26 @@ g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s); })(); + + var currentUrl = location.href; + window.addEventListener('hashchange', function () { + _paq.push(['setReferrerUrl', currentUrl]); + currentUrl = '/' + window.location.hash.substr(1); + _paq.push(['setCustomUrl', currentUrl]); + _paq.push(['setDocumentTitle', 'My New Title']); + + // remove all previously assigned custom variables, requires Matomo (formerly Piwik) 3.0.2 + _paq.push(['deleteCustomVariables', 'page']); + _paq.push(['setGenerationTimeMs', 0]); + _paq.push(['trackPageView']); + + // make Matomo aware of newly added content + var content = document.getElementById('content'); + _paq.push(['MediaAnalytics::scanForMedia', content]); + _paq.push(['FormAnalytics::scanForForms', content]); + _paq.push(['trackContentImpressionsWithinNode', content]); + _paq.push(['enableLinkTracking']); + }); From 9ff3464237bbc4e9d1ff8e7e3c75deecebff0bf6 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Wed, 29 Jul 2020 13:44:01 +1200 Subject: [PATCH 05/22] feat(core/telemetry): update matomo --- app/__module.js | 2 ++ app/config.js | 22 +++++++++++++++++++--- app/index.html | 21 --------------------- app/portainer/__module.js | 26 +++++++++++++------------- app/vendors.js | 2 ++ package.json | 4 +++- yarn.lock | 12 ++++++++++++ 7 files changed, 51 insertions(+), 38 deletions(-) diff --git a/app/__module.js b/app/__module.js index 489ebe0a31d1f..04f4e1e4fdd28 100644 --- a/app/__module.js +++ b/app/__module.js @@ -37,6 +37,8 @@ angular.module('portainer', [ 'portainer.integrations', 'rzModule', 'moment-picker', + 'angulartics', + 'angulartics.piwik', ]); if (require) { diff --git a/app/config.js b/app/config.js index 0f8d8234e6e01..654f0f4d9fa26 100644 --- a/app/config.js +++ b/app/config.js @@ -3,6 +3,7 @@ import { Terminal } from 'xterm'; import * as fit from 'xterm/lib/addons/fit/fit'; angular.module('portainer').config([ + '$analyticsProvider', '$urlRouterProvider', '$httpProvider', 'localStorageServiceProvider', @@ -11,7 +12,17 @@ angular.module('portainer').config([ '$uibTooltipProvider', '$compileProvider', 'cfpLoadingBarProvider', - function ($urlRouterProvider, $httpProvider, localStorageServiceProvider, jwtOptionsProvider, AnalyticsProvider, $uibTooltipProvider, $compileProvider, cfpLoadingBarProvider) { + function ( + $analyticsProvider, + $urlRouterProvider, + $httpProvider, + localStorageServiceProvider, + jwtOptionsProvider, + AnalyticsProvider, + $uibTooltipProvider, + $compileProvider, + cfpLoadingBarProvider + ) { 'use strict'; var environment = '@@ENVIRONMENT'; @@ -52,8 +63,13 @@ angular.module('portainer').config([ }, ]); - AnalyticsProvider.setAccount({ tracker: __CONFIG_GA_ID, set: { anonymizeIp: true } }); - AnalyticsProvider.startOffline(true); + $analyticsProvider.withBase(false); + $analyticsProvider.withAutoBase(true); + + // need to set trackRelativePath + + // AnalyticsProvider.setAccount({ tracker: __CONFIG_GA_ID, set: { anonymizeIp: true } }); + // AnalyticsProvider.startOffline(true); toastr.options.timeOut = 3000; diff --git a/app/index.html b/app/index.html index 2338ca3eda7bb..a60a36a474cd6 100644 --- a/app/index.html +++ b/app/index.html @@ -24,7 +24,6 @@ diff --git a/app/portainer/__module.js b/app/portainer/__module.js index 9bb8fb74b6a0f..2b318b4576f9d 100644 --- a/app/portainer/__module.js +++ b/app/portainer/__module.js @@ -15,15 +15,15 @@ async function initAuthentication(authManager, Authentication, $rootScope, $stat await Authentication.init(); } -function initAnalytics(Analytics, $rootScope) { - Analytics.offline(false); - Analytics.registerScriptTags(); - Analytics.registerTrackers(); - $rootScope.$on('$stateChangeSuccess', function (event, toState) { - Analytics.trackPage(toState.url); - Analytics.pageView(); - }); -} +// function initAnalytics(Analytics, $rootScope) { +// Analytics.offline(false); +// Analytics.registerScriptTags(); +// Analytics.registerTrackers(); +// $rootScope.$on('$stateChangeSuccess', function (event, toState) { +// Analytics.trackPage(toState.url); +// Analytics.pageView(); +// }); +// } angular.module('portainer.app', ['portainer.oauth']).config([ '$stateRegistryProvider', @@ -51,10 +51,10 @@ angular.module('portainer.app', ['portainer.oauth']).config([ deferred.resolve(); } else { StateManager.initialize() - .then(function success(state) { - if (state.application.analytics) { - initAnalytics(Analytics, $rootScope); - } + .then(function success() { + // if (state.application.analytics) { + // initAnalytics(Analytics, $rootScope); + // } return $async(initAuthentication, authManager, Authentication, $rootScope, $state); }) .then(() => deferred.resolve()) diff --git a/app/vendors.js b/app/vendors.js index c925c910f622b..00b7032d902b5 100644 --- a/app/vendors.js +++ b/app/vendors.js @@ -37,5 +37,7 @@ import 'js-yaml/dist/js-yaml.js'; import 'angular-ui-bootstrap'; import 'angular-moment-picker'; import 'angular-multiselect/isteven-multi-select.js'; +import 'angulartics/dist/angulartics.min.js'; +import 'angulartics-piwik/dist/angulartics-piwik.min.js'; window.angular = angular; diff --git a/package.json b/package.json index 077ecadc2660c..2e98ed01c7813 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,8 @@ "angular-utils-pagination": "~0.11.1", "angularjs-scroll-glue": "^2.2.0", "angularjs-slider": "^6.4.0", + "angulartics": "^1.6.0", + "angulartics-piwik": "^1.0.6", "babel-plugin-angularjs-annotate": "^0.10.0", "bootbox": "^5.4.0", "bootstrap": "^3.4.0", @@ -162,4 +164,4 @@ "*.js": "eslint --cache --fix", "*.{js,css,md,html}": "prettier --write" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index c7cc60ff1367f..16256812cbee8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1301,6 +1301,18 @@ angularjs-slider@^6.4.0: resolved "https://registry.yarnpkg.com/angularjs-slider/-/angularjs-slider-6.7.0.tgz#eb2229311b81b79315a36e7b5eb700e128f50319" integrity sha512-Cizsuax65wN2Y+htmA3safE5ALOSCyWcKyWkziaO8vCVymi26bQQs6kKDhkYc8GFix/KE7Oc9gH3QLlTUgD38w== +angulartics-piwik@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/angulartics-piwik/-/angulartics-piwik-1.0.6.tgz#cf050b65e8974f3f9ae9a2a1cef4bc1cac82099f" + integrity sha1-zwULZeiXTz+a6aKhzvS8HKyCCZ8= + dependencies: + angulartics "*" + +angulartics@*, angulartics@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/angulartics/-/angulartics-1.6.0.tgz#a89c17ef8ea2334ebced65d6265951846f848172" + integrity sha512-fywhCi1InawcX+rpLv9NQ32Ed87KoZeH20SUIsRUz9dYJSxuk4/uxiKiopITveGxTC8THYHFEATj9Y/X+BvMqA== + ansi-colors@^3.0.0, ansi-colors@^3.2.1: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" From 26ebbc423d0a01635b8afd81a0857ca3eb3557c0 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Thu, 30 Jul 2020 17:24:30 +1200 Subject: [PATCH 06/22] feat(core/telemetry): update matomo --- app/config.js | 17 +++-------------- app/vendors.js | 2 +- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/app/config.js b/app/config.js index 654f0f4d9fa26..416235d6c2e8d 100644 --- a/app/config.js +++ b/app/config.js @@ -3,7 +3,6 @@ import { Terminal } from 'xterm'; import * as fit from 'xterm/lib/addons/fit/fit'; angular.module('portainer').config([ - '$analyticsProvider', '$urlRouterProvider', '$httpProvider', 'localStorageServiceProvider', @@ -12,17 +11,7 @@ angular.module('portainer').config([ '$uibTooltipProvider', '$compileProvider', 'cfpLoadingBarProvider', - function ( - $analyticsProvider, - $urlRouterProvider, - $httpProvider, - localStorageServiceProvider, - jwtOptionsProvider, - AnalyticsProvider, - $uibTooltipProvider, - $compileProvider, - cfpLoadingBarProvider - ) { + function ($urlRouterProvider, $httpProvider, localStorageServiceProvider, jwtOptionsProvider, AnalyticsProvider, $uibTooltipProvider, $compileProvider, cfpLoadingBarProvider) { 'use strict'; var environment = '@@ENVIRONMENT'; @@ -63,8 +52,8 @@ angular.module('portainer').config([ }, ]); - $analyticsProvider.withBase(false); - $analyticsProvider.withAutoBase(true); + // $analyticsProvider.withBase(false); + // $analyticsProvider.withAutoBase(true); // need to set trackRelativePath diff --git a/app/vendors.js b/app/vendors.js index 00b7032d902b5..53f6e6d671e39 100644 --- a/app/vendors.js +++ b/app/vendors.js @@ -38,6 +38,6 @@ import 'angular-ui-bootstrap'; import 'angular-moment-picker'; import 'angular-multiselect/isteven-multi-select.js'; import 'angulartics/dist/angulartics.min.js'; -import 'angulartics-piwik/dist/angulartics-piwik.min.js'; +import 'angulartics-piwik/src/angulartics-piwik.js'; window.angular = angular; From abbcd64ec15e1b9fe2de174c637a3de932cc08c9 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 10:32:30 +0300 Subject: [PATCH 07/22] feat(telemetry): remove google analytics code --- .eslintrc.yml | 1 - app/__module.js | 1 - app/config.js | 11 +---------- app/portainer/__module.js | 16 +--------------- app/portainer/models/status.js | 1 - app/portainer/services/stateManager.js | 1 - app/vendors.js | 1 - package.json | 4 ---- webpack/webpack.common.js | 5 +---- yarn.lock | 4 ---- 10 files changed, 3 insertions(+), 42 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index b6829dd816cc2..9a3201f9630b7 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -6,7 +6,6 @@ env: globals: angular: true - __CONFIG_GA_ID: true extends: - 'eslint:recommended' diff --git a/app/__module.js b/app/__module.js index 04f4e1e4fdd28..06a243347b2c4 100644 --- a/app/__module.js +++ b/app/__module.js @@ -21,7 +21,6 @@ angular.module('portainer', [ 'angularUtils.directives.dirPagination', 'LocalStorageModule', 'angular-jwt', - 'angular-google-analytics', 'angular-json-tree', 'angular-loading-bar', 'angular-clipboard', diff --git a/app/config.js b/app/config.js index 416235d6c2e8d..5e74a916d2bcf 100644 --- a/app/config.js +++ b/app/config.js @@ -7,11 +7,10 @@ angular.module('portainer').config([ '$httpProvider', 'localStorageServiceProvider', 'jwtOptionsProvider', - 'AnalyticsProvider', '$uibTooltipProvider', '$compileProvider', 'cfpLoadingBarProvider', - function ($urlRouterProvider, $httpProvider, localStorageServiceProvider, jwtOptionsProvider, AnalyticsProvider, $uibTooltipProvider, $compileProvider, cfpLoadingBarProvider) { + function ($urlRouterProvider, $httpProvider, localStorageServiceProvider, jwtOptionsProvider, $uibTooltipProvider, $compileProvider, cfpLoadingBarProvider) { 'use strict'; var environment = '@@ENVIRONMENT'; @@ -52,14 +51,6 @@ angular.module('portainer').config([ }, ]); - // $analyticsProvider.withBase(false); - // $analyticsProvider.withAutoBase(true); - - // need to set trackRelativePath - - // AnalyticsProvider.setAccount({ tracker: __CONFIG_GA_ID, set: { anonymizeIp: true } }); - // AnalyticsProvider.startOffline(true); - toastr.options.timeOut = 3000; Terminal.applyAddon(fit); diff --git a/app/portainer/__module.js b/app/portainer/__module.js index 2b318b4576f9d..b312bcd408cbe 100644 --- a/app/portainer/__module.js +++ b/app/portainer/__module.js @@ -15,16 +15,6 @@ async function initAuthentication(authManager, Authentication, $rootScope, $stat await Authentication.init(); } -// function initAnalytics(Analytics, $rootScope) { -// Analytics.offline(false); -// Analytics.registerScriptTags(); -// Analytics.registerTrackers(); -// $rootScope.$on('$stateChangeSuccess', function (event, toState) { -// Analytics.trackPage(toState.url); -// Analytics.pageView(); -// }); -// } - angular.module('portainer.app', ['portainer.oauth']).config([ '$stateRegistryProvider', function ($stateRegistryProvider) { @@ -38,13 +28,12 @@ angular.module('portainer.app', ['portainer.oauth']).config([ 'StateManager', 'Authentication', 'Notifications', - 'Analytics', 'authManager', '$rootScope', '$state', '$async', '$q', - (StateManager, Authentication, Notifications, Analytics, authManager, $rootScope, $state, $async, $q) => { + (StateManager, Authentication, Notifications, authManager, $rootScope, $state, $async, $q) => { const deferred = $q.defer(); const appState = StateManager.getState(); if (!appState.loading) { @@ -52,9 +41,6 @@ angular.module('portainer.app', ['portainer.oauth']).config([ } else { StateManager.initialize() .then(function success() { - // if (state.application.analytics) { - // initAnalytics(Analytics, $rootScope); - // } return $async(initAuthentication, authManager, Authentication, $rootScope, $state); }) .then(() => deferred.resolve()) diff --git a/app/portainer/models/status.js b/app/portainer/models/status.js index 20057e382bdd1..96652cd6d4fa2 100644 --- a/app/portainer/models/status.js +++ b/app/portainer/models/status.js @@ -1,7 +1,6 @@ export function StatusViewModel(data) { this.Authentication = data.Authentication; this.Snapshot = data.Snapshot; - this.Analytics = data.Analytics; this.Version = data.Version; } diff --git a/app/portainer/services/stateManager.js b/app/portainer/services/stateManager.js index 7b28dfc86dff1..44128c98c95bc 100644 --- a/app/portainer/services/stateManager.js +++ b/app/portainer/services/stateManager.js @@ -107,7 +107,6 @@ angular.module('portainer.app').factory('StateManager', [ }; function assignStateFromStatusAndSettings(status, settings) { - state.application.analytics = status.Analytics; state.application.version = status.Version; state.application.logo = settings.LogoURL; state.application.snapshotInterval = settings.SnapshotInterval; diff --git a/app/vendors.js b/app/vendors.js index 53f6e6d671e39..f76079b4e1005 100644 --- a/app/vendors.js +++ b/app/vendors.js @@ -25,7 +25,6 @@ import 'angular-resource'; import 'angular-utils-pagination'; import 'angular-local-storage'; import 'angular-jwt'; -import 'angular-google-analytics'; import 'angular-json-tree'; import 'angular-loading-bar'; import 'angular-clipboard'; diff --git a/package.json b/package.json index 2e98ed01c7813..8890b3910d509 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,6 @@ "bugs": { "url": "https://github.com/portainer/portainer/issues" }, - "config": { - "GA_ID": "UA-84944922-2" - }, "licenses": [ { "type": "Zlib", @@ -56,7 +53,6 @@ "angular": "1.8.0", "angular-clipboard": "^1.6.2", "angular-file-saver": "^1.1.3", - "angular-google-analytics": "github:revolunet/angular-google-analytics#semver:~1.1.9", "angular-json-tree": "1.0.1", "angular-jwt": "~0.1.8", "angular-loading-bar": "~0.9.0", diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index f0258a201112e..461ebbcb51220 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -1,5 +1,5 @@ const path = require('path'); -const { ProvidePlugin, IgnorePlugin, DefinePlugin } = require('webpack'); +const { ProvidePlugin, IgnorePlugin } = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const WebpackBuildNotifierPlugin = require('webpack-build-notifier'); const CleanTerminalPlugin = require('clean-terminal-webpack-plugin'); @@ -92,9 +92,6 @@ module.exports = { collections: true, paths: true, }), - new DefinePlugin({ - __CONFIG_GA_ID: JSON.stringify(pkg.config.GA_ID), - }), ], optimization: { splitChunks: { diff --git a/yarn.lock b/yarn.lock index 16256812cbee8..79eb4e074913d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1205,10 +1205,6 @@ angular-file-saver@^1.1.3: blob-tmp "^1.0.0" file-saver "^1.3.3" -"angular-google-analytics@github:revolunet/angular-google-analytics#semver:~1.1.9": - version "1.1.8" - resolved "https://codeload.github.com/revolunet/angular-google-analytics/tar.gz/92768a525870bc066dcf85fbe9d9f115358a6d91" - angular-json-tree@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/angular-json-tree/-/angular-json-tree-1.0.1.tgz#61b6e76ab165130335d9ec46fa572eb99604de51" From eef4d029fb36528c246ea2357737dae7db58999b Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 10:34:59 +0300 Subject: [PATCH 08/22] refactor(telemetry): move matomo code to bundle --- app/__module.js | 2 +- app/index.html | 20 -------------------- app/matomo.js | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 21 deletions(-) create mode 100644 app/matomo.js diff --git a/app/__module.js b/app/__module.js index 06a243347b2c4..be34176944fbe 100644 --- a/app/__module.js +++ b/app/__module.js @@ -2,7 +2,7 @@ import './assets/css'; import '@babel/polyfill'; import angular from 'angular'; - +import './matomo'; import './agent'; import './azure/_module'; import './docker/__module'; diff --git a/app/index.html b/app/index.html index a60a36a474cd6..89e9c87ea7ab0 100644 --- a/app/index.html +++ b/app/index.html @@ -19,26 +19,6 @@ - - - - diff --git a/app/matomo.js b/app/matomo.js new file mode 100644 index 0000000000000..a580503fe709d --- /dev/null +++ b/app/matomo.js @@ -0,0 +1,14 @@ +const _paq = (window._paq = window._paq || []); +/* tracker methods like "setCustomDimension" should be called before "trackPageView" */ +_paq.push(['enableLinkTracking']); + +const u = '//188.166.250.54/'; +_paq.push(['setTrackerUrl', u + 'matomo.php']); +_paq.push(['setSiteId', '1']); +const d = document, + g = d.createElement('script'), + s = d.getElementsByTagName('script')[0]; +g.type = 'text/javascript'; +g.async = true; +g.src = u + 'matomo.js'; +s.parentNode.insertBefore(g, s); From c1951914429254683ddb7525029c0743c11073f9 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 11:34:32 +0300 Subject: [PATCH 09/22] refactor(telemetry): move matomo lib to assets --- app/__module.js | 7 +- app/assets/js/angulartics-matomo.js | 223 ++++++++++++++++++++++++++++ app/{matomo.js => matomo-setup.js} | 0 app/vendors.js | 1 - package.json | 1 - 5 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 app/assets/js/angulartics-matomo.js rename app/{matomo.js => matomo-setup.js} (100%) diff --git a/app/__module.js b/app/__module.js index be34176944fbe..87b5ef59331dc 100644 --- a/app/__module.js +++ b/app/__module.js @@ -2,7 +2,10 @@ import './assets/css'; import '@babel/polyfill'; import angular from 'angular'; -import './matomo'; + +import './matomo-setup'; +import './assets/js/angulartics-matomo'; + import './agent'; import './azure/_module'; import './docker/__module'; @@ -37,7 +40,7 @@ angular.module('portainer', [ 'rzModule', 'moment-picker', 'angulartics', - 'angulartics.piwik', + 'angulartics.matomo', ]); if (require) { diff --git a/app/assets/js/angulartics-matomo.js b/app/assets/js/angulartics-matomo.js new file mode 100644 index 0000000000000..ad25bbac73496 --- /dev/null +++ b/app/assets/js/angulartics-matomo.js @@ -0,0 +1,223 @@ +import angular from 'angular'; + +// forked from https://github.com/angulartics/angulartics-piwik/blob/master/src/angulartics-piwik.js + +/* global _paq */ +/** + * @ngdoc overview + * @name angulartics.piwik + * Enables analytics support for Piwik/Matomo (http://piwik.org/docs/tracking-api/) + */ +angular.module('angulartics.matomo', ['angulartics']).config([ + '$analyticsProvider', + '$windowProvider', + function ($analyticsProvider, $windowProvider) { + var $window = $windowProvider.$get(); + + $analyticsProvider.settings.pageTracking.trackRelativePath = true; + + // Add piwik specific trackers to angulartics API + + // Requires the CustomDimensions plugin for Piwik. + $analyticsProvider.api.setCustomDimension = function (dimensionId, value) { + if ($window._paq) { + $window._paq.push(['setCustomDimension', dimensionId, value]); + } + }; + + // Requires the CustomDimensions plugin for Piwik. + $analyticsProvider.api.deleteCustomDimension = function (dimensionId) { + if ($window._paq) { + $window._paq.push(['deleteCustomDimension', dimensionId]); + } + }; + + // scope: visit or page. Defaults to 'page' + $analyticsProvider.api.setCustomVariable = function (varIndex, varName, value, scope) { + if ($window._paq) { + scope = scope || 'page'; + $window._paq.push(['setCustomVariable', varIndex, varName, value, scope]); + } + }; + + // scope: visit or page. Defaults to 'page' + $analyticsProvider.api.deleteCustomVariable = function (varIndex, scope) { + if ($window._paq) { + scope = scope || 'page'; + $window._paq.push(['deleteCustomVariable', varIndex, scope]); + } + }; + + // trackSiteSearch(keyword, category, [searchCount]) + $analyticsProvider.api.trackSiteSearch = function (keyword, category, searchCount) { + // keyword is required + if ($window._paq && keyword) { + var params = ['trackSiteSearch', keyword, category || false]; + + // searchCount is optional + if (angular.isDefined(searchCount)) { + params.push(searchCount); + } + + $window._paq.push(params); + } + }; + + // logs a conversion for goal 1. revenue is optional + // trackGoal(goalID, [revenue]); + $analyticsProvider.api.trackGoal = function (goalID, revenue) { + if ($window._paq) { + _paq.push(['trackGoal', goalID, revenue || 0]); + } + }; + + // track outlink or download + // linkType is 'link' or 'download', 'link' by default + // trackLink(url, [linkType]); + $analyticsProvider.api.trackLink = function (url, linkType) { + var type = linkType || 'link'; + if ($window._paq) { + $window._paq.push(['trackLink', url, type]); + } + }; + + // Set default angulartics page and event tracking + + $analyticsProvider.registerSetUsername(function (username) { + if ($window._paq) { + $window._paq.push(['setUserId', username]); + } + }); + + // locationObj is the angular $location object + $analyticsProvider.registerPageTrack(function (path) { + if ($window._paq) { + $window._paq.push(['setDocumentTitle', $window.document.title]); + $window._paq.push(['setReferrerUrl', '']); + $window._paq.push(['setCustomUrl', 'http://portainer.app' + path]); + $window._paq.push(['trackPageView']); + } + }); + + /** + * @name eventTrack + * Track a basic event in Piwik, or send an ecommerce event. + * + * @param {string} action A string corresponding to the type of event that needs to be tracked. + * @param {object} properties The properties that need to be logged with the event. + */ + $analyticsProvider.registerEventTrack(function (action, properties) { + if ($window._paq) { + properties = properties || {}; + + switch (action) { + /** + * @description Sets the current page view as a product or category page view. When you call + * setEcommerceView it must be followed by a call to trackPageView to record the product or + * category page view. + * + * @link https://piwik.org/docs/ecommerce-analytics/#tracking-product-page-views-category-page-views-optional + * @link https://developer.piwik.org/api-reference/tracking-javascript#ecommerce + * + * @property productSKU (required) SKU: Product unique identifier + * @property productName (optional) Product name + * @property categoryName (optional) Product category, or array of up to 5 categories + * @property price (optional) Product Price as displayed on the page + */ + case 'setEcommerceView': + $window._paq.push(['setEcommerceView', properties.productSKU, properties.productName, properties.categoryName, properties.price]); + break; + + /** + * @description Adds a product into the ecommerce order. Must be called for each product in + * the order. + * + * @link https://piwik.org/docs/ecommerce-analytics/#tracking-ecommerce-orders-items-purchased-required + * @link https://developer.piwik.org/api-reference/tracking-javascript#ecommerce + * + * @property productSKU (required) SKU: Product unique identifier + * @property productName (optional) Product name + * @property categoryName (optional) Product category, or array of up to 5 categories + * @property price (recommended) Product price + * @property quantity (optional, default to 1) Product quantity + */ + case 'addEcommerceItem': + $window._paq.push(['addEcommerceItem', properties.productSKU, properties.productName, properties.productCategory, properties.price, properties.quantity]); + break; + + /** + * @description Tracks a shopping cart. Call this javascript function every time a user is + * adding, updating or deleting a product from the cart. + * + * @link https://piwik.org/docs/ecommerce-analytics/#tracking-add-to-cart-items-added-to-the-cart-optional + * @link https://developer.piwik.org/api-reference/tracking-javascript#ecommerce + * + * @property grandTotal (required) Cart amount + */ + case 'trackEcommerceCartUpdate': + $window._paq.push(['trackEcommerceCartUpdate', properties.grandTotal]); + break; + + /** + * @description Tracks an Ecommerce order, including any ecommerce item previously added to + * the order. orderId and grandTotal (ie. revenue) are required parameters. + * + * @link https://piwik.org/docs/ecommerce-analytics/#tracking-ecommerce-orders-items-purchased-required + * @link https://developer.piwik.org/api-reference/tracking-javascript#ecommerce + * + * @property orderId (required) Unique Order ID + * @property grandTotal (required) Order Revenue grand total (includes tax, shipping, and subtracted discount) + * @property subTotal (optional) Order sub total (excludes shipping) + * @property tax (optional) Tax amount + * @property shipping (optional) Shipping amount + * @property discount (optional) Discount offered (set to false for unspecified parameter) + */ + case 'trackEcommerceOrder': + $window._paq.push(['trackEcommerceOrder', properties.orderId, properties.grandTotal, properties.subTotal, properties.tax, properties.shipping, properties.discount]); + break; + + /** + * @description Logs an event with an event category (Videos, Music, Games...), an event + * action (Play, Pause, Duration, Add Playlist, Downloaded, Clicked...), and an optional + * event name and optional numeric value. + * + * @link https://piwik.org/docs/event-tracking/ + * @link https://developer.piwik.org/api-reference/tracking-javascript#using-the-tracker-object + * + * @property category + * @property action + * @property name (optional, recommended) + * @property value (optional) + */ + default: + // PAQ requires that eventValue be an integer, see: http://piwik.org/docs/event-tracking + if (properties.value) { + var parsed = parseInt(properties.value, 10); + properties.value = isNaN(parsed) ? 0 : parsed; + } + + $window._paq.push([ + 'trackEvent', + properties.category, + action, + properties.name || properties.label, // Changed in favour of Piwik documentation. Added fallback so it's backwards compatible. + properties.value, + ]); + } + } + }); + + /** + * @name exceptionTrack + * Sugar on top of the eventTrack method for easily handling errors + * + * @param {object} error An Error object to track: error.toString() used for event 'action', error.stack used for event 'label'. + * @param {object} cause The cause of the error given from $exceptionHandler, not used. + */ + $analyticsProvider.registerExceptionTrack(function (error) { + if ($window._paq) { + $window._paq.push(['trackEvent', 'Exceptions', error.toString(), error.stack, 0]); + } + }); + }, +]); diff --git a/app/matomo.js b/app/matomo-setup.js similarity index 100% rename from app/matomo.js rename to app/matomo-setup.js diff --git a/app/vendors.js b/app/vendors.js index f76079b4e1005..8bdc36d0d7b11 100644 --- a/app/vendors.js +++ b/app/vendors.js @@ -37,6 +37,5 @@ import 'angular-ui-bootstrap'; import 'angular-moment-picker'; import 'angular-multiselect/isteven-multi-select.js'; import 'angulartics/dist/angulartics.min.js'; -import 'angulartics-piwik/src/angulartics-piwik.js'; window.angular = angular; diff --git a/package.json b/package.json index 8890b3910d509..145b540dfad9c 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,6 @@ "angularjs-scroll-glue": "^2.2.0", "angularjs-slider": "^6.4.0", "angulartics": "^1.6.0", - "angulartics-piwik": "^1.0.6", "babel-plugin-angularjs-annotate": "^0.10.0", "bootbox": "^5.4.0", "bootstrap": "^3.4.0", From 5131a8a3ba231e879c0d954883f36f59412b2a8f Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 11:35:02 +0300 Subject: [PATCH 10/22] refactor(telemetry): depreciate --no-analytics --- api/cli/cli.go | 7 +- api/cmd/portainer/main.go | 3 +- api/portainer.go | 3 +- api/swagger.yaml | 5088 +++++++++++++------------- app/app.js | 6 +- app/portainer/views/about/about.html | 14 +- gruntfile.js | 4 +- yarn.lock | 9 +- 8 files changed, 2560 insertions(+), 2574 deletions(-) diff --git a/api/cli/cli.go b/api/cli/cli.go index 4bcec03fd9c54..afc0656bc0984 100644 --- a/api/cli/cli.go +++ b/api/cli/cli.go @@ -2,6 +2,7 @@ package cli import ( "errors" + "log" "time" "github.com/portainer/portainer/api" @@ -35,7 +36,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) { Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(), EndpointURL: kingpin.Flag("host", "Endpoint URL").Short('H').String(), EnableEdgeComputeFeatures: kingpin.Flag("edge-compute", "Enable Edge Compute features").Bool(), - NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app").Default(defaultNoAnalytics).Bool(), + NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app (depreciated)").Default(defaultNoAnalytics).Bool(), TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(), TLSSkipVerify: kingpin.Flag("tlsskipverify", "Disable TLS server verification").Default(defaultTLSSkipVerify).Bool(), TLSCacert: kingpin.Flag("tlscacert", "Path to the CA").Default(defaultTLSCACertPath).String(), @@ -88,7 +89,9 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error { } func displayDeprecationWarnings(flags *portainer.CLIFlags) { - + if flags.NoAnalytics != nil { + log.Println("Warning: The --no-analytics has been depreciated and will be removed in a future version of Portainer. It has currently no effect, telemetry settings are available in the Portainer settings.") + } } func validateEndpointURL(endpointURL string) error { diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index e3b2d0da93d21..4064df9230ab0 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -154,8 +154,7 @@ func loadEdgeJobsFromDatabase(dataStore portainer.DataStore, reverseTunnelServic func initStatus(flags *portainer.CLIFlags) *portainer.Status { return &portainer.Status{ - Analytics: !*flags.NoAnalytics, - Version: portainer.APIVersion, + Version: portainer.APIVersion, } } diff --git a/api/portainer.go b/api/portainer.go index 36706181661e7..bf3efd5340329 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -566,8 +566,7 @@ type ( // Status represents the application status Status struct { - Analytics bool `json:"Analytics"` - Version string `json:"Version"` + Version string `json:"Version"` } // Tag represents a tag that can be associated to a resource diff --git a/api/swagger.yaml b/api/swagger.yaml index 2ecd61139d1f1..1e29c9fae9b97 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -1,5 +1,5 @@ --- -swagger: "2.0" +swagger: '2.0' info: description: | Portainer API is an HTTP API served by Portainer. It is used by the Portainer UI and everything you can do with the UI can be done using the HTTP API. @@ -54,4725 +54,4721 @@ info: **NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8). - version: "2.0.0-dev" - title: "Portainer API" + version: '2.0.0-dev' + title: 'Portainer API' contact: - email: "info@portainer.io" -host: "portainer.domain" -basePath: "/api" + email: 'info@portainer.io' +host: 'portainer.domain' +basePath: '/api' tags: -- name: "auth" - description: "Authenticate against Portainer HTTP API" -- name: "dockerhub" - description: "Manage how Portainer connects to the DockerHub" -- name: "endpoints" - description: "Manage Docker environments" -- name: "endpoint_groups" - description: "Manage endpoint groups" -- name: "extensions" - description: "Manage extensions" -- name: "registries" - description: "Manage Docker registries" -- name: "resource_controls" - description: "Manage access control on Docker resources" -- name: "roles" - description: "Manage roles" -- name: "settings" - description: "Manage Portainer settings" -- name: "status" - description: "Information about the Portainer instance" -- name: "stacks" - description: "Manage Docker stacks" -- name: "users" - description: "Manage users" -- name: "tags" - description: "Manage tags" -- name: "teams" - description: "Manage teams" -- name: "team_memberships" - description: "Manage team memberships" -- name: "templates" - description: "Manage App Templates" -- name: "stacks" - description: "Manage stacks" -- name: "upload" - description: "Upload files" -- name: "websocket" - description: "Create exec sessions using websockets" + - name: 'auth' + description: 'Authenticate against Portainer HTTP API' + - name: 'dockerhub' + description: 'Manage how Portainer connects to the DockerHub' + - name: 'endpoints' + description: 'Manage Docker environments' + - name: 'endpoint_groups' + description: 'Manage endpoint groups' + - name: 'extensions' + description: 'Manage extensions' + - name: 'registries' + description: 'Manage Docker registries' + - name: 'resource_controls' + description: 'Manage access control on Docker resources' + - name: 'roles' + description: 'Manage roles' + - name: 'settings' + description: 'Manage Portainer settings' + - name: 'status' + description: 'Information about the Portainer instance' + - name: 'stacks' + description: 'Manage Docker stacks' + - name: 'users' + description: 'Manage users' + - name: 'tags' + description: 'Manage tags' + - name: 'teams' + description: 'Manage teams' + - name: 'team_memberships' + description: 'Manage team memberships' + - name: 'templates' + description: 'Manage App Templates' + - name: 'stacks' + description: 'Manage stacks' + - name: 'upload' + description: 'Upload files' + - name: 'websocket' + description: 'Create exec sessions using websockets' schemes: -- "http" -- "https" + - 'http' + - 'https' paths: /auth: post: tags: - - "auth" - summary: "Authenticate a user" + - 'auth' + summary: 'Authenticate a user' description: | Use this endpoint to authenticate against Portainer using a username and password. **Access policy**: public - operationId: "AuthenticateUser" + operationId: 'AuthenticateUser' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' parameters: - - in: "body" - name: "body" - description: "Credentials used for authentication" - required: true - schema: - $ref: "#/definitions/AuthenticateUserRequest" + - in: 'body' + name: 'body' + description: 'Credentials used for authentication' + required: true + schema: + $ref: '#/definitions/AuthenticateUserRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/AuthenticateUserResponse" + $ref: '#/definitions/AuthenticateUserResponse' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid credentials" + err: 'Invalid credentials' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 503: - description: "Authentication disabled" + description: 'Authentication disabled' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Authentication is disabled" + err: 'Authentication is disabled' /dockerhub: get: tags: - - "dockerhub" - summary: "Retrieve DockerHub information" + - 'dockerhub' + summary: 'Retrieve DockerHub information' description: | Use this endpoint to retrieve the information used to connect to the DockerHub **Access policy**: authenticated - operationId: "DockerHubInspect" + operationId: 'DockerHubInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/DockerHubSubset" + $ref: '#/definitions/DockerHubSubset' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "dockerhub" - summary: "Update DockerHub information" + - 'dockerhub' + summary: 'Update DockerHub information' description: | Use this endpoint to update the information used to connect to the DockerHub **Access policy**: administrator - operationId: "DockerHubUpdate" + operationId: 'DockerHubUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "DockerHub information" - required: true - schema: - $ref: "#/definitions/DockerHubUpdateRequest" + - in: 'body' + name: 'body' + description: 'DockerHub information' + required: true + schema: + $ref: '#/definitions/DockerHubUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/DockerHub" + $ref: '#/definitions/DockerHub' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /endpoints: get: tags: - - "endpoints" - summary: "List endpoints" + - 'endpoints' + summary: 'List endpoints' description: | List all endpoints based on the current user authorizations. Will return all endpoints if using an administrator account otherwise it will only return authorized endpoints. **Access policy**: restricted - operationId: "EndpointList" + operationId: 'EndpointList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/EndpointListResponse" + $ref: '#/definitions/EndpointListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "endpoints" - summary: "Create a new endpoint" + - 'endpoints' + summary: 'Create a new endpoint' description: | Create a new endpoint that will be used to manage a Docker environment. **Access policy**: administrator - operationId: "EndpointCreate" + operationId: 'EndpointCreate' consumes: - - "multipart/form-data" + - 'multipart/form-data' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "Name" - in: "formData" - type: "string" - description: "Name that will be used to identify this endpoint (example: my-endpoint)" - required: true - - name: "EndpointType" - in: "formData" - type: "integer" - description: "Environment type. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge agent environment)" - required: true - - name: "URL" - in: "formData" - type: "string" - description: "URL or IP address of a Docker host (example: docker.mydomain.tld:2375).\ - \ Defaults to local if not specified (Linux: /var/run/docker.sock, Windows: //./pipe/docker_engine)" - - name: "PublicURL" - in: "formData" - type: "string" - description: "URL or IP address where exposed containers will be reachable.\ - \ Defaults to URL if not specified (example: docker.mydomain.tld:2375)" - - name: "GroupID" - in: "formData" - type: "string" - description: "Endpoint group identifier. If not specified will default to 1 (unassigned)." - - name: "TLS" - in: "formData" - type: "string" - description: "Require TLS to connect against this endpoint (example: true)" - - name: "TLSSkipVerify" - in: "formData" - type: "string" - description: "Skip server verification when using TLS (example: false)" - - name: "TLSSkipClientVerify" - in: "formData" - type: "string" - description: "Skip client verification when using TLS (example: false)" - - name: "TLSCACertFile" - in: "formData" - type: "file" - description: "TLS CA certificate file" - - name: "TLSCertFile" - in: "formData" - type: "file" - description: "TLS client certificate file" - - name: "TLSKeyFile" - in: "formData" - type: "file" - description: "TLS client key file" - - name: "AzureApplicationID" - in: "formData" - type: "string" - description: "Azure application ID. Required if endpoint type is set to 3" - - name: "AzureTenantID" - in: "formData" - type: "string" - description: "Azure tenant ID. Required if endpoint type is set to 3" - - name: "AzureAuthenticationKey" - in: "formData" - type: "string" - description: "Azure authentication key. Required if endpoint type is set to 3" + - name: 'Name' + in: 'formData' + type: 'string' + description: 'Name that will be used to identify this endpoint (example: my-endpoint)' + required: true + - name: 'EndpointType' + in: 'formData' + type: 'integer' + description: 'Environment type. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge agent environment)' + required: true + - name: 'URL' + in: 'formData' + type: 'string' + description: "URL or IP address of a Docker host (example: docker.mydomain.tld:2375).\ + \ Defaults to local if not specified (Linux: /var/run/docker.sock, Windows: //./pipe/docker_engine)" + - name: 'PublicURL' + in: 'formData' + type: 'string' + description: "URL or IP address where exposed containers will be reachable.\ + \ Defaults to URL if not specified (example: docker.mydomain.tld:2375)" + - name: 'GroupID' + in: 'formData' + type: 'string' + description: 'Endpoint group identifier. If not specified will default to 1 (unassigned).' + - name: 'TLS' + in: 'formData' + type: 'string' + description: 'Require TLS to connect against this endpoint (example: true)' + - name: 'TLSSkipVerify' + in: 'formData' + type: 'string' + description: 'Skip server verification when using TLS (example: false)' + - name: 'TLSSkipClientVerify' + in: 'formData' + type: 'string' + description: 'Skip client verification when using TLS (example: false)' + - name: 'TLSCACertFile' + in: 'formData' + type: 'file' + description: 'TLS CA certificate file' + - name: 'TLSCertFile' + in: 'formData' + type: 'file' + description: 'TLS client certificate file' + - name: 'TLSKeyFile' + in: 'formData' + type: 'file' + description: 'TLS client key file' + - name: 'AzureApplicationID' + in: 'formData' + type: 'string' + description: 'Azure application ID. Required if endpoint type is set to 3' + - name: 'AzureTenantID' + in: 'formData' + type: 'string' + description: 'Azure tenant ID. Required if endpoint type is set to 3' + - name: 'AzureAuthenticationKey' + in: 'formData' + type: 'string' + description: 'Azure authentication key. Required if endpoint type is set to 3' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Endpoint" + $ref: '#/definitions/Endpoint' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 503: - description: "Endpoint management disabled" + description: 'Endpoint management disabled' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint management is disabled" + err: 'Endpoint management is disabled' /endpoints/{id}: get: tags: - - "endpoints" - summary: "Inspect an endpoint" + - 'endpoints' + summary: 'Inspect an endpoint' description: | Retrieve details abount an endpoint. **Access policy**: restricted - operationId: "EndpointInspect" + operationId: 'EndpointInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Endpoint identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Endpoint identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Endpoint" + $ref: '#/definitions/Endpoint' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "Endpoint not found" + description: 'Endpoint not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint not found" + err: 'Endpoint not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "endpoints" - summary: "Update an endpoint" + - 'endpoints' + summary: 'Update an endpoint' description: | Update an endpoint. **Access policy**: administrator - operationId: "EndpointUpdate" + operationId: 'EndpointUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Endpoint identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "Endpoint details" - required: true - schema: - $ref: "#/definitions/EndpointUpdateRequest" + - name: 'id' + in: 'path' + description: 'Endpoint identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Endpoint details' + required: true + schema: + $ref: '#/definitions/EndpointUpdateRequest' responses: 200: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 404: - description: "Endpoint not found" + description: 'Endpoint not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint not found" + err: 'Endpoint not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 503: - description: "Endpoint management disabled" + description: 'Endpoint management disabled' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint management is disabled" + err: 'Endpoint management is disabled' delete: tags: - - "endpoints" - summary: "Remove an endpoint" + - 'endpoints' + summary: 'Remove an endpoint' description: | Remove an endpoint. **Access policy**: administrator - operationId: "EndpointDelete" + operationId: 'EndpointDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Endpoint identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Endpoint identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "Endpoint not found" + description: 'Endpoint not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint not found" + err: 'Endpoint not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 503: - description: "Endpoint management disabled" + description: 'Endpoint management disabled' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint management is disabled" + err: 'Endpoint management is disabled' /endpoints/{id}/job: post: tags: - - "endpoints" - summary: "Execute a job on the endpoint host" + - 'endpoints' + summary: 'Execute a job on the endpoint host' description: | Execute a job (script) on the underlying host of the endpoint. **Access policy**: administrator - operationId: "EndpointJob" + operationId: 'EndpointJob' consumes: - - "multipart/form-data" + - 'multipart/form-data' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Endpoint identifier" - required: true - type: "integer" - - name: "method" - in: "query" - description: "Job execution method. Possible values: file or string." - required: true - type: "string" - - name: "nodeName" - in: "query" - description: "Optional. Hostname of a node when targeting a Portainer agent cluster." - required: true - type: "string" - - in: "body" - name: "body" - description: "Job details. Required when method equals string." - required: true - schema: - $ref: "#/definitions/EndpointJobRequest" - - name: "Image" - in: "formData" - type: "string" - description: "Container image which will be used to execute the job. Required when method equals file." - - name: "file" - in: "formData" - type: "file" - description: "Job script file. Required when method equals file." + - name: 'id' + in: 'path' + description: 'Endpoint identifier' + required: true + type: 'integer' + - name: 'method' + in: 'query' + description: 'Job execution method. Possible values: file or string.' + required: true + type: 'string' + - name: 'nodeName' + in: 'query' + description: 'Optional. Hostname of a node when targeting a Portainer agent cluster.' + required: true + type: 'string' + - in: 'body' + name: 'body' + description: 'Job details. Required when method equals string.' + required: true + schema: + $ref: '#/definitions/EndpointJobRequest' + - name: 'Image' + in: 'formData' + type: 'string' + description: 'Container image which will be used to execute the job. Required when method equals file.' + - name: 'file' + in: 'formData' + type: 'file' + description: 'Job script file. Required when method equals file.' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Endpoint" + $ref: '#/definitions/Endpoint' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 404: - description: "Endpoint not found" + description: 'Endpoint not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint not found" + err: 'Endpoint not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /endpoint_groups: get: tags: - - "endpoint_groups" - summary: "List endpoint groups" + - 'endpoint_groups' + summary: 'List endpoint groups' description: | List all endpoint groups based on the current user authorizations. Will return all endpoint groups if using an administrator account otherwise it will only return authorized endpoint groups. **Access policy**: restricted - operationId: "EndpointGroupList" + operationId: 'EndpointGroupList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/EndpointGroupListResponse" + $ref: '#/definitions/EndpointGroupListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "endpoint_groups" - summary: "Create a new endpoint" + - 'endpoint_groups' + summary: 'Create a new endpoint' description: | Create a new endpoint group. **Access policy**: administrator - operationId: "EndpointGroupCreate" + operationId: 'EndpointGroupCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Registry details" - required: true - schema: - $ref: "#/definitions/EndpointGroupCreateRequest" + - in: 'body' + name: 'body' + description: 'Registry details' + required: true + schema: + $ref: '#/definitions/EndpointGroupCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/EndpointGroup" + $ref: '#/definitions/EndpointGroup' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /endpoint_groups/{id}: get: tags: - - "endpoint_groups" - summary: "Inspect an endpoint group" + - 'endpoint_groups' + summary: 'Inspect an endpoint group' description: | Retrieve details abount an endpoint group. **Access policy**: administrator - operationId: "EndpointGroupInspect" + operationId: 'EndpointGroupInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Endpoint group identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Endpoint group identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/EndpointGroup" + $ref: '#/definitions/EndpointGroup' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "EndpointGroup not found" + description: 'EndpointGroup not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "EndpointGroup not found" + err: 'EndpointGroup not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "endpoint_groups" - summary: "Update an endpoint group" + - 'endpoint_groups' + summary: 'Update an endpoint group' description: | Update an endpoint group. **Access policy**: administrator - operationId: "EndpointGroupUpdate" + operationId: 'EndpointGroupUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "EndpointGroup identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "EndpointGroup details" - required: true - schema: - $ref: "#/definitions/EndpointGroupUpdateRequest" + - name: 'id' + in: 'path' + description: 'EndpointGroup identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'EndpointGroup details' + required: true + schema: + $ref: '#/definitions/EndpointGroupUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/EndpointGroup" + $ref: '#/definitions/EndpointGroup' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 404: - description: "EndpointGroup not found" + description: 'EndpointGroup not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "EndpointGroup not found" + err: 'EndpointGroup not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 503: - description: "EndpointGroup management disabled" + description: 'EndpointGroup management disabled' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "EndpointGroup management is disabled" + err: 'EndpointGroup management is disabled' delete: tags: - - "endpoint_groups" - summary: "Remove an endpoint group" + - 'endpoint_groups' + summary: 'Remove an endpoint group' description: | Remove an endpoint group. **Access policy**: administrator - operationId: "EndpointGroupDelete" + operationId: 'EndpointGroupDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "EndpointGroup identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'EndpointGroup identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "EndpointGroup not found" + description: 'EndpointGroup not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "EndpointGroup not found" + err: 'EndpointGroup not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 503: - description: "EndpointGroup management disabled" + description: 'EndpointGroup management disabled' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "EndpointGroup management is disabled" + err: 'EndpointGroup management is disabled' /endpoint_groups/{id}/endpoints/{endpointId}: put: tags: - - "endpoint_groups" - summary: "Add an endpoint to an endpoint group" + - 'endpoint_groups' + summary: 'Add an endpoint to an endpoint group' description: | Add an endpoint to an endpoint group **Access policy**: administrator - operationId: "EndpointGroupAddEndpoint" + operationId: 'EndpointGroupAddEndpoint' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "EndpointGroup identifier" - required: true - type: "integer" - - name: "endpointId" - in: "path" - description: "Endpoint identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'EndpointGroup identifier' + required: true + type: 'integer' + - name: 'endpointId' + in: 'path' + description: 'Endpoint identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 404: - description: "EndpointGroup not found" + description: 'EndpointGroup not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "EndpointGroup not found" + err: 'EndpointGroup not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "endpoint_groups" - summary: "Remove an endpoint group" + - 'endpoint_groups' + summary: 'Remove an endpoint group' description: | Remove an endpoint group. **Access policy**: administrator - operationId: "EndpointGroupDeleteEndpoint" + operationId: 'EndpointGroupDeleteEndpoint' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "EndpointGroup identifier" - required: true - type: "integer" - - name: "endpointId" - in: "path" - description: "Endpoint identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'EndpointGroup identifier' + required: true + type: 'integer' + - name: 'endpointId' + in: 'path' + description: 'Endpoint identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "EndpointGroup not found" + description: 'EndpointGroup not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "EndpointGroup not found" + err: 'EndpointGroup not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /extensions: get: tags: - - "extensions" - summary: "List extensions" + - 'extensions' + summary: 'List extensions' description: | List all extensions registered inside Portainer. If the store parameter is set to true, will retrieve extensions details from the online repository. **Access policy**: administrator - operationId: "ExtensionList" + operationId: 'ExtensionList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "store" - in: "query" - description: "Retrieve online information about extensions. Possible values: true or false." - required: false - type: "boolean" + - name: 'store' + in: 'query' + description: 'Retrieve online information about extensions. Possible values: true or false.' + required: false + type: 'boolean' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/ExtensionListResponse" + $ref: '#/definitions/ExtensionListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "extensions" - summary: "Enable an extension" + - 'extensions' + summary: 'Enable an extension' description: | Enable an extension. **Access policy**: administrator - operationId: "ExtensionCreate" + operationId: 'ExtensionCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Extension details" - required: true - schema: - $ref: "#/definitions/ExtensionCreateRequest" + - in: 'body' + name: 'body' + description: 'Extension details' + required: true + schema: + $ref: '#/definitions/ExtensionCreateRequest' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /extensions/{id}: get: tags: - - "extensions" - summary: "Inspect an extension" + - 'extensions' + summary: 'Inspect an extension' description: | Retrieve details abount an extension. **Access policy**: administrator - operationId: "ExtensionInspect" + operationId: 'ExtensionInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "extension identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'extension identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Extension" + $ref: '#/definitions/Extension' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "Extension not found" + description: 'Extension not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Extension not found" + err: 'Extension not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "extensions" - summary: "Update an extension" + - 'extensions' + summary: 'Update an extension' description: | Update an extension to a specific version of the extension. **Access policy**: administrator - operationId: "ExtensionUpdate" + operationId: 'ExtensionUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Extension identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "Extension details" - required: true - schema: - $ref: "#/definitions/ExtensionUpdateRequest" + - name: 'id' + in: 'path' + description: 'Extension identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Extension details' + required: true + schema: + $ref: '#/definitions/ExtensionUpdateRequest' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 404: - description: "Extension not found" + description: 'Extension not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Extension not found" + err: 'Extension not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "extensions" - summary: "Disable an extension" + - 'extensions' + summary: 'Disable an extension' description: | Disable an extension. **Access policy**: administrator - operationId: "ExtensionDelete" + operationId: 'ExtensionDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Extension identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Extension identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "Extension not found" + description: 'Extension not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Extension not found" + err: 'Extension not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /registries: get: tags: - - "registries" - summary: "List registries" + - 'registries' + summary: 'List registries' description: | List all registries based on the current user authorizations. Will return all registries if using an administrator account otherwise it will only return authorized registries. **Access policy**: restricted - operationId: "RegistryList" + operationId: 'RegistryList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/RegistryListResponse" + $ref: '#/definitions/RegistryListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "registries" - summary: "Create a new registry" + - 'registries' + summary: 'Create a new registry' description: | Create a new registry. **Access policy**: administrator - operationId: "RegistryCreate" + operationId: 'RegistryCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Registry details" - required: true - schema: - $ref: "#/definitions/RegistryCreateRequest" + - in: 'body' + name: 'body' + description: 'Registry details' + required: true + schema: + $ref: '#/definitions/RegistryCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Registry" + $ref: '#/definitions/Registry' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 409: - description: "Registry already exists" + description: 'Registry already exists' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "A registry is already defined for this URL" + err: 'A registry is already defined for this URL' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /registries/{id}: get: tags: - - "registries" - summary: "Inspect a registry" + - 'registries' + summary: 'Inspect a registry' description: | Retrieve details about a registry. **Access policy**: administrator - operationId: "RegistryInspect" + operationId: 'RegistryInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Registry identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Registry identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Registry" + $ref: '#/definitions/Registry' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "Registry not found" + description: 'Registry not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Registry not found" + err: 'Registry not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "registries" - summary: "Update a registry" + - 'registries' + summary: 'Update a registry' description: | Update a registry. **Access policy**: administrator - operationId: "RegistryUpdate" + operationId: 'RegistryUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Registry identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "Registry details" - required: true - schema: - $ref: "#/definitions/RegistryUpdateRequest" + - name: 'id' + in: 'path' + description: 'Registry identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Registry details' + required: true + schema: + $ref: '#/definitions/RegistryUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Registry" + $ref: '#/definitions/Registry' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 404: - description: "Registry not found" + description: 'Registry not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint not found" + err: 'Endpoint not found' 409: - description: "Registry already exists" + description: 'Registry already exists' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "A registry is already defined for this URL" + err: 'A registry is already defined for this URL' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "registries" - summary: "Remove a registry" + - 'registries' + summary: 'Remove a registry' description: | Remove a registry. **Access policy**: administrator - operationId: "RegistryDelete" + operationId: 'RegistryDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Registry identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Registry identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "Registry not found" + description: 'Registry not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Registry not found" + err: 'Registry not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /resource_controls: post: tags: - - "resource_controls" - summary: "Create a new resource control" + - 'resource_controls' + summary: 'Create a new resource control' description: | Create a new resource control to restrict access to a Docker resource. **Access policy**: administrator - operationId: "ResourceControlCreate" + operationId: 'ResourceControlCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Resource control details" - required: true - schema: - $ref: "#/definitions/ResourceControlCreateRequest" + - in: 'body' + name: 'body' + description: 'Resource control details' + required: true + schema: + $ref: '#/definitions/ResourceControlCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/ResourceControl" + $ref: '#/definitions/ResourceControl' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 409: - description: "Resource control already exists" + description: 'Resource control already exists' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "A resource control is already applied on this resource" + err: 'A resource control is already applied on this resource' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /resource_controls/{id}: put: tags: - - "resource_controls" - summary: "Update a resource control" + - 'resource_controls' + summary: 'Update a resource control' description: | Update a resource control. **Access policy**: restricted - operationId: "ResourceControlUpdate" + operationId: 'ResourceControlUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Resource control identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "Resource control details" - required: true - schema: - $ref: "#/definitions/ResourceControlUpdateRequest" + - name: 'id' + in: 'path' + description: 'Resource control identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Resource control details' + required: true + schema: + $ref: '#/definitions/ResourceControlUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/ResourceControl" + $ref: '#/definitions/ResourceControl' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "Resource control not found" + description: 'Resource control not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Resource control not found" + err: 'Resource control not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "resource_controls" - summary: "Remove a resource control" + - 'resource_controls' + summary: 'Remove a resource control' description: | Remove a resource control. **Access policy**: administrator - operationId: "ResourceControlDelete" + operationId: 'ResourceControlDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Resource control identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Resource control identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "Resource control not found" + description: 'Resource control not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Resource control not found" + err: 'Resource control not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /roles: get: tags: - - "roles" - summary: "List roles" + - 'roles' + summary: 'List roles' description: | List all roles available for use with the RBAC extension. **Access policy**: administrator - operationId: "RoleList" + operationId: 'RoleList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/RoleListResponse" + $ref: '#/definitions/RoleListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /settings: get: tags: - - "settings" - summary: "Retrieve Portainer settings" + - 'settings' + summary: 'Retrieve Portainer settings' description: | Retrieve Portainer settings. **Access policy**: administrator - operationId: "SettingsInspect" + operationId: 'SettingsInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Settings" + $ref: '#/definitions/Settings' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "settings" - summary: "Update Portainer settings" + - 'settings' + summary: 'Update Portainer settings' description: | Update Portainer settings. **Access policy**: administrator - operationId: "SettingsUpdate" + operationId: 'SettingsUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "New settings" - required: true - schema: - $ref: "#/definitions/SettingsUpdateRequest" + - in: 'body' + name: 'body' + description: 'New settings' + required: true + schema: + $ref: '#/definitions/SettingsUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Settings" + $ref: '#/definitions/Settings' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /settings/public: get: tags: - - "settings" - summary: "Retrieve Portainer public settings" + - 'settings' + summary: 'Retrieve Portainer public settings' description: | Retrieve public settings. Returns a small set of settings that are not reserved to administrators only. **Access policy**: public - operationId: "PublicSettingsInspect" + operationId: 'PublicSettingsInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/PublicSettingsInspectResponse" + $ref: '#/definitions/PublicSettingsInspectResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /settings/authentication/checkLDAP: put: tags: - - "settings" - summary: "Test LDAP connectivity" + - 'settings' + summary: 'Test LDAP connectivity' description: | Test LDAP connectivity using LDAP details. **Access policy**: administrator - operationId: "SettingsLDAPCheck" + operationId: 'SettingsLDAPCheck' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "LDAP settings" - required: true - schema: - $ref: "#/definitions/SettingsLDAPCheckRequest" + - in: 'body' + name: 'body' + description: 'LDAP settings' + required: true + schema: + $ref: '#/definitions/SettingsLDAPCheckRequest' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /status: get: tags: - - "status" - summary: "Check Portainer status" + - 'status' + summary: 'Check Portainer status' description: | Retrieve Portainer status. **Access policy**: public - operationId: "StatusInspect" + operationId: 'StatusInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Status" + $ref: '#/definitions/Status' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /stacks: get: tags: - - "stacks" - summary: "List stacks" + - 'stacks' + summary: 'List stacks' description: | List all stacks based on the current user authorizations. Will return all stacks if using an administrator account otherwise it will only return the list of stacks the user have access to. **Access policy**: restricted - operationId: "StackList" + operationId: 'StackList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "filters" - in: "query" - description: | - Filters to process on the stack list. Encoded as JSON (a map[string]string). - For example, {"SwarmID": "jpofkc0i9uo9wtx1zesuk649w"} will only return stacks that are part - of the specified Swarm cluster. Available filters: EndpointID, SwarmID. - type: "string" + - name: 'filters' + in: 'query' + description: | + Filters to process on the stack list. Encoded as JSON (a map[string]string). + For example, {"SwarmID": "jpofkc0i9uo9wtx1zesuk649w"} will only return stacks that are part + of the specified Swarm cluster. Available filters: EndpointID, SwarmID. + type: 'string' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/StackListResponse" + $ref: '#/definitions/StackListResponse' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "stacks" - summary: "Deploy a new stack" + - 'stacks' + summary: 'Deploy a new stack' description: | Deploy a new stack into a Docker environment specified via the endpoint identifier. **Access policy**: restricted - operationId: "StackCreate" + operationId: 'StackCreate' consumes: - - "multipart/form-data" + - 'multipart/form-data' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "type" - in: "query" - description: "Stack deployment type. Possible values: 1 (Swarm stack) or 2 (Compose stack)." - required: true - type: "integer" - - name: "method" - in: "query" - description: "Stack deployment method. Possible values: file, string or repository." - required: true - type: "string" - - name: "endpointId" - in: "query" - description: "Identifier of the endpoint that will be used to deploy the stack." - required: true - type: "integer" - - in: "body" - name: "body" - description: "Stack details. Required when method equals string or repository." - schema: - $ref: "#/definitions/StackCreateRequest" - - name: "Name" - in: "formData" - type: "string" - description: "Name of the stack. Required when method equals file." - - name: "EndpointID" - in: "formData" - type: "string" - description: "Endpoint identifier used to deploy the stack. Required when method equals file." - - name: "SwarmID" - in: "formData" - type: "string" - description: "Swarm cluster identifier. Required when method equals file and type equals 1." - - name: "file" - in: "formData" - type: "file" - description: "Stack file. Required when method equals file." - - name: "Env" - in: "formData" - type: "string" - description: "Environment variables passed during deployment, represented as a JSON array [{'name': 'name', 'value': 'value'}]. Optional, used when method equals file and type equals 1." + - name: 'type' + in: 'query' + description: 'Stack deployment type. Possible values: 1 (Swarm stack) or 2 (Compose stack).' + required: true + type: 'integer' + - name: 'method' + in: 'query' + description: 'Stack deployment method. Possible values: file, string or repository.' + required: true + type: 'string' + - name: 'endpointId' + in: 'query' + description: 'Identifier of the endpoint that will be used to deploy the stack.' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Stack details. Required when method equals string or repository.' + schema: + $ref: '#/definitions/StackCreateRequest' + - name: 'Name' + in: 'formData' + type: 'string' + description: 'Name of the stack. Required when method equals file.' + - name: 'EndpointID' + in: 'formData' + type: 'string' + description: 'Endpoint identifier used to deploy the stack. Required when method equals file.' + - name: 'SwarmID' + in: 'formData' + type: 'string' + description: 'Swarm cluster identifier. Required when method equals file and type equals 1.' + - name: 'file' + in: 'formData' + type: 'file' + description: 'Stack file. Required when method equals file.' + - name: 'Env' + in: 'formData' + type: 'string' + description: "Environment variables passed during deployment, represented as a JSON array [{'name': 'name', 'value': 'value'}]. Optional, used when method equals file and type equals 1." responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Stack" + $ref: '#/definitions/Stack' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 404: - description: "Endpoint not found" + description: 'Endpoint not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Endpoint not found" + err: 'Endpoint not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /stacks/{id}: get: tags: - - "stacks" - summary: "Inspect a stack" + - 'stacks' + summary: 'Inspect a stack' description: | Retrieve details about a stack. **Access policy**: restricted - operationId: "StackInspect" + operationId: 'StackInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Stack identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Stack identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Stack" + $ref: '#/definitions/Stack' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 404: - description: "Stack not found" + description: 'Stack not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Stack not found" + err: 'Stack not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "stacks" - summary: "Update a stack" + - 'stacks' + summary: 'Update a stack' description: | Update a stack. **Access policy**: restricted - operationId: "StackUpdate" + operationId: 'StackUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Stack identifier" - required: true - type: "integer" - - name: "endpointId" - in: "query" - description: "Stacks created before version 1.18.0 might not have an associated endpoint identifier. Use this \ - optional parameter to set the endpoint identifier used by the stack." - type: "integer" - - in: "body" - name: "body" - description: "Stack details" - required: true - schema: - $ref: "#/definitions/StackUpdateRequest" + - name: 'id' + in: 'path' + description: 'Stack identifier' + required: true + type: 'integer' + - name: 'endpointId' + in: 'query' + description: "Stacks created before version 1.18.0 might not have an associated endpoint identifier. Use this \ + optional parameter to set the endpoint identifier used by the stack." + type: 'integer' + - in: 'body' + name: 'body' + description: 'Stack details' + required: true + schema: + $ref: '#/definitions/StackUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Stack" + $ref: '#/definitions/Stack' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 404: - description: "Stack not found" + description: 'Stack not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Stack not found" + err: 'Stack not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "stacks" - summary: "Remove a stack" + - 'stacks' + summary: 'Remove a stack' description: | Remove a stack. **Access policy**: restricted - operationId: "StackDelete" + operationId: 'StackDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Stack identifier" - required: true - type: "integer" - - name: "external" - in: "query" - description: "Set to true to delete an external stack. Only external Swarm stacks are supported." - type: "boolean" - - name: "endpointId" - in: "query" - description: "Endpoint identifier used to remove an external stack (required when external is set to true)" - type: "string" + - name: 'id' + in: 'path' + description: 'Stack identifier' + required: true + type: 'integer' + - name: 'external' + in: 'query' + description: 'Set to true to delete an external stack. Only external Swarm stacks are supported.' + type: 'boolean' + - name: 'endpointId' + in: 'query' + description: 'Endpoint identifier used to remove an external stack (required when external is set to true)' + type: 'string' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 404: - description: "Stack not found" + description: 'Stack not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Stack not found" + err: 'Stack not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /stacks/{id}/file: get: tags: - - "stacks" - summary: "Retrieve the content of the Stack file for the specified stack" + - 'stacks' + summary: 'Retrieve the content of the Stack file for the specified stack' description: | Get Stack file content. **Access policy**: restricted - operationId: "StackFileInspect" + operationId: 'StackFileInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Stack identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Stack identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/StackFileInspectResponse" + $ref: '#/definitions/StackFileInspectResponse' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 404: - description: "Stack not found" + description: 'Stack not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Stack not found" + err: 'Stack not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /stacks/{id}/migrate: post: tags: - - "stacks" - summary: "Migrate a stack to another endpoint" + - 'stacks' + summary: 'Migrate a stack to another endpoint' description: | Migrate a stack from an endpoint to another endpoint. It will re-create the stack inside the target endpoint before removing the original stack. **Access policy**: restricted - operationId: "StackMigrate" + operationId: 'StackMigrate' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Stack identifier" - required: true - type: "integer" - - name: "endpointId" - in: "query" - description: "Stacks created before version 1.18.0 might not have an associated endpoint identifier. Use this \ - optional parameter to set the endpoint identifier used by the stack." - type: "integer" - - in: "body" - name: "body" - description: "Stack migration details." - schema: - $ref: "#/definitions/StackMigrateRequest" + - name: 'id' + in: 'path' + description: 'Stack identifier' + required: true + type: 'integer' + - name: 'endpointId' + in: 'query' + description: "Stacks created before version 1.18.0 might not have an associated endpoint identifier. Use this \ + optional parameter to set the endpoint identifier used by the stack." + type: 'integer' + - in: 'body' + name: 'body' + description: 'Stack migration details.' + schema: + $ref: '#/definitions/StackMigrateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Stack" + $ref: '#/definitions/Stack' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' 404: - description: "Stack not found" + description: 'Stack not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Stack not found" + err: 'Stack not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /users: get: tags: - - "users" - summary: "List users" + - 'users' + summary: 'List users' description: | List Portainer users. Non-administrator users will only be able to list other non-administrator user accounts. **Access policy**: restricted - operationId: "UserList" + operationId: 'UserList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/UserListResponse" + $ref: '#/definitions/UserListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "users" - summary: "Create a new user" + - 'users' + summary: 'Create a new user' description: | Create a new Portainer user. Only team leaders and administrators can create users. Only administrators can create an administrator user account. **Access policy**: restricted - operationId: "UserCreate" + operationId: 'UserCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "User details" - required: true - schema: - $ref: "#/definitions/UserCreateRequest" + - in: 'body' + name: 'body' + description: 'User details' + required: true + schema: + $ref: '#/definitions/UserCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/UserSubset" + $ref: '#/definitions/UserSubset' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 409: - description: "User already exists" + description: 'User already exists' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "User already exists" + err: 'User already exists' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /users/{id}: get: tags: - - "users" - summary: "Inspect a user" + - 'users' + summary: 'Inspect a user' description: | Retrieve details about a user. **Access policy**: administrator - operationId: "UserInspect" + operationId: 'UserInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "User identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'User identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/User" + $ref: '#/definitions/User' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "User not found" + description: 'User not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "User not found" + err: 'User not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "users" - summary: "Update a user" + - 'users' + summary: 'Update a user' description: | Update user details. A regular user account can only update his details. **Access policy**: authenticated - operationId: "UserUpdate" + operationId: 'UserUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "User identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "User details" - required: true - schema: - $ref: "#/definitions/UserUpdateRequest" + - name: 'id' + in: 'path' + description: 'User identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'User details' + required: true + schema: + $ref: '#/definitions/UserUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/User" + $ref: '#/definitions/User' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "User not found" + description: 'User not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "User not found" + err: 'User not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "users" - summary: "Remove a user" + - 'users' + summary: 'Remove a user' description: | Remove a user. **Access policy**: administrator - operationId: "UserDelete" + operationId: 'UserDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "User identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'User identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "User not found" + description: 'User not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "User not found" + err: 'User not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /users/{id}/memberships: get: tags: - - "users" - summary: "Inspect a user memberships" + - 'users' + summary: 'Inspect a user memberships' description: | Inspect a user memberships. **Access policy**: authenticated - operationId: "UserMembershipsInspect" + operationId: 'UserMembershipsInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "User identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'User identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/UserMembershipsResponse" + $ref: '#/definitions/UserMembershipsResponse' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /users/{id}/passwd: post: tags: - - "users" - summary: "Check password validity for a user" + - 'users' + summary: 'Check password validity for a user' description: | Check if the submitted password is valid for the specified user. **Access policy**: authenticated - operationId: "UserPasswordCheck" + operationId: 'UserPasswordCheck' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "User identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "User details" - required: true - schema: - $ref: "#/definitions/UserPasswordCheckRequest" + - name: 'id' + in: 'path' + description: 'User identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'User details' + required: true + schema: + $ref: '#/definitions/UserPasswordCheckRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/UserPasswordCheckResponse" + $ref: '#/definitions/UserPasswordCheckResponse' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 404: - description: "User not found" + description: 'User not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "User not found" + err: 'User not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /users/admin/check: get: tags: - - "users" - summary: "Check administrator account existence" + - 'users' + summary: 'Check administrator account existence' description: | Check if an administrator account exists in the database. **Access policy**: public - operationId: "UserAdminCheck" + operationId: 'UserAdminCheck' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 204: - description: "Success" + description: 'Success' 404: - description: "User not found" + description: 'User not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "User not found" + err: 'User not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /users/admin/init: post: tags: - - "users" - summary: "Initialize administrator account" + - 'users' + summary: 'Initialize administrator account' description: | Initialize the 'admin' user account. **Access policy**: public - operationId: "UserAdminInit" + operationId: 'UserAdminInit' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "User details" - required: true - schema: - $ref: "#/definitions/UserAdminInitRequest" + - in: 'body' + name: 'body' + description: 'User details' + required: true + schema: + $ref: '#/definitions/UserAdminInitRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/User" + $ref: '#/definitions/User' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 409: - description: "Admin user already initialized" + description: 'Admin user already initialized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "User already exists" + err: 'User already exists' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /upload/tls/{certificate}: post: tags: - - "upload" - summary: "Upload TLS files" + - 'upload' + summary: 'Upload TLS files' description: | Use this endpoint to upload TLS files. **Access policy**: administrator - operationId: "UploadTLS" + operationId: 'UploadTLS' consumes: - - multipart/form-data + - multipart/form-data produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "path" - name: "certificate" - description: "TLS file type. Valid values are 'ca', 'cert' or 'key'." - required: true - type: "string" - - in: "query" - name: "folder" - description: "Folder where the TLS file will be stored. Will be created if not existing." - required: true - type: "string" - - in: "formData" - name: "file" - type: "file" - description: "The file to upload." + - in: 'path' + name: 'certificate' + description: "TLS file type. Valid values are 'ca', 'cert' or 'key'." + required: true + type: 'string' + - in: 'query' + name: 'folder' + description: 'Folder where the TLS file will be stored. Will be created if not existing.' + required: true + type: 'string' + - in: 'formData' + name: 'file' + type: 'file' + description: 'The file to upload.' responses: 200: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data" + err: 'Invalid request data' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /tags: get: tags: - - "tags" - summary: "List tags" + - 'tags' + summary: 'List tags' description: | List tags. **Access policy**: administrator - operationId: "TagList" + operationId: 'TagList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/TagListResponse" + $ref: '#/definitions/TagListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "tags" - summary: "Create a new tag" + - 'tags' + summary: 'Create a new tag' description: | Create a new tag. **Access policy**: administrator - operationId: "TagCreate" + operationId: 'TagCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Tag details" - required: true - schema: - $ref: "#/definitions/TagCreateRequest" + - in: 'body' + name: 'body' + description: 'Tag details' + required: true + schema: + $ref: '#/definitions/TagCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Tag" + $ref: '#/definitions/Tag' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 409: - description: "Conflict" + description: 'Conflict' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "A tag with the specified name already exists" + err: 'A tag with the specified name already exists' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /tags/{id}: delete: tags: - - "tags" - summary: "Remove a tag" + - 'tags' + summary: 'Remove a tag' description: | Remove a tag. **Access policy**: administrator - operationId: "TagDelete" + operationId: 'TagDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Tag identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Tag identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /teams: get: tags: - - "teams" - summary: "List teams" + - 'teams' + summary: 'List teams' description: | List teams. For non-administrator users, will only list the teams they are member of. **Access policy**: restricted - operationId: "TeamList" + operationId: 'TeamList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/TeamListResponse" + $ref: '#/definitions/TeamListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "teams" - summary: "Create a new team" + - 'teams' + summary: 'Create a new team' description: | Create a new team. **Access policy**: administrator - operationId: "TeamCreate" + operationId: 'TeamCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Team details" - required: true - schema: - $ref: "#/definitions/TeamCreateRequest" + - in: 'body' + name: 'body' + description: 'Team details' + required: true + schema: + $ref: '#/definitions/TeamCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Team" + $ref: '#/definitions/Team' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 409: - description: "Team already exists" + description: 'Team already exists' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Team already exists" + err: 'Team already exists' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /teams/{id}: get: tags: - - "teams" - summary: "Inspect a team" + - 'teams' + summary: 'Inspect a team' description: | Retrieve details about a team. Access is only available for administrator and leaders of that team. **Access policy**: restricted - operationId: "TeamInspect" + operationId: 'TeamInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Team identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Team identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Team" + $ref: '#/definitions/Team' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "Team not found" + description: 'Team not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Team not found" + err: 'Team not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "teams" - summary: "Update a team" + - 'teams' + summary: 'Update a team' description: | Update a team. **Access policy**: administrator - operationId: "TeamUpdate" + operationId: 'TeamUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Team identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "Team details" - required: true - schema: - $ref: "#/definitions/TeamUpdateRequest" + - name: 'id' + in: 'path' + description: 'Team identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Team details' + required: true + schema: + $ref: '#/definitions/TeamUpdateRequest' responses: 200: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 404: - description: "Team not found" + description: 'Team not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Team not found" + err: 'Team not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "teams" - summary: "Remove a team" + - 'teams' + summary: 'Remove a team' description: | Remove a team. **Access policy**: administrator - operationId: "TeamDelete" + operationId: 'TeamDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Team identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Team identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 404: - description: "Team not found" + description: 'Team not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Team not found" + err: 'Team not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /teams/{id}/memberships: get: tags: - - "teams" - summary: "Inspect a team memberships" + - 'teams' + summary: 'Inspect a team memberships' description: | Inspect a team memberships. Access is only available for administrator and leaders of that team. **Access policy**: restricted - operationId: "TeamMembershipsInspect" + operationId: 'TeamMembershipsInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Team identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Team identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/TeamMembershipsResponse" + $ref: '#/definitions/TeamMembershipsResponse' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /team_memberships: get: tags: - - "team_memberships" - summary: "List team memberships" + - 'team_memberships' + summary: 'List team memberships' description: | List team memberships. Access is only available to administrators and team leaders. **Access policy**: restricted - operationId: "TeamMembershipList" + operationId: 'TeamMembershipList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/TeamMembershipListResponse" + $ref: '#/definitions/TeamMembershipListResponse' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "team_memberships" - summary: "Create a new team membership" + - 'team_memberships' + summary: 'Create a new team membership' description: | Create a new team memberships. Access is only available to administrators leaders of the associated team. **Access policy**: restricted - operationId: "TeamMembershipCreate" + operationId: 'TeamMembershipCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Team membership details" - required: true - schema: - $ref: "#/definitions/TeamMembershipCreateRequest" + - in: 'body' + name: 'body' + description: 'Team membership details' + required: true + schema: + $ref: '#/definitions/TeamMembershipCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/TeamMembership" + $ref: '#/definitions/TeamMembership' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 409: - description: "Team membership already exists" + description: 'Team membership already exists' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Team membership already exists for this user and team." + err: 'Team membership already exists for this user and team.' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /team_memberships/{id}: put: tags: - - "team_memberships" - summary: "Update a team membership" + - 'team_memberships' + summary: 'Update a team membership' description: | Update a team membership. Access is only available to administrators leaders of the associated team. **Access policy**: restricted - operationId: "TeamMembershipUpdate" + operationId: 'TeamMembershipUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Team membership identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "Team membership details" - required: true - schema: - $ref: "#/definitions/TeamMembershipUpdateRequest" + - name: 'id' + in: 'path' + description: 'Team membership identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Team membership details' + required: true + schema: + $ref: '#/definitions/TeamMembershipUpdateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/TeamMembership" + $ref: '#/definitions/TeamMembership' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "Team membership not found" + description: 'Team membership not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Team membership not found" + err: 'Team membership not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "team_memberships" - summary: "Remove a team membership" + - 'team_memberships' + summary: 'Remove a team membership' description: | Remove a team membership. Access is only available to administrators leaders of the associated team. **Access policy**: restricted - operationId: "TeamMembershipDelete" + operationId: 'TeamMembershipDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "TeamMembership identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'TeamMembership identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "Team membership not found" + description: 'Team membership not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Team membership not found" + err: 'Team membership not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /templates: get: tags: - - "templates" - summary: "List available templates" + - 'templates' + summary: 'List available templates' description: | List available templates. Administrator templates will not be listed for non-administrator users. **Access policy**: restricted - operationId: "TemplateList" + operationId: 'TemplateList' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: [] responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/TemplateListResponse" + $ref: '#/definitions/TemplateListResponse' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' post: tags: - - "templates" - summary: "Create a new template" + - 'templates' + summary: 'Create a new template' description: | Create a new template. **Access policy**: administrator - operationId: "TemplateCreate" + operationId: 'TemplateCreate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - in: "body" - name: "body" - description: "Template details" - required: true - schema: - $ref: "#/definitions/TemplateCreateRequest" + - in: 'body' + name: 'body' + description: 'Template details' + required: true + schema: + $ref: '#/definitions/TemplateCreateRequest' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Template" + $ref: '#/definitions/Template' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' /templates/{id}: get: tags: - - "templates" - summary: "Inspect a template" + - 'templates' + summary: 'Inspect a template' description: | Retrieve details about a template. **Access policy**: administrator - operationId: "TemplateInspect" + operationId: 'TemplateInspect' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Template identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Template identifier' + required: true + type: 'integer' responses: 200: - description: "Success" + description: 'Success' schema: - $ref: "#/definitions/Template" + $ref: '#/definitions/Template' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "Template not found" + description: 'Template not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Template not found" + err: 'Template not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' put: tags: - - "templates" - summary: "Update a template" + - 'templates' + summary: 'Update a template' description: | Update a template. **Access policy**: administrator - operationId: "TemplateUpdate" + operationId: 'TemplateUpdate' consumes: - - "application/json" + - 'application/json' produces: - - "application/json" + - 'application/json' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Template identifier" - required: true - type: "integer" - - in: "body" - name: "body" - description: "Template details" - required: true - schema: - $ref: "#/definitions/TemplateUpdateRequest" + - name: 'id' + in: 'path' + description: 'Template identifier' + required: true + type: 'integer' + - in: 'body' + name: 'body' + description: 'Template details' + required: true + schema: + $ref: '#/definitions/TemplateUpdateRequest' responses: 200: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request data format" + err: 'Invalid request data format' 403: - description: "Unauthorized" + description: 'Unauthorized' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Access denied to resource" + err: 'Access denied to resource' 404: - description: "Template not found" + description: 'Template not found' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Template not found" + err: 'Template not found' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' delete: tags: - - "templates" - summary: "Remove a template" + - 'templates' + summary: 'Remove a template' description: | Remove a template. **Access policy**: administrator - operationId: "TemplateDelete" + operationId: 'TemplateDelete' security: - - jwt: [] + - jwt: [] parameters: - - name: "id" - in: "path" - description: "Template identifier" - required: true - type: "integer" + - name: 'id' + in: 'path' + description: 'Template identifier' + required: true + type: 'integer' responses: 204: - description: "Success" + description: 'Success' 400: - description: "Invalid request" + description: 'Invalid request' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' examples: application/json: - err: "Invalid request" + err: 'Invalid request' 500: - description: "Server error" + description: 'Server error' schema: - $ref: "#/definitions/GenericError" + $ref: '#/definitions/GenericError' securityDefinitions: jwt: - type: "apiKey" - name: "Authorization" - in: "header" + type: 'apiKey' + name: 'Authorization' + in: 'header' definitions: Tag: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Tag identifier" + description: 'Tag identifier' Name: - type: "string" - example: "org/acme" - description: "Tag name" + type: 'string' + example: 'org/acme' + description: 'Tag name' Team: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' Name: - type: "string" - example: "developers" - description: "Team name" + type: 'string' + example: 'developers' + description: 'Team name' TeamMembership: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Membership identifier" + description: 'Membership identifier' UserID: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' TeamID: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' Role: - type: "integer" + type: 'integer' example: 1 - description: "Team role (1 for team leader and 2 for team member)" + description: 'Team role (1 for team leader and 2 for team member)' UserSubset: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' Username: - type: "string" - example: "bob" - description: "Username" + type: 'string' + example: 'bob' + description: 'Username' Role: - type: "integer" + type: 'integer' example: 1 - description: "User role (1 for administrator account and 2 for regular account)" + description: 'User role (1 for administrator account and 2 for regular account)' User: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' Username: - type: "string" - example: "bob" - description: "Username" + type: 'string' + example: 'bob' + description: 'Username' Password: - type: "string" - example: "passwd" - description: "Password" + type: 'string' + example: 'passwd' + description: 'Password' Role: - type: "integer" + type: 'integer' example: 1 - description: "User role (1 for administrator account and 2 for regular account)" + description: 'User role (1 for administrator account and 2 for regular account)' Status: - type: "object" + type: 'object' properties: Authentication: - type: "boolean" - example: true - description: "Is authentication enabled" - Analytics: - type: "boolean" + type: 'boolean' example: true - description: "Is analytics enabled" + description: 'Is authentication enabled' Version: - type: "string" - example: "2.0.0-dev" - description: "Portainer API version" + type: 'string' + example: '2.0.0-dev' + description: 'Portainer API version' PublicSettingsInspectResponse: - type: "object" + type: 'object' properties: LogoURL: - type: "string" - example: "https://mycompany.mydomain.tld/logo.png" + type: 'string' + example: 'https://mycompany.mydomain.tld/logo.png' description: "URL to a logo that will be displayed on the login page as well\ \ as on top of the sidebar. Will use default Portainer logo when value is\ \ empty string" DisplayExternalContributors: - type: "boolean" + type: 'boolean' example: false description: "Whether to display or not external templates contributions as\ \ sub-menus in the UI." AuthenticationMethod: - type: "integer" + type: 'integer' example: 1 - description: "Active authentication method for the Portainer instance. Valid values are: 1 for managed or 2 for LDAP." + description: 'Active authentication method for the Portainer instance. Valid values are: 1 for managed or 2 for LDAP.' AllowBindMountsForRegularUsers: - type: "boolean" + type: 'boolean' example: false - description: "Whether non-administrator should be able to use bind mounts when creating containers" + description: 'Whether non-administrator should be able to use bind mounts when creating containers' AllowPrivilegedModeForRegularUsers: - type: "boolean" + type: 'boolean' example: true - description: "Whether non-administrator should be able to use privileged mode when creating containers" + description: 'Whether non-administrator should be able to use privileged mode when creating containers' TLSConfiguration: - type: "object" + type: 'object' properties: TLS: - type: "boolean" + type: 'boolean' example: true - description: "Use TLS" + description: 'Use TLS' TLSSkipVerify: - type: "boolean" + type: 'boolean' example: false - description: "Skip the verification of the server TLS certificate" + description: 'Skip the verification of the server TLS certificate' TLSCACertPath: - type: "string" - example: "/data/tls/ca.pem" - description: "Path to the TLS CA certificate file" + type: 'string' + example: '/data/tls/ca.pem' + description: 'Path to the TLS CA certificate file' TLSCertPath: - type: "string" - example: "/data/tls/cert.pem" - description: "Path to the TLS client certificate file" + type: 'string' + example: '/data/tls/cert.pem' + description: 'Path to the TLS client certificate file' TLSKeyPath: - type: "string" - example: "/data/tls/key.pem" - description: "Path to the TLS client key file" + type: 'string' + example: '/data/tls/key.pem' + description: 'Path to the TLS client key file' AzureCredentials: - type: "object" + type: 'object' properties: ApplicationID: - type: "string" - example: "eag7cdo9-o09l-9i83-9dO9-f0b23oe78db4" - description: "Azure application ID" + type: 'string' + example: 'eag7cdo9-o09l-9i83-9dO9-f0b23oe78db4' + description: 'Azure application ID' TenantID: - type: "string" - example: "34ddc78d-4fel-2358-8cc1-df84c8o839f5" - description: "Azure tenant ID" + type: 'string' + example: '34ddc78d-4fel-2358-8cc1-df84c8o839f5' + description: 'Azure tenant ID' AuthenticationKey: - type: "string" - example: "cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk=" - description: "Azure authentication key" + type: 'string' + example: 'cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk=' + description: 'Azure authentication key' LDAPSearchSettings: - type: "object" + type: 'object' properties: BaseDN: - type: "string" - example: "dc=ldap,dc=domain,dc=tld" - description: "The distinguished name of the element from which the LDAP server will search for users" + type: 'string' + example: 'dc=ldap,dc=domain,dc=tld' + description: 'The distinguished name of the element from which the LDAP server will search for users' Filter: - type: "string" - example: "(objectClass=account)" - description: "Optional LDAP search filter used to select user elements" + type: 'string' + example: '(objectClass=account)' + description: 'Optional LDAP search filter used to select user elements' UserNameAttribute: - type: "string" - example: "uid" - description: "LDAP attribute which denotes the username" + type: 'string' + example: 'uid' + description: 'LDAP attribute which denotes the username' LDAPGroupSearchSettings: - type: "object" + type: 'object' properties: GroupBaseDN: - type: "string" - example: "dc=ldap,dc=domain,dc=tld" - description: "The distinguished name of the element from which the LDAP server will search for groups." + type: 'string' + example: 'dc=ldap,dc=domain,dc=tld' + description: 'The distinguished name of the element from which the LDAP server will search for groups.' GroupFilter: - type: "string" - example: "(objectClass=account)" - description: "The LDAP search filter used to select group elements, optional." + type: 'string' + example: '(objectClass=account)' + description: 'The LDAP search filter used to select group elements, optional.' GroupAttribute: - type: "string" - example: "member" - description: "LDAP attribute which denotes the group membership." + type: 'string' + example: 'member' + description: 'LDAP attribute which denotes the group membership.' UserAccessPolicies: - type: "object" - description: "User access policies associated to a registry/endpoint/endpoint group. RoleID is not required for registry access policies and can be set to 0." + type: 'object' + description: 'User access policies associated to a registry/endpoint/endpoint group. RoleID is not required for registry access policies and can be set to 0.' additionalProperties: - $ref: "#/definitions/AccessPolicy" + $ref: '#/definitions/AccessPolicy' example: 1: { RoleID: 1 } 2: { RoleID: 3 } TeamAccessPolicies: - type: "object" - description: "Team access policies associated to a registry/endpoint/endpoint group. RoleID is not required for registry access policies and can be set to 0." + type: 'object' + description: 'Team access policies associated to a registry/endpoint/endpoint group. RoleID is not required for registry access policies and can be set to 0.' additionalProperties: - $ref: "#/definitions/AccessPolicy" + $ref: '#/definitions/AccessPolicy' example: 1: { RoleID: 1 } 2: { RoleID: 3 } AccessPolicy: - type: "object" + type: 'object' properties: RoleID: - type: "integer" - example: "1" - description: "Role identifier. Reference the role that will be associated to this access policy" + type: 'integer' + example: '1' + description: 'Role identifier. Reference the role that will be associated to this access policy' LDAPSettings: - type: "object" + type: 'object' properties: AnonymousMode: - type: "boolean" + type: 'boolean' example: true - description: "Enable this option if the server is configured for Anonymous access. When enabled, ReaderDN and Password will not be used." + description: 'Enable this option if the server is configured for Anonymous access. When enabled, ReaderDN and Password will not be used.' ReaderDN: - type: "string" - example: "cn=readonly-account,dc=ldap,dc=domain,dc=tld" - description: "Account that will be used to search for users" + type: 'string' + example: 'cn=readonly-account,dc=ldap,dc=domain,dc=tld' + description: 'Account that will be used to search for users' Password: - type: "string" - example: "readonly-password" - description: "Password of the account that will be used to search users" + type: 'string' + example: 'readonly-password' + description: 'Password of the account that will be used to search users' URL: - type: "string" - example: "myldap.domain.tld:389" - description: "URL or IP address of the LDAP server" + type: 'string' + example: 'myldap.domain.tld:389' + description: 'URL or IP address of the LDAP server' TLSConfig: - $ref: "#/definitions/TLSConfiguration" + $ref: '#/definitions/TLSConfiguration' StartTLS: - type: "boolean" + type: 'boolean' example: true - description: "Whether LDAP connection should use StartTLS" + description: 'Whether LDAP connection should use StartTLS' SearchSettings: - type: "array" + type: 'array' items: - $ref: "#/definitions/LDAPSearchSettings" + $ref: '#/definitions/LDAPSearchSettings' GroupSearchSettings: - type: "array" + type: 'array' items: - $ref: "#/definitions/LDAPGroupSearchSettings" + $ref: '#/definitions/LDAPGroupSearchSettings' AutoCreateUsers: - type: "boolean" + type: 'boolean' example: true - description: "Automatically provision users and assign them to matching LDAP group names" + description: 'Automatically provision users and assign them to matching LDAP group names' Settings: - type: "object" + type: 'object' properties: TemplatesURL: - type: "string" - example: "https://raw.githubusercontent.com/portainer/templates/master/templates.json" + type: 'string' + example: 'https://raw.githubusercontent.com/portainer/templates/master/templates.json' description: "URL to the templates that will be displayed in the UI when navigating\ \ to App Templates" LogoURL: - type: "string" - example: "https://mycompany.mydomain.tld/logo.png" + type: 'string' + example: 'https://mycompany.mydomain.tld/logo.png' description: "URL to a logo that will be displayed on the login page as well\ \ as on top of the sidebar. Will use default Portainer logo when value is\ \ empty string" BlackListedLabels: - type: "array" + type: 'array' description: "A list of label name & value that will be used to hide containers\ \ when querying containers" items: - $ref: "#/definitions/Settings_BlackListedLabels" + $ref: '#/definitions/Settings_BlackListedLabels' DisplayExternalContributors: - type: "boolean" + type: 'boolean' example: false description: "Whether to display or not external templates contributions as\ \ sub-menus in the UI." AuthenticationMethod: - type: "integer" + type: 'integer' example: 1 - description: "Active authentication method for the Portainer instance. Valid values are: 1 for managed or 2 for LDAP." + description: 'Active authentication method for the Portainer instance. Valid values are: 1 for managed or 2 for LDAP.' LDAPSettings: - $ref: "#/definitions/LDAPSettings" + $ref: '#/definitions/LDAPSettings' AllowBindMountsForRegularUsers: - type: "boolean" + type: 'boolean' example: false - description: "Whether non-administrator should be able to use bind mounts when creating containers" + description: 'Whether non-administrator should be able to use bind mounts when creating containers' AllowPrivilegedModeForRegularUsers: - type: "boolean" + type: 'boolean' example: true - description: "Whether non-administrator should be able to use privileged mode when creating containers" + description: 'Whether non-administrator should be able to use privileged mode when creating containers' Settings_BlackListedLabels: properties: name: - type: "string" - example: "com.foo" + type: 'string' + example: 'com.foo' value: - type: "string" - example: "bar" + type: 'string' + example: 'bar' Pair: properties: name: - type: "string" - example: "name" + type: 'string' + example: 'name' value: - type: "string" - example: "value" + type: 'string' + example: 'value' Registry: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Registry identifier" + description: 'Registry identifier' Name: - type: "string" - example: "my-registry" - description: "Registry name" + type: 'string' + example: 'my-registry' + description: 'Registry name' URL: - type: "string" - example: "registry.mydomain.tld:2375" - description: "URL or IP address of the Docker registry" + type: 'string' + example: 'registry.mydomain.tld:2375' + description: 'URL or IP address of the Docker registry' Authentication: - type: "boolean" + type: 'boolean' example: true - description: "Is authentication against this registry enabled" + description: 'Is authentication against this registry enabled' Username: - type: "string" - example: "registry_user" - description: "Username used to authenticate against this registry" + type: 'string' + example: 'registry_user' + description: 'Username used to authenticate against this registry' Password: - type: "string" - example: "registry_password" - description: "Password used to authenticate against this registry" + type: 'string' + example: 'registry_password' + description: 'Password used to authenticate against this registry' AuthorizedUsers: - type: "array" - description: "List of user identifiers authorized to use this registry" + type: 'array' + description: 'List of user identifiers authorized to use this registry' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' AuthorizedTeams: - type: "array" - description: "List of team identifiers authorized to use this registry" + type: 'array' + description: 'List of team identifiers authorized to use this registry' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' RegistrySubset: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Registry identifier" + description: 'Registry identifier' Name: - type: "string" - example: "my-registry" - description: "Registry name" + type: 'string' + example: 'my-registry' + description: 'Registry name' URL: - type: "string" - example: "registry.mydomain.tld:2375" - description: "URL or IP address of the Docker registry" + type: 'string' + example: 'registry.mydomain.tld:2375' + description: 'URL or IP address of the Docker registry' Authentication: - type: "boolean" + type: 'boolean' example: true - description: "Is authentication against this registry enabled" + description: 'Is authentication against this registry enabled' Username: - type: "string" - example: "registry_user" - description: "Username used to authenticate against this registry" + type: 'string' + example: 'registry_user' + description: 'Username used to authenticate against this registry' AuthorizedUsers: - type: "array" - description: "List of user identifiers authorized to use this registry" + type: 'array' + description: 'List of user identifiers authorized to use this registry' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' AuthorizedTeams: - type: "array" - description: "List of team identifiers authorized to use this registry" + type: 'array' + description: 'List of team identifiers authorized to use this registry' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' EndpointGroup: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint group identifier" + description: 'Endpoint group identifier' Name: - type: "string" - example: "my-endpoint-group" - description: "Endpoint group name" + type: 'string' + example: 'my-endpoint-group' + description: 'Endpoint group name' Description: - type: "string" - example: "Description associated to the endpoint group" - description: "Endpoint group description" + type: 'string' + example: 'Description associated to the endpoint group' + description: 'Endpoint group description' AuthorizedUsers: - type: "array" - description: "List of user identifiers authorized to connect to this endpoint group. Will be inherited by endpoints that are part of the group" + type: 'array' + description: 'List of user identifiers authorized to connect to this endpoint group. Will be inherited by endpoints that are part of the group' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' AuthorizedTeams: - type: "array" - description: "List of team identifiers authorized to connect to this endpoint. Will be inherited by endpoints that are part of the group" + type: 'array' + description: 'List of team identifiers authorized to connect to this endpoint. Will be inherited by endpoints that are part of the group' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' Labels: - type: "array" + type: 'array' items: - $ref: "#/definitions/Pair" + $ref: '#/definitions/Pair' Endpoint: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint identifier" + description: 'Endpoint identifier' Name: - type: "string" - example: "my-endpoint" - description: "Endpoint name" + type: 'string' + example: 'my-endpoint' + description: 'Endpoint name' Type: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint environment type. 1 for a Docker environment, 2 for an agent on Docker environment or 3 for an Azure environment." + description: 'Endpoint environment type. 1 for a Docker environment, 2 for an agent on Docker environment or 3 for an Azure environment.' URL: - type: "string" - example: "docker.mydomain.tld:2375" - description: "URL or IP address of the Docker host associated to this endpoint" + type: 'string' + example: 'docker.mydomain.tld:2375' + description: 'URL or IP address of the Docker host associated to this endpoint' PublicURL: - type: "string" - example: "docker.mydomain.tld:2375" - description: "URL or IP address where exposed containers will be reachable" + type: 'string' + example: 'docker.mydomain.tld:2375' + description: 'URL or IP address where exposed containers will be reachable' GroupID: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint group identifier" + description: 'Endpoint group identifier' AuthorizedUsers: - type: "array" - description: "List of user identifiers authorized to connect to this endpoint" + type: 'array' + description: 'List of user identifiers authorized to connect to this endpoint' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' AuthorizedTeams: - type: "array" - description: "List of team identifiers authorized to connect to this endpoint" + type: 'array' + description: 'List of team identifiers authorized to connect to this endpoint' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' TLSConfig: - $ref: "#/definitions/TLSConfiguration" + $ref: '#/definitions/TLSConfiguration' AzureCredentials: - $ref: "#/definitions/AzureCredentials" + $ref: '#/definitions/AzureCredentials' EndpointSubset: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint identifier" + description: 'Endpoint identifier' Name: - type: "string" - example: "my-endpoint" - description: "Endpoint name" + type: 'string' + example: 'my-endpoint' + description: 'Endpoint name' Type: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint environment type. 1 for a Docker environment, 2 for an agent on Docker environment, 3 for an Azure environment." + description: 'Endpoint environment type. 1 for a Docker environment, 2 for an agent on Docker environment, 3 for an Azure environment.' URL: - type: "string" - example: "docker.mydomain.tld:2375" - description: "URL or IP address of the Docker host associated to this endpoint" + type: 'string' + example: 'docker.mydomain.tld:2375' + description: 'URL or IP address of the Docker host associated to this endpoint' PublicURL: - type: "string" - example: "docker.mydomain.tld:2375" - description: "URL or IP address where exposed containers will be reachable" + type: 'string' + example: 'docker.mydomain.tld:2375' + description: 'URL or IP address where exposed containers will be reachable' GroupID: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint group identifier" + description: 'Endpoint group identifier' AuthorizedUsers: - type: "array" - description: "List of user identifiers authorized to connect to this endpoint" + type: 'array' + description: 'List of user identifiers authorized to connect to this endpoint' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' AuthorizedTeams: - type: "array" - description: "List of team identifiers authorized to connect to this endpoint" + type: 'array' + description: 'List of team identifiers authorized to connect to this endpoint' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' TLSConfig: - $ref: "#/definitions/TLSConfiguration" + $ref: '#/definitions/TLSConfiguration' GenericError: - type: "object" + type: 'object' properties: err: - type: "string" - example: "Something bad happened" - description: "Error message" + type: 'string' + example: 'Something bad happened' + description: 'Error message' AuthenticateUserRequest: - type: "object" + type: 'object' required: - - "Password" - - "Username" + - 'Password' + - 'Username' properties: Username: - type: "string" - example: "admin" - description: "Username" + type: 'string' + example: 'admin' + description: 'Username' Password: - type: "string" - example: "mypassword" - description: "Password" + type: 'string' + example: 'mypassword' + description: 'Password' AuthenticateUserResponse: - type: "object" + type: 'object' properties: jwt: - type: "string" - example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsImV4cCI6MTQ5OTM3NjE1NH0.NJ6vE8FY1WG6jsRQzfMqeatJ4vh2TWAeeYfDhP71YEE" - description: "JWT token used to authenticate against the API" + type: 'string' + example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsImV4cCI6MTQ5OTM3NjE1NH0.NJ6vE8FY1WG6jsRQzfMqeatJ4vh2TWAeeYfDhP71YEE' + description: 'JWT token used to authenticate against the API' DockerHubSubset: - type: "object" + type: 'object' properties: Authentication: - type: "boolean" + type: 'boolean' example: true - description: "Is authentication against DockerHub enabled" + description: 'Is authentication against DockerHub enabled' Username: - type: "string" - example: "hub_user" - description: "Username used to authenticate against the DockerHub" + type: 'string' + example: 'hub_user' + description: 'Username used to authenticate against the DockerHub' DockerHub: - type: "object" + type: 'object' properties: Authentication: - type: "boolean" + type: 'boolean' example: true - description: "Is authentication against DockerHub enabled" + description: 'Is authentication against DockerHub enabled' Username: - type: "string" - example: "hub_user" - description: "Username used to authenticate against the DockerHub" + type: 'string' + example: 'hub_user' + description: 'Username used to authenticate against the DockerHub' Password: - type: "string" - example: "hub_password" - description: "Password used to authenticate against the DockerHub" + type: 'string' + example: 'hub_password' + description: 'Password used to authenticate against the DockerHub' ResourceControl: - type: "object" + type: 'object' properties: ResourceID: - type: "string" - example: "617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08" + type: 'string' + example: '617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08' description: "Docker resource identifier on which access control will be applied.\ \ In the case of a resource control applied to a stack, use the stack name as identifier" Type: - type: "string" - example: "container" + type: 'string' + example: 'container' description: "Type of Docker resource. Valid values are: container, volume\ \ service, secret, config or stack" Public: - type: "boolean" + type: 'boolean' example: true - description: "Permit access to the associated resource to any user" + description: 'Permit access to the associated resource to any user' Users: - type: "array" - description: "List of user identifiers with access to the associated resource" + type: 'array' + description: 'List of user identifiers with access to the associated resource' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' Teams: - type: "array" - description: "List of team identifiers with access to the associated resource" + type: 'array' + description: 'List of team identifiers with access to the associated resource' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' SubResourceIDs: - type: "array" - description: "List of Docker resources that will inherit this access control" + type: 'array' + description: 'List of Docker resources that will inherit this access control' items: - type: "string" - example: "617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08" - description: "Docker resource identifier" + type: 'string' + example: '617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08' + description: 'Docker resource identifier' DockerHubUpdateRequest: - type: "object" + type: 'object' required: - - "Authentication" - - "Password" - - "Username" + - 'Authentication' + - 'Password' + - 'Username' properties: Authentication: - type: "boolean" + type: 'boolean' example: true - description: "Enable authentication against DockerHub" + description: 'Enable authentication against DockerHub' Username: - type: "string" - example: "hub_user" - description: "Username used to authenticate against the DockerHub" + type: 'string' + example: 'hub_user' + description: 'Username used to authenticate against the DockerHub' Password: - type: "string" - example: "hub_password" - description: "Password used to authenticate against the DockerHub" + type: 'string' + example: 'hub_password' + description: 'Password used to authenticate against the DockerHub' EndpointListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/EndpointSubset" + $ref: '#/definitions/EndpointSubset' EndpointGroupListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/EndpointGroup" + $ref: '#/definitions/EndpointGroup' EndpointUpdateRequest: - type: "object" + type: 'object' properties: Name: - type: "string" - example: "my-endpoint" - description: "Name that will be used to identify this endpoint" + type: 'string' + example: 'my-endpoint' + description: 'Name that will be used to identify this endpoint' URL: - type: "string" - example: "docker.mydomain.tld:2375" - description: "URL or IP address of a Docker host" + type: 'string' + example: 'docker.mydomain.tld:2375' + description: 'URL or IP address of a Docker host' PublicURL: - type: "string" - example: "docker.mydomain.tld:2375" + type: 'string' + example: 'docker.mydomain.tld:2375' description: "URL or IP address where exposed containers will be reachable.\ \ Defaults to URL if not specified" GroupID: - type: "integer" - example: "1" - description: "Group identifier" + type: 'integer' + example: '1' + description: 'Group identifier' TLS: - type: "boolean" + type: 'boolean' example: true - description: "Require TLS to connect against this endpoint" + description: 'Require TLS to connect against this endpoint' TLSSkipVerify: - type: "boolean" + type: 'boolean' example: false - description: "Skip server verification when using TLS" + description: 'Skip server verification when using TLS' TLSSkipClientVerify: - type: "boolean" + type: 'boolean' example: false - description: "Skip client verification when using TLS" + description: 'Skip client verification when using TLS' ApplicationID: - type: "string" - example: "eag7cdo9-o09l-9i83-9dO9-f0b23oe78db4" - description: "Azure application ID" + type: 'string' + example: 'eag7cdo9-o09l-9i83-9dO9-f0b23oe78db4' + description: 'Azure application ID' TenantID: - type: "string" - example: "34ddc78d-4fel-2358-8cc1-df84c8o839f5" - description: "Azure tenant ID" + type: 'string' + example: '34ddc78d-4fel-2358-8cc1-df84c8o839f5' + description: 'Azure tenant ID' AuthenticationKey: - type: "string" - example: "cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk=" - description: "Azure authentication key" + type: 'string' + example: 'cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk=' + description: 'Azure authentication key' UserAccessPolicies: - $ref: "#/definitions/UserAccessPolicies" + $ref: '#/definitions/UserAccessPolicies' TeamAccessPolicies: - $ref: "#/definitions/TeamAccessPolicies" + $ref: '#/definitions/TeamAccessPolicies' RegistryCreateRequest: - type: "object" + type: 'object' required: - - "Authentication" - - "Name" - - "Password" - - "Type" - - "URL" - - "Username" + - 'Authentication' + - 'Name' + - 'Password' + - 'Type' + - 'URL' + - 'Username' properties: Name: - type: "string" - example: "my-registry" - description: "Name that will be used to identify this registry" + type: 'string' + example: 'my-registry' + description: 'Name that will be used to identify this registry' Type: - type: "integer" + type: 'integer' example: 1 - description: "Registry Type. Valid values are: 1 (Quay.io), 2 (Azure container registry) or 3 (custom registry)" + description: 'Registry Type. Valid values are: 1 (Quay.io), 2 (Azure container registry) or 3 (custom registry)' URL: - type: "string" - example: "registry.mydomain.tld:2375" - description: "URL or IP address of the Docker registry" + type: 'string' + example: 'registry.mydomain.tld:2375' + description: 'URL or IP address of the Docker registry' Authentication: - type: "boolean" + type: 'boolean' example: true - description: "Is authentication against this registry enabled" + description: 'Is authentication against this registry enabled' Username: - type: "string" - example: "registry_user" - description: "Username used to authenticate against this registry" + type: 'string' + example: 'registry_user' + description: 'Username used to authenticate against this registry' Password: - type: "string" - example: "registry_password" - description: "Password used to authenticate against this registry" + type: 'string' + example: 'registry_password' + description: 'Password used to authenticate against this registry' RegistryListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/RegistrySubset" + $ref: '#/definitions/RegistrySubset' RegistryUpdateRequest: - type: "object" + type: 'object' required: - - "Name" - - "URL" + - 'Name' + - 'URL' properties: Name: - type: "string" - example: "my-registry" - description: "Name that will be used to identify this registry" + type: 'string' + example: 'my-registry' + description: 'Name that will be used to identify this registry' URL: - type: "string" - example: "registry.mydomain.tld:2375" - description: "URL or IP address of the Docker registry" + type: 'string' + example: 'registry.mydomain.tld:2375' + description: 'URL or IP address of the Docker registry' Authentication: - type: "boolean" + type: 'boolean' example: true - description: "Is authentication against this registry enabled" + description: 'Is authentication against this registry enabled' Username: - type: "string" - example: "registry_user" - description: "Username used to authenticate against this registry" + type: 'string' + example: 'registry_user' + description: 'Username used to authenticate against this registry' Password: - type: "string" - example: "registry_password" - description: "Password used to authenticate against this registry" + type: 'string' + example: 'registry_password' + description: 'Password used to authenticate against this registry' UserAccessPolicies: - $ref: "#/definitions/UserAccessPolicies" + $ref: '#/definitions/UserAccessPolicies' TeamAccessPolicies: - $ref: "#/definitions/TeamAccessPolicies" + $ref: '#/definitions/TeamAccessPolicies' ResourceControlCreateRequest: - type: "object" + type: 'object' required: - - "ResourceID" - - "Type" + - 'ResourceID' + - 'Type' properties: ResourceID: - type: "string" - example: "617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08" + type: 'string' + example: '617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08' description: "Docker resource identifier on which access control will be applied.\ \ In the case of a resource control applied to a stack, use the stack name as identifier" Type: - type: "string" - example: "container" + type: 'string' + example: 'container' description: "Type of Docker resource. Valid values are: container, volume\ \ service, secret, config or stack" Public: - type: "boolean" + type: 'boolean' example: true - description: "Permit access to the associated resource to any user" + description: 'Permit access to the associated resource to any user' Users: - type: "array" - description: "List of user identifiers with access to the associated resource" + type: 'array' + description: 'List of user identifiers with access to the associated resource' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' Teams: - type: "array" - description: "List of team identifiers with access to the associated resource" + type: 'array' + description: 'List of team identifiers with access to the associated resource' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' SubResourceIDs: - type: "array" - description: "List of Docker resources that will inherit this access control" + type: 'array' + description: 'List of Docker resources that will inherit this access control' items: - type: "string" - example: "617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08" - description: "Docker resource identifier" + type: 'string' + example: '617c5f22bb9b023d6daab7cba43a57576f83492867bc767d1c59416b065e5f08' + description: 'Docker resource identifier' ResourceControlUpdateRequest: - type: "object" + type: 'object' properties: Public: - type: "boolean" + type: 'boolean' example: false - description: "Permit access to the associated resource to any user" + description: 'Permit access to the associated resource to any user' Users: - type: "array" - description: "List of user identifiers with access to the associated resource" + type: 'array' + description: 'List of user identifiers with access to the associated resource' items: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' Teams: - type: "array" - description: "List of team identifiers with access to the associated resource" + type: 'array' + description: 'List of team identifiers with access to the associated resource' items: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' SettingsUpdateRequest: - type: "object" + type: 'object' required: - - "TemplatesURL" - - "AuthenticationMethod" + - 'TemplatesURL' + - 'AuthenticationMethod' properties: TemplatesURL: - type: "string" - example: "https://raw.githubusercontent.com/portainer/templates/master/templates.json" + type: 'string' + example: 'https://raw.githubusercontent.com/portainer/templates/master/templates.json' description: "URL to the templates that will be displayed in the UI when navigating\ \ to App Templates" LogoURL: - type: "string" - example: "https://mycompany.mydomain.tld/logo.png" + type: 'string' + example: 'https://mycompany.mydomain.tld/logo.png' description: "URL to a logo that will be displayed on the login page as well\ \ as on top of the sidebar. Will use default Portainer logo when value is\ \ empty string" BlackListedLabels: - type: "array" + type: 'array' description: "A list of label name & value that will be used to hide containers\ \ when querying containers" items: - $ref: "#/definitions/Settings_BlackListedLabels" + $ref: '#/definitions/Settings_BlackListedLabels' DisplayExternalContributors: - type: "boolean" + type: 'boolean' example: false description: "Whether to display or not external templates contributions as\ \ sub-menus in the UI." AuthenticationMethod: - type: "integer" + type: 'integer' example: 1 - description: "Active authentication method for the Portainer instance. Valid values are: 1 for managed or 2 for LDAP." + description: 'Active authentication method for the Portainer instance. Valid values are: 1 for managed or 2 for LDAP.' LDAPSettings: - $ref: "#/definitions/LDAPSettings" + $ref: '#/definitions/LDAPSettings' AllowBindMountsForRegularUsers: - type: "boolean" + type: 'boolean' example: true - description: "Whether non-administrator users should be able to use bind mounts when creating containers" + description: 'Whether non-administrator users should be able to use bind mounts when creating containers' AllowPrivilegedModeForRegularUsers: - type: "boolean" + type: 'boolean' example: true - description: "Whether non-administrator users should be able to use privileged mode when creating containers" + description: 'Whether non-administrator users should be able to use privileged mode when creating containers' EdgeAgentCheckinInterval: - type: "integer" - example: "30" - description: "Polling interval for Edge agent (in seconds)" + type: 'integer' + example: '30' + description: 'Polling interval for Edge agent (in seconds)' EndpointGroupCreateRequest: - type: "object" + type: 'object' required: - - "Name" + - 'Name' properties: Name: - type: "string" - example: "my-endpoint-group" - description: "Endpoint group name" + type: 'string' + example: 'my-endpoint-group' + description: 'Endpoint group name' Description: - type: "string" - example: "Endpoint group description" - description: "Endpoint group description" + type: 'string' + example: 'Endpoint group description' + description: 'Endpoint group description' Labels: - type: "array" + type: 'array' items: - $ref: "#/definitions/Pair" + $ref: '#/definitions/Pair' AssociatedEndpoints: - type: "array" - description: "List of endpoint identifiers that will be part of this group" + type: 'array' + description: 'List of endpoint identifiers that will be part of this group' items: - type: "integer" + type: 'integer' example: 1 - description: "Endpoint identifier" + description: 'Endpoint identifier' EndpointGroupUpdateRequest: - type: "object" + type: 'object' properties: Name: - type: "string" - example: "my-endpoint-group" - description: "Endpoint group name" + type: 'string' + example: 'my-endpoint-group' + description: 'Endpoint group name' Description: - type: "string" - example: "Endpoint group description" - description: "Endpoint group description" + type: 'string' + example: 'Endpoint group description' + description: 'Endpoint group description' Tags: - type: "array" - description: "List of tags associated to the endpoint group" + type: 'array' + description: 'List of tags associated to the endpoint group' items: - type: "string" - example: "zone/east-coast" - description: "Tag" + type: 'string' + example: 'zone/east-coast' + description: 'Tag' UserAccessPolicies: - $ref: "#/definitions/UserAccessPolicies" + $ref: '#/definitions/UserAccessPolicies' TeamAccessPolicies: - $ref: "#/definitions/TeamAccessPolicies" + $ref: '#/definitions/TeamAccessPolicies' UserCreateRequest: - type: "object" + type: 'object' required: - - "Password" - - "Role" - - "Username" + - 'Password' + - 'Role' + - 'Username' properties: Username: - type: "string" - example: "bob" - description: "Username" + type: 'string' + example: 'bob' + description: 'Username' Password: - type: "string" - example: "cg9Wgky3" - description: "Password" + type: 'string' + example: 'cg9Wgky3' + description: 'Password' Role: - type: "integer" + type: 'integer' example: 1 - description: "User role (1 for administrator account and 2 for regular account)" + description: 'User role (1 for administrator account and 2 for regular account)' UserListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/UserSubset" + $ref: '#/definitions/UserSubset' UserUpdateRequest: - type: "object" + type: 'object' properties: Password: - type: "string" - example: "cg9Wgky3" - description: "Password" + type: 'string' + example: 'cg9Wgky3' + description: 'Password' Role: - type: "integer" + type: 'integer' example: 1 - description: "User role (1 for administrator account and 2 for regular account)" + description: 'User role (1 for administrator account and 2 for regular account)' UserMembershipsResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/TeamMembership" + $ref: '#/definitions/TeamMembership' UserPasswordCheckRequest: - type: "object" + type: 'object' required: - - "Password" + - 'Password' properties: Password: - type: "string" - example: "cg9Wgky3" - description: "Password" + type: 'string' + example: 'cg9Wgky3' + description: 'Password' UserPasswordCheckResponse: - type: "object" + type: 'object' properties: valid: - type: "boolean" + type: 'boolean' example: true - description: "Is the password valid" + description: 'Is the password valid' TagListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/Tag" + $ref: '#/definitions/Tag' TagCreateRequest: - type: "object" + type: 'object' required: - - "Name" + - 'Name' properties: Name: - type: "string" - example: "org/acme" - description: "Name" + type: 'string' + example: 'org/acme' + description: 'Name' TeamCreateRequest: - type: "object" + type: 'object' required: - - "Name" + - 'Name' properties: Name: - type: "string" - example: "developers" - description: "Name" + type: 'string' + example: 'developers' + description: 'Name' TeamListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/Team" + $ref: '#/definitions/Team' TeamUpdateRequest: - type: "object" + type: 'object' required: - - "Name" + - 'Name' properties: Name: - type: "string" - example: "developers" - description: "Name" + type: 'string' + example: 'developers' + description: 'Name' TeamMembershipsResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/TeamMembership" + $ref: '#/definitions/TeamMembership' TeamMembershipCreateRequest: - type: "object" + type: 'object' required: - - "UserID" - - "TeamID" - - "Role" + - 'UserID' + - 'TeamID' + - 'Role' properties: UserID: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' TeamID: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' Role: - type: "integer" + type: 'integer' example: 1 - description: "Role for the user inside the team (1 for leader and 2 for regular member)" + description: 'Role for the user inside the team (1 for leader and 2 for regular member)' TeamMembershipListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/TeamMembership" + $ref: '#/definitions/TeamMembership' TeamMembershipUpdateRequest: - type: "object" + type: 'object' required: - - "UserID" - - "TeamID" - - "Role" + - 'UserID' + - 'TeamID' + - 'Role' properties: UserID: - type: "integer" + type: 'integer' example: 1 - description: "User identifier" + description: 'User identifier' TeamID: - type: "integer" + type: 'integer' example: 1 - description: "Team identifier" + description: 'Team identifier' Role: - type: "integer" + type: 'integer' example: 1 - description: "Role for the user inside the team (1 for leader and 2 for regular member)" + description: 'Role for the user inside the team (1 for leader and 2 for regular member)' SettingsLDAPCheckRequest: - type: "object" + type: 'object' properties: LDAPSettings: - $ref: "#/definitions/LDAPSettings" + $ref: '#/definitions/LDAPSettings' UserAdminInitRequest: - type: "object" + type: 'object' properties: Username: - type: "string" - example: "admin" - description: "Username for the admin user" + type: 'string' + example: 'admin' + description: 'Username for the admin user' Password: - type: "string" - example: "admin-password" - description: "Password for the admin user" + type: 'string' + example: 'admin-password' + description: 'Password for the admin user' TemplateListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/Template" + $ref: '#/definitions/Template' TemplateCreateRequest: - type: "object" + type: 'object' required: - - "type" - - "title" - - "description" + - 'type' + - 'title' + - 'description' properties: type: - type: "integer" + type: 'integer' example: 1 - description: "Template type. Valid values are: 1 (container), 2 (Swarm stack) or 3 (Compose stack)" + description: 'Template type. Valid values are: 1 (container), 2 (Swarm stack) or 3 (Compose stack)' title: - type: "string" - example: "Nginx" - description: "Title of the template" + type: 'string' + example: 'Nginx' + description: 'Title of the template' description: - type: "string" - example: "High performance web server" - description: "Description of the template" + type: 'string' + example: 'High performance web server' + description: 'Description of the template' administrator_only: - type: "boolean" + type: 'boolean' example: true - description: "Whether the template should be available to administrators only" + description: 'Whether the template should be available to administrators only' image: - type: "string" - example: "nginx:latest" - description: "Image associated to a container template. Mandatory for a container template" + type: 'string' + example: 'nginx:latest' + description: 'Image associated to a container template. Mandatory for a container template' repository: - $ref: "#/definitions/TemplateRepository" + $ref: '#/definitions/TemplateRepository' name: - type: "string" - example: "mystackname" - description: "Default name for the stack/container to be used on deployment" + type: 'string' + example: 'mystackname' + description: 'Default name for the stack/container to be used on deployment' logo: - type: "string" - example: "https://cloudinovasi.id/assets/img/logos/nginx.png" + type: 'string' + example: 'https://cloudinovasi.id/assets/img/logos/nginx.png' description: "URL of the template's logo" env: - type: "array" - description: "A list of environment variables used during the template deployment" + type: 'array' + description: 'A list of environment variables used during the template deployment' items: - $ref: "#/definitions/TemplateEnv" + $ref: '#/definitions/TemplateEnv' note: - type: "string" - example: "This is my custom template" - description: "A note that will be displayed in the UI. Supports HTML content" + type: 'string' + example: 'This is my custom template' + description: 'A note that will be displayed in the UI. Supports HTML content' platform: - type: "string" - example: "linux" + type: 'string' + example: 'linux' description: "Platform associated to the template. Valid values are: 'linux', 'windows' or leave empty for multi-platform" categories: - type: "array" - description: "A list of categories associated to the template" + type: 'array' + description: 'A list of categories associated to the template' items: - type: "string" - example: "database" + type: 'string' + example: 'database' registry: - type: "string" - example: "quay.io" - description: "The URL of a registry associated to the image for a container template" + type: 'string' + example: 'quay.io' + description: 'The URL of a registry associated to the image for a container template' command: - type: "string" - example: "ls -lah" - description: "The command that will be executed in a container template" + type: 'string' + example: 'ls -lah' + description: 'The command that will be executed in a container template' network: - type: "string" - example: "mynet" - description: "Name of a network that will be used on container deployment if it exists inside the environment" + type: 'string' + example: 'mynet' + description: 'Name of a network that will be used on container deployment if it exists inside the environment' volumes: - type: "array" - description: "A list of volumes used during the container template deployment" + type: 'array' + description: 'A list of volumes used during the container template deployment' items: - $ref: "#/definitions/TemplateVolume" + $ref: '#/definitions/TemplateVolume' ports: - type: "array" - description: "A list of ports exposed by the container" + type: 'array' + description: 'A list of ports exposed by the container' items: - type: "string" - example: "8080:80/tcp" + type: 'string' + example: '8080:80/tcp' labels: - type: "array" - description: "Container labels" + type: 'array' + description: 'Container labels' items: $ref: '#/definitions/Pair' privileged: - type: "boolean" + type: 'boolean' example: true - description: "Whether the container should be started in privileged mode" + description: 'Whether the container should be started in privileged mode' interactive: - type: "boolean" + type: 'boolean' example: true - description: "Whether the container should be started in interactive mode (-i -t equivalent on the CLI)" + description: 'Whether the container should be started in interactive mode (-i -t equivalent on the CLI)' restart_policy: - type: "string" - example: "on-failure" - description: "Container restart policy" + type: 'string' + example: 'on-failure' + description: 'Container restart policy' hostname: - type: "string" - example: "mycontainer" - description: "Container hostname" + type: 'string' + example: 'mycontainer' + description: 'Container hostname' TemplateUpdateRequest: - type: "object" + type: 'object' properties: type: - type: "integer" + type: 'integer' example: 1 - description: "Template type. Valid values are: 1 (container), 2 (Swarm stack) or 3 (Compose stack)" + description: 'Template type. Valid values are: 1 (container), 2 (Swarm stack) or 3 (Compose stack)' title: - type: "string" - example: "Nginx" - description: "Title of the template" + type: 'string' + example: 'Nginx' + description: 'Title of the template' description: - type: "string" - example: "High performance web server" - description: "Description of the template" + type: 'string' + example: 'High performance web server' + description: 'Description of the template' administrator_only: - type: "boolean" + type: 'boolean' example: true - description: "Whether the template should be available to administrators only" + description: 'Whether the template should be available to administrators only' image: - type: "string" - example: "nginx:latest" - description: "Image associated to a container template. Mandatory for a container template" + type: 'string' + example: 'nginx:latest' + description: 'Image associated to a container template. Mandatory for a container template' repository: - $ref: "#/definitions/TemplateRepository" + $ref: '#/definitions/TemplateRepository' name: - type: "string" - example: "mystackname" - description: "Default name for the stack/container to be used on deployment" + type: 'string' + example: 'mystackname' + description: 'Default name for the stack/container to be used on deployment' logo: - type: "string" - example: "https://cloudinovasi.id/assets/img/logos/nginx.png" + type: 'string' + example: 'https://cloudinovasi.id/assets/img/logos/nginx.png' description: "URL of the template's logo" env: - type: "array" - description: "A list of environment variables used during the template deployment" + type: 'array' + description: 'A list of environment variables used during the template deployment' items: - $ref: "#/definitions/TemplateEnv" + $ref: '#/definitions/TemplateEnv' note: - type: "string" - example: "This is my custom template" - description: "A note that will be displayed in the UI. Supports HTML content" + type: 'string' + example: 'This is my custom template' + description: 'A note that will be displayed in the UI. Supports HTML content' platform: - type: "string" - example: "linux" + type: 'string' + example: 'linux' description: "Platform associated to the template. Valid values are: 'linux', 'windows' or leave empty for multi-platform" categories: - type: "array" - description: "A list of categories associated to the template" + type: 'array' + description: 'A list of categories associated to the template' items: - type: "string" - example: "database" + type: 'string' + example: 'database' registry: - type: "string" - example: "quay.io" - description: "The URL of a registry associated to the image for a container template" + type: 'string' + example: 'quay.io' + description: 'The URL of a registry associated to the image for a container template' command: - type: "string" - example: "ls -lah" - description: "The command that will be executed in a container template" + type: 'string' + example: 'ls -lah' + description: 'The command that will be executed in a container template' network: - type: "string" - example: "mynet" - description: "Name of a network that will be used on container deployment if it exists inside the environment" + type: 'string' + example: 'mynet' + description: 'Name of a network that will be used on container deployment if it exists inside the environment' volumes: - type: "array" - description: "A list of volumes used during the container template deployment" + type: 'array' + description: 'A list of volumes used during the container template deployment' items: - $ref: "#/definitions/TemplateVolume" + $ref: '#/definitions/TemplateVolume' ports: - type: "array" - description: "A list of ports exposed by the container" + type: 'array' + description: 'A list of ports exposed by the container' items: - type: "string" - example: "8080:80/tcp" + type: 'string' + example: '8080:80/tcp' labels: - type: "array" - description: "Container labels" + type: 'array' + description: 'Container labels' items: $ref: '#/definitions/Pair' privileged: - type: "boolean" + type: 'boolean' example: true - description: "Whether the container should be started in privileged mode" + description: 'Whether the container should be started in privileged mode' interactive: - type: "boolean" + type: 'boolean' example: true - description: "Whether the container should be started in interactive mode (-i -t equivalent on the CLI)" + description: 'Whether the container should be started in interactive mode (-i -t equivalent on the CLI)' restart_policy: - type: "string" - example: "on-failure" - description: "Container restart policy" + type: 'string' + example: 'on-failure' + description: 'Container restart policy' hostname: - type: "string" - example: "mycontainer" - description: "Container hostname" + type: 'string' + example: 'mycontainer' + description: 'Container hostname' Template: - type: "object" + type: 'object' properties: id: - type: "integer" + type: 'integer' example: 1 - description: "Template identifier" + description: 'Template identifier' type: - type: "integer" + type: 'integer' example: 1 - description: "Template type. Valid values are: 1 (container), 2 (Swarm stack) or 3 (Compose stack)" + description: 'Template type. Valid values are: 1 (container), 2 (Swarm stack) or 3 (Compose stack)' title: - type: "string" - example: "Nginx" - description: "Title of the template" + type: 'string' + example: 'Nginx' + description: 'Title of the template' description: - type: "string" - example: "High performance web server" - description: "Description of the template" + type: 'string' + example: 'High performance web server' + description: 'Description of the template' administrator_only: - type: "boolean" + type: 'boolean' example: true - description: "Whether the template should be available to administrators only" + description: 'Whether the template should be available to administrators only' image: - type: "string" - example: "nginx:latest" - description: "Image associated to a container template. Mandatory for a container template" + type: 'string' + example: 'nginx:latest' + description: 'Image associated to a container template. Mandatory for a container template' repository: - $ref: "#/definitions/TemplateRepository" + $ref: '#/definitions/TemplateRepository' name: - type: "string" - example: "mystackname" - description: "Default name for the stack/container to be used on deployment" + type: 'string' + example: 'mystackname' + description: 'Default name for the stack/container to be used on deployment' logo: - type: "string" - example: "https://cloudinovasi.id/assets/img/logos/nginx.png" + type: 'string' + example: 'https://cloudinovasi.id/assets/img/logos/nginx.png' description: "URL of the template's logo" env: - type: "array" - description: "A list of environment variables used during the template deployment" + type: 'array' + description: 'A list of environment variables used during the template deployment' items: - $ref: "#/definitions/TemplateEnv" + $ref: '#/definitions/TemplateEnv' note: - type: "string" - example: "This is my custom template" - description: "A note that will be displayed in the UI. Supports HTML content" + type: 'string' + example: 'This is my custom template' + description: 'A note that will be displayed in the UI. Supports HTML content' platform: - type: "string" - example: "linux" + type: 'string' + example: 'linux' description: "Platform associated to the template. Valid values are: 'linux', 'windows' or leave empty for multi-platform" categories: - type: "array" - description: "A list of categories associated to the template" + type: 'array' + description: 'A list of categories associated to the template' items: - type: "string" - example: "database" + type: 'string' + example: 'database' registry: - type: "string" - example: "quay.io" - description: "The URL of a registry associated to the image for a container template" + type: 'string' + example: 'quay.io' + description: 'The URL of a registry associated to the image for a container template' command: - type: "string" - example: "ls -lah" - description: "The command that will be executed in a container template" + type: 'string' + example: 'ls -lah' + description: 'The command that will be executed in a container template' network: - type: "string" - example: "mynet" - description: "Name of a network that will be used on container deployment if it exists inside the environment" + type: 'string' + example: 'mynet' + description: 'Name of a network that will be used on container deployment if it exists inside the environment' volumes: - type: "array" - description: "A list of volumes used during the container template deployment" + type: 'array' + description: 'A list of volumes used during the container template deployment' items: - $ref: "#/definitions/TemplateVolume" + $ref: '#/definitions/TemplateVolume' ports: - type: "array" - description: "A list of ports exposed by the container" + type: 'array' + description: 'A list of ports exposed by the container' items: - type: "string" - example: "8080:80/tcp" + type: 'string' + example: '8080:80/tcp' labels: - type: "array" - description: "Container labels" + type: 'array' + description: 'Container labels' items: $ref: '#/definitions/Pair' privileged: - type: "boolean" + type: 'boolean' example: true - description: "Whether the container should be started in privileged mode" + description: 'Whether the container should be started in privileged mode' interactive: - type: "boolean" + type: 'boolean' example: true - description: "Whether the container should be started in interactive mode (-i -t equivalent on the CLI)" + description: 'Whether the container should be started in interactive mode (-i -t equivalent on the CLI)' restart_policy: - type: "string" - example: "on-failure" - description: "Container restart policy" + type: 'string' + example: 'on-failure' + description: 'Container restart policy' hostname: - type: "string" - example: "mycontainer" - description: "Container hostname" + type: 'string' + example: 'mycontainer' + description: 'Container hostname' TemplateVolume: - type: "object" + type: 'object' properties: container: - type: "string" - example: "/data" - description: "Path inside the container" + type: 'string' + example: '/data' + description: 'Path inside the container' bind: - type: "string" - example: "/tmp" - description: "Path on the host" + type: 'string' + example: '/tmp' + description: 'Path on the host' readonly: - type: "boolean" + type: 'boolean' example: true - description: "Whether the volume used should be readonly" + description: 'Whether the volume used should be readonly' TemplateEnv: - type: "object" + type: 'object' properties: name: - type: "string" - example: "MYSQL_ROOT_PASSWORD" - description: "name of the environment variable" + type: 'string' + example: 'MYSQL_ROOT_PASSWORD' + description: 'name of the environment variable' label: - type: "string" - example: "Root password" - description: "Text for the label that will be generated in the UI" + type: 'string' + example: 'Root password' + description: 'Text for the label that will be generated in the UI' description: - type: "string" - example: "MySQL root account password" - description: "Content of the tooltip that will be generated in the UI" + type: 'string' + example: 'MySQL root account password' + description: 'Content of the tooltip that will be generated in the UI' default: - type: "string" - example: "default_value" - description: "Default value that will be set for the variable" + type: 'string' + example: 'default_value' + description: 'Default value that will be set for the variable' preset: - type: "boolean" + type: 'boolean' example: true - description: "If set to true, will not generate any input for this variable in the UI" + description: 'If set to true, will not generate any input for this variable in the UI' select: - type: "array" - description: "A list of name/value that will be used to generate a dropdown in the UI" + type: 'array' + description: 'A list of name/value that will be used to generate a dropdown in the UI' items: $ref: '#/definitions/TemplateEnvSelect' TemplateEnvSelect: - type: "object" + type: 'object' properties: text: - type: "string" - example: "text value" - description: "Some text that will displayed as a choice" + type: 'string' + example: 'text value' + description: 'Some text that will displayed as a choice' value: - type: "string" - example: "value" - description: "A value that will be associated to the choice" + type: 'string' + example: 'value' + description: 'A value that will be associated to the choice' default: - type: "boolean" + type: 'boolean' example: true - description: "Will set this choice as the default choice" + description: 'Will set this choice as the default choice' TemplateRepository: - type: "object" + type: 'object' required: - - "URL" + - 'URL' properties: URL: - type: "string" - example: "https://github.com/portainer/portainer-compose" - description: "URL of a git repository used to deploy a stack template. Mandatory for a Swarm/Compose stack template" + type: 'string' + example: 'https://github.com/portainer/portainer-compose' + description: 'URL of a git repository used to deploy a stack template. Mandatory for a Swarm/Compose stack template' stackfile: - type: "string" - example: "./subfolder/docker-compose.yml" - description: "Path to the stack file inside the git repository" + type: 'string' + example: './subfolder/docker-compose.yml' + description: 'Path to the stack file inside the git repository' StackMigrateRequest: - type: "object" + type: 'object' required: - - "EndpointID" + - 'EndpointID' properties: EndpointID: - type: "integer" + type: 'integer' example: 2 - description: "Endpoint identifier of the target endpoint where the stack will be relocated" + description: 'Endpoint identifier of the target endpoint where the stack will be relocated' SwarmID: - type: "string" - example: "jpofkc0i9uo9wtx1zesuk649w" - description: "Swarm cluster identifier, must match the identifier of the cluster where the stack will be relocated" + type: 'string' + example: 'jpofkc0i9uo9wtx1zesuk649w' + description: 'Swarm cluster identifier, must match the identifier of the cluster where the stack will be relocated' Name: - type: "string" - example: "new-stack" - description: "If provided will rename the migrated stack" + type: 'string' + example: 'new-stack' + description: 'If provided will rename the migrated stack' EndpointJobRequest: - type: "object" + type: 'object' required: - - "Image" - - "FileContent" + - 'Image' + - 'FileContent' properties: Image: - type: "string" - example: "ubuntu:latest" - description: "Container image which will be used to execute the job" + type: 'string' + example: 'ubuntu:latest' + description: 'Container image which will be used to execute the job' FileContent: - type: "string" - example: "ls -lah /host/tmp" - description: "Content of the job script" + type: 'string' + example: 'ls -lah /host/tmp' + description: 'Content of the job script' StackCreateRequest: - type: "object" + type: 'object' required: - - "Name" + - 'Name' properties: Name: - type: "string" - example: "myStack" - description: "Name of the stack" + type: 'string' + example: 'myStack' + description: 'Name of the stack' SwarmID: - type: "string" - example: "jpofkc0i9uo9wtx1zesuk649w" - description: "Swarm cluster identifier. Required when creating a Swarm stack (type 1)." + type: 'string' + example: 'jpofkc0i9uo9wtx1zesuk649w' + description: 'Swarm cluster identifier. Required when creating a Swarm stack (type 1).' StackFileContent: - type: "string" + type: 'string' example: "version: 3\n services:\n web:\n image:nginx" description: "Content of the Stack file. Required when using the 'string' deployment method." RepositoryURL: - type: "string" - example: "https://github.com/openfaas/faas" + type: 'string' + example: 'https://github.com/openfaas/faas' description: "URL of a Git repository hosting the Stack file. Required when using the 'repository' deployment method." RepositoryReferenceName: - type: "string" - example: "refs/heads/master" + type: 'string' + example: 'refs/heads/master' description: "Reference name of a Git repository hosting the Stack file. Used in 'repository' deployment method." ComposeFilePathInRepository: - type: "string" - example: "docker-compose.yml" + type: 'string' + example: 'docker-compose.yml' description: "Path to the Stack file inside the Git repository. Will default to 'docker-compose.yml' if not specified." RepositoryAuthentication: - type: "boolean" + type: 'boolean' example: true - description: "Use basic authentication to clone the Git repository." + description: 'Use basic authentication to clone the Git repository.' RepositoryUsername: - type: "string" - example: "myGitUsername" - description: "Username used in basic authentication. Required when RepositoryAuthentication is true." + type: 'string' + example: 'myGitUsername' + description: 'Username used in basic authentication. Required when RepositoryAuthentication is true.' RepositoryPassword: - type: "string" - example: "myGitPassword" - description: "Password used in basic authentication. Required when RepositoryAuthentication is true." + type: 'string' + example: 'myGitPassword' + description: 'Password used in basic authentication. Required when RepositoryAuthentication is true.' Env: - type: "array" - description: "A list of environment variables used during stack deployment" + type: 'array' + description: 'A list of environment variables used during stack deployment' items: - $ref: "#/definitions/Stack_Env" + $ref: '#/definitions/Stack_Env' Stack_Env: properties: name: - type: "string" - example: "MYSQL_ROOT_PASSWORD" + type: 'string' + example: 'MYSQL_ROOT_PASSWORD' value: - type: "string" - example: "password" + type: 'string' + example: 'password' StackListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/Stack" + $ref: '#/definitions/Stack' Stack: - type: "object" + type: 'object' properties: Id: - type: "string" - example: "myStack_jpofkc0i9uo9wtx1zesuk649w" - description: "Stack identifier" + type: 'string' + example: 'myStack_jpofkc0i9uo9wtx1zesuk649w' + description: 'Stack identifier' Name: - type: "string" - example: "myStack" - description: "Stack name" + type: 'string' + example: 'myStack' + description: 'Stack name' Type: - type: "integer" - example: "1" - description: "Stack type. 1 for a Swarm stack, 2 for a Compose stack" + type: 'integer' + example: '1' + description: 'Stack type. 1 for a Swarm stack, 2 for a Compose stack' EndpointID: - type: "integer" - example: "1" - description: "Endpoint identifier. Reference the endpoint that will be used for deployment " + type: 'integer' + example: '1' + description: 'Endpoint identifier. Reference the endpoint that will be used for deployment ' EntryPoint: - type: "string" - example: "docker-compose.yml" - description: "Path to the Stack file" + type: 'string' + example: 'docker-compose.yml' + description: 'Path to the Stack file' SwarmID: - type: "string" - example: "jpofkc0i9uo9wtx1zesuk649w" - description: "Cluster identifier of the Swarm cluster where the stack is deployed" + type: 'string' + example: 'jpofkc0i9uo9wtx1zesuk649w' + description: 'Cluster identifier of the Swarm cluster where the stack is deployed' ProjectPath: - type: "string" - example: "/data/compose/myStack_jpofkc0i9uo9wtx1zesuk649w" - description: "Path on disk to the repository hosting the Stack file" + type: 'string' + example: '/data/compose/myStack_jpofkc0i9uo9wtx1zesuk649w' + description: 'Path on disk to the repository hosting the Stack file' Env: - type: "array" - description: "A list of environment variables used during stack deployment" + type: 'array' + description: 'A list of environment variables used during stack deployment' items: - $ref: "#/definitions/Stack_Env" + $ref: '#/definitions/Stack_Env' StackUpdateRequest: - type: "object" + type: 'object' properties: StackFileContent: - type: "string" + type: 'string' example: "version: 3\n services:\n web:\n image:nginx" - description: "New content of the Stack file." + description: 'New content of the Stack file.' Env: - type: "array" - description: "A list of environment variables used during stack deployment" + type: 'array' + description: 'A list of environment variables used during stack deployment' items: - $ref: "#/definitions/Stack_Env" + $ref: '#/definitions/Stack_Env' Prune: - type: "boolean" + type: 'boolean' example: false - description: "Prune services that are no longer referenced (only available for Swarm stacks)" + description: 'Prune services that are no longer referenced (only available for Swarm stacks)' StackFileInspectResponse: - type: "object" + type: 'object' properties: StackFileContent: - type: "string" + type: 'string' example: "version: 3\n services:\n web:\n image:nginx" - description: "Content of the Stack file." + description: 'Content of the Stack file.' LicenseInformation: - type: "object" + type: 'object' properties: LicenseKey: - type: "string" - description: "License key" - example: "1-uKmVwboSWVIZv5URmE0VRkpbPX0rrCVeDxJl97LZ0piltw2SU28DSrNwPZAHCEAwB2SeKm6BCFcVwzGMBEixKQ" + type: 'string' + description: 'License key' + example: '1-uKmVwboSWVIZv5URmE0VRkpbPX0rrCVeDxJl97LZ0piltw2SU28DSrNwPZAHCEAwB2SeKm6BCFcVwzGMBEixKQ' Company: - type: "string" - description: "Company associated to the license" - example: "Portainer.io" + type: 'string' + description: 'Company associated to the license' + example: 'Portainer.io' Expiration: - type: "string" - description: "License expiry date" - example: "2077-07-07" + type: 'string' + description: 'License expiry date' + example: '2077-07-07' Valid: - type: "boolean" - description: "Is the license valid" - example: "true" + type: 'boolean' + description: 'Is the license valid' + example: 'true' Extension: - type: "object" + type: 'object' properties: Id: - type: "integer" + type: 'integer' example: 1 - description: "Extension identifier" + description: 'Extension identifier' Name: - type: "string" - example: "Registry Manager" - description: "Extension name" + type: 'string' + example: 'Registry Manager' + description: 'Extension name' Enabled: - type: "boolean" - example: "true" - description: "Is the extension enabled" + type: 'boolean' + example: 'true' + description: 'Is the extension enabled' ShortDescription: - type: "string" - description: "Short description about the extension" - example: "Enable in-app registry management" + type: 'string' + description: 'Short description about the extension' + example: 'Enable in-app registry management' DescriptionURL: - type: "string" - description: "URL to the file containing the extension description" + type: 'string' + description: 'URL to the file containing the extension description' example: https://portainer-io-assets.sfo2.digitaloceanspaces.com/description_registry_manager.html" Available: - type: "boolean" - description: "Is the extension available for download and activation" - example: "true" + type: 'boolean' + description: 'Is the extension available for download and activation' + example: 'true' Images: - type: "array" - description: "List of screenshot URLs" + type: 'array' + description: 'List of screenshot URLs' items: - type: "string" - example: "https://portainer-io-assets.sfo2.digitaloceanspaces.com/screenshots/rm01.png" - description: "Screenshot URL" + type: 'string' + example: 'https://portainer-io-assets.sfo2.digitaloceanspaces.com/screenshots/rm01.png' + description: 'Screenshot URL' Logo: - type: "string" - description: "Icon associated to the extension" - example: "fa-database" + type: 'string' + description: 'Icon associated to the extension' + example: 'fa-database' Price: - type: "string" - description: "Extension price" - example: "US$9.95" + type: 'string' + description: 'Extension price' + example: 'US$9.95' PriceDescription: - type: "string" - description: "Details about extension pricing" - example: "Price per instance per year" + type: 'string' + description: 'Details about extension pricing' + example: 'Price per instance per year' ShopURL: - type: "string" - description: "URL used to buy the extension" - example: "https://portainer.io/checkout/?add-to-cart=1164" + type: 'string' + description: 'URL used to buy the extension' + example: 'https://portainer.io/checkout/?add-to-cart=1164' UpdateAvailable: - type: "boolean" - description: "Is an update available for this extension" - example: "true" + type: 'boolean' + description: 'Is an update available for this extension' + example: 'true' Version: - type: "string" - description: "Extension version" - example: "1.0.0" + type: 'string' + description: 'Extension version' + example: '1.0.0' License: - $ref: "#/definitions/LicenseInformation" + $ref: '#/definitions/LicenseInformation' ExtensionListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/Extension" + $ref: '#/definitions/Extension' ExtensionCreateRequest: - type: "object" + type: 'object' required: - - "License" + - 'License' properties: License: - type: "string" - example: "1-uKmVwboSWVIZv5URmE0VRkpbPX0rrCVeDxJl97LZ0piltw2SU28DSrNwPZAHCEAwB2SeKm6BCFcVwzGMBEixKQ" - description: "License key" + type: 'string' + example: '1-uKmVwboSWVIZv5URmE0VRkpbPX0rrCVeDxJl97LZ0piltw2SU28DSrNwPZAHCEAwB2SeKm6BCFcVwzGMBEixKQ' + description: 'License key' ExtensionUpdateRequest: - type: "object" + type: 'object' required: - - "Version" + - 'Version' properties: Version: - type: "string" - example: "1.1.0" - description: "New version of the extension" + type: 'string' + example: '1.1.0' + description: 'New version of the extension' RoleListResponse: - type: "array" + type: 'array' items: - $ref: "#/definitions/Role" + $ref: '#/definitions/Role' Role: - type: "object" + type: 'object' properties: Id: - type: "integer" - description: "Role identifier" + type: 'integer' + description: 'Role identifier' example: 2 Name: - type: "string" - description: "Role name" - example: "HelpDesk" + type: 'string' + description: 'Role name' + example: 'HelpDesk' Description: - type: "string" - description: "Role description" - example: "Read-only access of all resources in an endpoint" + type: 'string' + description: 'Role description' + example: 'Read-only access of all resources in an endpoint' Authorizations: - $ref: "#/definitions/Authorizations" + $ref: '#/definitions/Authorizations' Authorizations: - type: "object" - description: "Authorizations associated to a role" + type: 'object' + description: 'Authorizations associated to a role' additionalProperties: - type: "object" + type: 'object' properties: authorization: - type: "string" + type: 'string' value: - type: "boolean" + type: 'boolean' example: - "DockerContainerList": true - "DockerVolumeList": true + 'DockerContainerList': true + 'DockerVolumeList': true diff --git a/app/app.js b/app/app.js index 52ff750b4f0a2..672279747c9aa 100644 --- a/app/app.js +++ b/app/app.js @@ -30,9 +30,9 @@ angular.module('portainer').run([ HttpRequestHelper.resetAgentHeaders(); }); - $state.defaultErrorHandler(function () { - // Do not log transitionTo errors - }); + // $state.defaultErrorHandler(function () { + // // Do not log transitionTo errors + // }); // Keep-alive Edge endpoints by sending a ping request every minute $interval(function () { diff --git a/app/portainer/views/about/about.html b/app/portainer/views/about/about.html index ef609050af75b..e583327154524 100644 --- a/app/portainer/views/about/about.html +++ b/app/portainer/views/about/about.html @@ -29,20 +29,16 @@
Opt-out
    -
  • You may opt-out by passing the --no-analytics flag as part of the docker run command when starting Portainer.
  • -
  • If you believe that we could improve our analytics approach make sure to let us know! There is an open discussion on - Github
  • +
  • You may opt-out by turning off the analytics in the settings page.
What we collect & GDPR
    -
  • We dont know who uses Portainer, where its used, to what scale its used, all we know (from analytics) is how often Portainer is used and which pages within the app - are most frequently used.
  • +
  • + We don't know who uses Portainer, where its used, to what scale its used, all we know (from analytics) is how often Portainer is used and which pages within the app + are most frequently used. +
  • As we are only collecting a very small amount of totally anonymous data, it is deemed that opt-in is not required.
diff --git a/gruntfile.js b/gruntfile.js index 919fc6d9339c9..d99563bdfbf1e 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -177,12 +177,12 @@ function shell_run_container() { 'docker rm -f portainer', 'docker run -d -p 8000:8000 -p 9000:9000 -v $(pwd)/dist:/app -v ' + portainer_data + - ':/data -v /var/run/docker.sock:/var/run/docker.sock:z --name portainer portainer/base /app/portainer --no-analytics', + ':/data -v /var/run/docker.sock:/var/run/docker.sock:z --name portainer portainer/base /app/portainer', ].join(';'); } function shell_run_localserver() { - return './dist/portainer --no-analytics'; + return './dist/portainer'; } function shell_install_yarndeps() { diff --git a/yarn.lock b/yarn.lock index 79eb4e074913d..1abaa00eea2b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1297,14 +1297,7 @@ angularjs-slider@^6.4.0: resolved "https://registry.yarnpkg.com/angularjs-slider/-/angularjs-slider-6.7.0.tgz#eb2229311b81b79315a36e7b5eb700e128f50319" integrity sha512-Cizsuax65wN2Y+htmA3safE5ALOSCyWcKyWkziaO8vCVymi26bQQs6kKDhkYc8GFix/KE7Oc9gH3QLlTUgD38w== -angulartics-piwik@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/angulartics-piwik/-/angulartics-piwik-1.0.6.tgz#cf050b65e8974f3f9ae9a2a1cef4bc1cac82099f" - integrity sha1-zwULZeiXTz+a6aKhzvS8HKyCCZ8= - dependencies: - angulartics "*" - -angulartics@*, angulartics@^1.6.0: +angulartics@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/angulartics/-/angulartics-1.6.0.tgz#a89c17ef8ea2334ebced65d6265951846f848172" integrity sha512-fywhCi1InawcX+rpLv9NQ32Ed87KoZeH20SUIsRUz9dYJSxuk4/uxiKiopITveGxTC8THYHFEATj9Y/X+BvMqA== From 33556ad1fbfa1fdf696484ea4388f687de925d2e Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 12:09:35 +0300 Subject: [PATCH 11/22] feat(settings): introduce a setting to enable telemetry --- api/bolt/migrator/migrate_dbversion24.go | 1 + api/cmd/portainer/main.go | 1 + api/http/handler/settings/settings_public.go | 2 ++ api/http/handler/settings/settings_update.go | 5 +++++ api/portainer.go | 1 + 5 files changed, 10 insertions(+) diff --git a/api/bolt/migrator/migrate_dbversion24.go b/api/bolt/migrator/migrate_dbversion24.go index 4749607c5ae09..b4843f2ff4414 100644 --- a/api/bolt/migrator/migrate_dbversion24.go +++ b/api/bolt/migrator/migrate_dbversion24.go @@ -15,6 +15,7 @@ func (m *Migrator) updateSettingsToDB25() error { } legacySettings.UserSessionTimeout = portainer.DefaultUserSessionTimeout + legacySettings.EnableTelemetry = true legacySettings.AllowContainerCapabilitiesForRegularUsers = true diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index 4064df9230ab0..92fcdb1eda42a 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -167,6 +167,7 @@ func updateSettingsFromFlags(dataStore portainer.DataStore, flags *portainer.CLI settings.LogoURL = *flags.Logo settings.SnapshotInterval = *flags.SnapshotInterval settings.EnableEdgeComputeFeatures = *flags.EnableEdgeComputeFeatures + settings.EnableTelemetry = true if *flags.Templates != "" { settings.TemplatesURL = *flags.Templates diff --git a/api/http/handler/settings/settings_public.go b/api/http/handler/settings/settings_public.go index f0d4f422a66bf..e94f501e07c88 100644 --- a/api/http/handler/settings/settings_public.go +++ b/api/http/handler/settings/settings_public.go @@ -22,6 +22,7 @@ type publicSettingsResponse struct { EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"` EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"` OAuthLoginURI string `json:"OAuthLoginURI"` + EnableTelemetry bool `json:"EnableTelemetry"` } // GET request on /api/settings/public @@ -43,6 +44,7 @@ func (handler *Handler) settingsPublic(w http.ResponseWriter, r *http.Request) * AllowContainerCapabilitiesForRegularUsers: settings.AllowContainerCapabilitiesForRegularUsers, EnableHostManagementFeatures: settings.EnableHostManagementFeatures, EnableEdgeComputeFeatures: settings.EnableEdgeComputeFeatures, + EnableTelemetry: settings.EnableTelemetry, OAuthLoginURI: fmt.Sprintf("%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s&prompt=login", settings.OAuthSettings.AuthorizationURI, settings.OAuthSettings.ClientID, diff --git a/api/http/handler/settings/settings_update.go b/api/http/handler/settings/settings_update.go index 7b2a74f0ec639..5ab7c351375bd 100644 --- a/api/http/handler/settings/settings_update.go +++ b/api/http/handler/settings/settings_update.go @@ -33,6 +33,7 @@ type settingsUpdatePayload struct { EdgeAgentCheckinInterval *int EnableEdgeComputeFeatures *bool UserSessionTimeout *string + EnableTelemetry *bool } func (payload *settingsUpdatePayload) Validate(r *http.Request) error { @@ -164,6 +165,10 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) * settings.AllowDeviceMappingForRegularUsers = *payload.AllowDeviceMappingForRegularUsers } + if payload.EnableTelemetry != nil { + settings.EnableTelemetry = *payload.EnableTelemetry + } + tlsError := handler.updateTLS(settings) if tlsError != nil { return tlsError diff --git a/api/portainer.go b/api/portainer.go index bf3efd5340329..06f1339577374 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -532,6 +532,7 @@ type ( EdgeAgentCheckinInterval int `json:"EdgeAgentCheckinInterval"` EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"` UserSessionTimeout string `json:"UserSessionTimeout"` + EnableTelemetry bool `json:"EnableTelemetry"` // Deprecated fields DisplayDonationHeader bool From ae82d1922f8d163df0b26a4fe3eb072007e3badb Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 12:23:09 +0300 Subject: [PATCH 12/22] fix(cli): fix typo --- api/cli/cli.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/cli/cli.go b/api/cli/cli.go index afc0656bc0984..1d678baf87128 100644 --- a/api/cli/cli.go +++ b/api/cli/cli.go @@ -36,7 +36,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) { Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(), EndpointURL: kingpin.Flag("host", "Endpoint URL").Short('H').String(), EnableEdgeComputeFeatures: kingpin.Flag("edge-compute", "Enable Edge Compute features").Bool(), - NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app (depreciated)").Default(defaultNoAnalytics).Bool(), + NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app (deprecated)").Default(defaultNoAnalytics).Bool(), TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(), TLSSkipVerify: kingpin.Flag("tlsskipverify", "Disable TLS server verification").Default(defaultTLSSkipVerify).Bool(), TLSCacert: kingpin.Flag("tlscacert", "Path to the CA").Default(defaultTLSCACertPath).String(), @@ -90,7 +90,7 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error { func displayDeprecationWarnings(flags *portainer.CLIFlags) { if flags.NoAnalytics != nil { - log.Println("Warning: The --no-analytics has been depreciated and will be removed in a future version of Portainer. It has currently no effect, telemetry settings are available in the Portainer settings.") + log.Println("Warning: The --no-analytics has been deprecated and will be removed in a future version of Portainer. It has currently no effect, telemetry settings are available in the Portainer settings.") } } From 05efa21e3c703f50b14d2e942aa4f474d6b03620 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 12:30:13 +0300 Subject: [PATCH 13/22] feat(settings): allow toggle telemetry from settings --- app/portainer/__module.js | 7 +++++-- app/portainer/models/settings.js | 2 ++ app/portainer/services/stateManager.js | 21 ++++++++++++++++++- app/portainer/views/settings/settings.html | 8 +++++++ .../views/settings/settingsController.js | 4 ++++ 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/app/portainer/__module.js b/app/portainer/__module.js index b312bcd408cbe..29d9ca7661d35 100644 --- a/app/portainer/__module.js +++ b/app/portainer/__module.js @@ -33,14 +33,17 @@ angular.module('portainer.app', ['portainer.oauth']).config([ '$state', '$async', '$q', - (StateManager, Authentication, Notifications, authManager, $rootScope, $state, $async, $q) => { + '$analytics', + (StateManager, Authentication, Notifications, authManager, $rootScope, $state, $async, $q, $analytics) => { const deferred = $q.defer(); const appState = StateManager.getState(); if (!appState.loading) { deferred.resolve(); } else { StateManager.initialize() - .then(function success() { + .then(function success(state) { + $analytics.setOptOut(!state.application.enableTelemetry); + return $async(initAuthentication, authManager, Authentication, $rootScope, $state); }) .then(() => deferred.resolve()) diff --git a/app/portainer/models/settings.js b/app/portainer/models/settings.js index bec5e789428de..40f0003f40472 100644 --- a/app/portainer/models/settings.js +++ b/app/portainer/models/settings.js @@ -17,6 +17,7 @@ export function SettingsViewModel(data) { this.EdgeAgentCheckinInterval = data.EdgeAgentCheckinInterval; this.EnableEdgeComputeFeatures = data.EnableEdgeComputeFeatures; this.UserSessionTimeout = data.UserSessionTimeout; + this.EnableTelemetry = data.EnableTelemetry; } export function PublicSettingsViewModel(settings) { @@ -32,6 +33,7 @@ export function PublicSettingsViewModel(settings) { this.EnableEdgeComputeFeatures = settings.EnableEdgeComputeFeatures; this.LogoURL = settings.LogoURL; this.OAuthLoginURI = settings.OAuthLoginURI; + this.EnableTelemetry = settings.EnableTelemetry; } export function LDAPSettingsViewModel(data) { diff --git a/app/portainer/services/stateManager.js b/app/portainer/services/stateManager.js index 44128c98c95bc..4f4dd623a4281 100644 --- a/app/portainer/services/stateManager.js +++ b/app/portainer/services/stateManager.js @@ -11,7 +11,19 @@ angular.module('portainer.app').factory('StateManager', [ 'StatusService', 'APPLICATION_CACHE_VALIDITY', 'AgentPingService', - function StateManagerFactory($q, SystemService, InfoHelper, EndpointProvider, LocalStorage, SettingsService, StatusService, APPLICATION_CACHE_VALIDITY, AgentPingService) { + '$analytics', + function StateManagerFactory( + $q, + SystemService, + InfoHelper, + EndpointProvider, + LocalStorage, + SettingsService, + StatusService, + APPLICATION_CACHE_VALIDITY, + AgentPingService, + $analytics + ) { 'use strict'; var manager = {}; @@ -106,8 +118,15 @@ angular.module('portainer.app').factory('StateManager', [ LocalStorage.storeApplicationState(state.application); }; + manager.updateEnableTelemetry = function updateEnableTelemetry(enableTelemetry) { + state.application.enableTelemetry = enableTelemetry; + $analytics.setOptOut(!enableTelemetry); + LocalStorage.storeApplicationState(state.application); + }; + function assignStateFromStatusAndSettings(status, settings) { state.application.version = status.Version; + state.application.enableTelemetry = settings.EnableTelemetry; state.application.logo = settings.LogoURL; state.application.snapshotInterval = settings.SnapshotInterval; state.application.enableHostManagementFeatures = settings.EnableHostManagementFeatures; diff --git a/app/portainer/views/settings/settings.html b/app/portainer/views/settings/settings.html index b9e3c97cfa10e..397bf11a986a5 100644 --- a/app/portainer/views/settings/settings.html +++ b/app/portainer/views/settings/settings.html @@ -26,6 +26,14 @@ +
+
+ + +
+
diff --git a/app/portainer/views/settings/settingsController.js b/app/portainer/views/settings/settingsController.js index 09e6f862a8676..5bd78d9f09adc 100644 --- a/app/portainer/views/settings/settingsController.js +++ b/app/portainer/views/settings/settingsController.js @@ -36,6 +36,7 @@ angular.module('portainer.app').controller('SettingsController', [ allowDeviceMappingForRegularUsers: false, allowStackManagementForRegularUsers: false, disableContainerCapabilitiesForRegularUsers: false, + enableTelemetry: false, }; $scope.isContainerEditDisabled = function isContainerEditDisabled() { @@ -85,6 +86,7 @@ angular.module('portainer.app').controller('SettingsController', [ settings.AllowDeviceMappingForRegularUsers = !$scope.formValues.disableDeviceMappingForRegularUsers; settings.AllowStackManagementForRegularUsers = !$scope.formValues.disableStackManagementForRegularUsers; settings.AllowContainerCapabilitiesForRegularUsers = !$scope.formValues.disableContainerCapabilitiesForRegularUsers; + settings.EnableTelemetry = $scope.formValues.enableTelemetry; $scope.state.actionInProgress = true; updateSettings(settings); @@ -105,6 +107,7 @@ angular.module('portainer.app').controller('SettingsController', [ StateManager.updateAllowContainerCapabilitiesForRegularUsers(settings.AllowContainerCapabilitiesForRegularUsers); StateManager.updateAllowPrivilegedModeForRegularUsers(settings.AllowPrivilegedModeForRegularUsers); StateManager.updateAllowBindMountsForRegularUsers(settings.AllowBindMountsForRegularUsers); + StateManager.updateEnableTelemetry(settings.EnableTelemetry); $state.reload(); }) .catch(function error(err) { @@ -133,6 +136,7 @@ angular.module('portainer.app').controller('SettingsController', [ $scope.formValues.disableDeviceMappingForRegularUsers = !settings.AllowDeviceMappingForRegularUsers; $scope.formValues.disableStackManagementForRegularUsers = !settings.AllowStackManagementForRegularUsers; $scope.formValues.disableContainerCapabilitiesForRegularUsers = !settings.AllowContainerCapabilitiesForRegularUsers; + $scope.formValues.enableTelemetry = settings.EnableTelemetry; }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to retrieve application settings'); From 330391e6d6f65916e6937f12c59faf55c6dbcb8b Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 12:50:29 +0300 Subject: [PATCH 14/22] fix(settings): handle case where AuthenticationMethod is missing --- api/http/handler/settings/settings_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/http/handler/settings/settings_update.go b/api/http/handler/settings/settings_update.go index 5ab7c351375bd..5d1aded0e0272 100644 --- a/api/http/handler/settings/settings_update.go +++ b/api/http/handler/settings/settings_update.go @@ -37,7 +37,7 @@ type settingsUpdatePayload struct { } func (payload *settingsUpdatePayload) Validate(r *http.Request) error { - if *payload.AuthenticationMethod != 1 && *payload.AuthenticationMethod != 2 && *payload.AuthenticationMethod != 3 { + if payload.AuthenticationMethod != nil && *payload.AuthenticationMethod != 1 && *payload.AuthenticationMethod != 2 && *payload.AuthenticationMethod != 3 { return errors.New("Invalid authentication method value. Value must be one of: 1 (internal), 2 (LDAP/AD) or 3 (OAuth)") } if payload.LogoURL != nil && *payload.LogoURL != "" && !govalidator.IsURL(*payload.LogoURL) { From 31e7fa84f972a2a7452a73038105c535e01899ea Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 12:50:46 +0300 Subject: [PATCH 15/22] feat(admin): set telemetry on admin init --- app/portainer/views/init/admin/initAdmin.html | 10 ++++++++++ app/portainer/views/init/admin/initAdminController.js | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/portainer/views/init/admin/initAdmin.html b/app/portainer/views/init/admin/initAdmin.html index aa5a5129d4ff1..8dcc46103324a 100644 --- a/app/portainer/views/init/admin/initAdmin.html +++ b/app/portainer/views/init/admin/initAdmin.html @@ -56,6 +56,16 @@
+ +
+
+ + +
+
+
diff --git a/app/portainer/views/init/admin/initAdminController.js b/app/portainer/views/init/admin/initAdminController.js index 3bc8f1c9fa841..1f78260193980 100644 --- a/app/portainer/views/init/admin/initAdminController.js +++ b/app/portainer/views/init/admin/initAdminController.js @@ -5,16 +5,18 @@ angular.module('portainer.app').controller('InitAdminController', [ 'Notifications', 'Authentication', 'StateManager', + 'SettingsService', 'UserService', 'EndpointService', 'ExtensionService', - function ($async, $scope, $state, Notifications, Authentication, StateManager, UserService, EndpointService, ExtensionService) { + function ($async, $scope, $state, Notifications, Authentication, StateManager, SettingsService, UserService, EndpointService, ExtensionService) { $scope.logo = StateManager.getState().application.logo; $scope.formValues = { Username: 'admin', Password: '', ConfirmPassword: '', + enableTelemetry: true, }; $scope.state = { @@ -45,6 +47,10 @@ angular.module('portainer.app').controller('InitAdminController', [ .then(function success() { return retrieveAndSaveEnabledExtensions(); }) + .then(function success() { + StateManager.updateEnableTelemetry($scope.formValues.enableTelemetry); + return SettingsService.update({ enableTelemetry: $scope.formValues.enableTelemetry }); + }) .then(function () { return EndpointService.endpoints(0, 100); }) From 4ef559b9e91227bec0c36fc852ed4814ffe8d26e Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 12:52:34 +0300 Subject: [PATCH 16/22] refactor(app); revert file --- app/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/app.js b/app/app.js index 672279747c9aa..52ff750b4f0a2 100644 --- a/app/app.js +++ b/app/app.js @@ -30,9 +30,9 @@ angular.module('portainer').run([ HttpRequestHelper.resetAgentHeaders(); }); - // $state.defaultErrorHandler(function () { - // // Do not log transitionTo errors - // }); + $state.defaultErrorHandler(function () { + // Do not log transitionTo errors + }); // Keep-alive Edge endpoints by sending a ping request every minute $interval(function () { From 70d815450bfa1b4dec53206b9fc685531a5f4833 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 12:57:27 +0300 Subject: [PATCH 17/22] refactor(state-manager): move optout to state manager --- app/portainer/__module.js | 7 ++----- app/portainer/services/stateManager.js | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/portainer/__module.js b/app/portainer/__module.js index 29d9ca7661d35..b312bcd408cbe 100644 --- a/app/portainer/__module.js +++ b/app/portainer/__module.js @@ -33,17 +33,14 @@ angular.module('portainer.app', ['portainer.oauth']).config([ '$state', '$async', '$q', - '$analytics', - (StateManager, Authentication, Notifications, authManager, $rootScope, $state, $async, $q, $analytics) => { + (StateManager, Authentication, Notifications, authManager, $rootScope, $state, $async, $q) => { const deferred = $q.defer(); const appState = StateManager.getState(); if (!appState.loading) { deferred.resolve(); } else { StateManager.initialize() - .then(function success(state) { - $analytics.setOptOut(!state.application.enableTelemetry); - + .then(function success() { return $async(initAuthentication, authManager, Authentication, $rootScope, $state); }) .then(() => deferred.resolve()) diff --git a/app/portainer/services/stateManager.js b/app/portainer/services/stateManager.js index 4f4dd623a4281..600ee21a51f7f 100644 --- a/app/portainer/services/stateManager.js +++ b/app/portainer/services/stateManager.js @@ -152,6 +152,7 @@ angular.module('portainer.app').factory('StateManager', [ var status = data.status; var settings = data.settings; assignStateFromStatusAndSettings(status, settings); + $analytics.setOptOut(!settings.EnableTelemetry); LocalStorage.storeApplicationState(state.application); deferred.resolve(state); }) @@ -194,6 +195,7 @@ angular.module('portainer.app').factory('StateManager', [ } else { state.application = applicationState; state.loading = false; + $analytics.setOptOut(!state.application.enableTelemetry); deferred.resolve(state); } } else { From bcbcb3308a8b9824a5d11e2e429fd932bc27bf40 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sat, 1 Aug 2020 13:20:58 +0300 Subject: [PATCH 18/22] feat(telemetry): set matomo url --- app/matomo-setup.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/matomo-setup.js b/app/matomo-setup.js index a580503fe709d..b85aa4480535e 100644 --- a/app/matomo-setup.js +++ b/app/matomo-setup.js @@ -2,13 +2,13 @@ const _paq = (window._paq = window._paq || []); /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(['enableLinkTracking']); -const u = '//188.166.250.54/'; +var u = 'https://portainer-ce.matomo.cloud/'; _paq.push(['setTrackerUrl', u + 'matomo.php']); _paq.push(['setSiteId', '1']); -const d = document, +var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0]; g.type = 'text/javascript'; g.async = true; -g.src = u + 'matomo.js'; +g.src = '//cdn.matomo.cloud/portainer-ce.matomo.cloud/matomo.js'; s.parentNode.insertBefore(g, s); From 8c1b4530009f402c71f965a905bb5fdcc592370e Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Tue, 4 Aug 2020 10:33:30 +1200 Subject: [PATCH 19/22] feat(core/settings): minor UI update --- api/cli/cli.go | 2 +- app/portainer/views/init/admin/initAdmin.html | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/api/cli/cli.go b/api/cli/cli.go index 1d678baf87128..1d3aece2adf2d 100644 --- a/api/cli/cli.go +++ b/api/cli/cli.go @@ -90,7 +90,7 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error { func displayDeprecationWarnings(flags *portainer.CLIFlags) { if flags.NoAnalytics != nil { - log.Println("Warning: The --no-analytics has been deprecated and will be removed in a future version of Portainer. It has currently no effect, telemetry settings are available in the Portainer settings.") + log.Println("Warning: The --no-analytics has been deprecated and will be removed in a future version of Portainer. It currently has no effect, telemetry settings are available in the Portainer settings.") } } diff --git a/app/portainer/views/init/admin/initAdmin.html b/app/portainer/views/init/admin/initAdmin.html index 8dcc46103324a..cc86c0759188d 100644 --- a/app/portainer/views/init/admin/initAdmin.html +++ b/app/portainer/views/init/admin/initAdmin.html @@ -56,16 +56,6 @@
- -
-
- - -
-
-
@@ -92,6 +82,14 @@
+ +
+
+ + Allow collection of anonymous statistics +
+
+ From e32f1b02db78e780c54817a6f47ace0e01ff6a58 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Tue, 4 Aug 2020 10:36:30 +1200 Subject: [PATCH 20/22] feat(core/telemetry): update custom URL --- app/assets/js/angulartics-matomo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/js/angulartics-matomo.js b/app/assets/js/angulartics-matomo.js index ad25bbac73496..1b6211ca8f37b 100644 --- a/app/assets/js/angulartics-matomo.js +++ b/app/assets/js/angulartics-matomo.js @@ -94,7 +94,7 @@ angular.module('angulartics.matomo', ['angulartics']).config([ if ($window._paq) { $window._paq.push(['setDocumentTitle', $window.document.title]); $window._paq.push(['setReferrerUrl', '']); - $window._paq.push(['setCustomUrl', 'http://portainer.app' + path]); + $window._paq.push(['setCustomUrl', 'http://portainer-ce.app' + path]); $window._paq.push(['trackPageView']); } }); From ac0ffcd9ff56ca398c863a309272434a1d826fa3 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Wed, 5 Aug 2020 15:45:03 +1200 Subject: [PATCH 21/22] feat(core/telemetry): add placeholder for privacy policy --- app/portainer/views/init/admin/initAdmin.html | 4 +++- app/portainer/views/settings/settings.html | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/portainer/views/init/admin/initAdmin.html b/app/portainer/views/init/admin/initAdmin.html index cc86c0759188d..90e55d70c1fa5 100644 --- a/app/portainer/views/init/admin/initAdmin.html +++ b/app/portainer/views/init/admin/initAdmin.html @@ -86,7 +86,9 @@
- Allow collection of anonymous statistics + Allow collection of anonymous statistics. You can find more information about this in our privacy policy.
diff --git a/app/portainer/views/settings/settings.html b/app/portainer/views/settings/settings.html index 397bf11a986a5..1551fa560f9fa 100644 --- a/app/portainer/views/settings/settings.html +++ b/app/portainer/views/settings/settings.html @@ -33,6 +33,9 @@ +
+ You can find more information about this in our privacy policy. +
From 6941b5ead94f33478e552eeb59e3ed38cb0fcb7d Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Thu, 6 Aug 2020 11:27:40 +1200 Subject: [PATCH 22/22] feat(core/telemetry): add privacy policy link --- app/portainer/views/init/admin/initAdmin.html | 3 ++- app/portainer/views/settings/settings.html | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/portainer/views/init/admin/initAdmin.html b/app/portainer/views/init/admin/initAdmin.html index 90e55d70c1fa5..b3643ea583f76 100644 --- a/app/portainer/views/init/admin/initAdmin.html +++ b/app/portainer/views/init/admin/initAdmin.html @@ -87,7 +87,8 @@
Allow collection of anonymous statistics. You can find more information about this in our privacy policy.Allow collection of anonymous statistics. You can find more information about this in our + privacy policy.
diff --git a/app/portainer/views/settings/settings.html b/app/portainer/views/settings/settings.html index 1551fa560f9fa..d5091ef431c19 100644 --- a/app/portainer/views/settings/settings.html +++ b/app/portainer/views/settings/settings.html @@ -34,7 +34,8 @@
- You can find more information about this in our privacy policy. + You can find more information about this in our + privacy policy.