Compare commits
5 Commits
refactor/E
...
refactor/E
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2e59f3b4e | ||
|
|
31047a5b3d | ||
|
|
4623f27bff | ||
|
|
8f322e3c17 | ||
|
|
4a9236967b |
@@ -0,0 +1,88 @@
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
/* @ngInject */
|
||||
export default function ApplicationSettingsPanelController($scope, $async, StateManager) {
|
||||
this.saveApplicationSettings = saveApplicationSettings.bind(this);
|
||||
this.onChangeCheckInInterval = onChangeCheckInInterval.bind(this);
|
||||
this.onToggleCustomLogo = onToggleCustomLogo.bind(this);
|
||||
this.$onInit = $onInit.bind(this);
|
||||
this.onToggleEnableTelemetry = onToggleEnableTelemetry.bind(this);
|
||||
this.onToggleCustomLoginBanner = onToggleCustomLoginBanner.bind(this);
|
||||
this.onChangeFormValues = onChangeFormValues.bind(this);
|
||||
|
||||
this.customBannerFeatureId = FeatureId.CUSTOM_LOGIN_BANNER;
|
||||
|
||||
this.formValues = {
|
||||
logoURL: '',
|
||||
logoEnabled: false,
|
||||
customLoginBannerEnabled: false,
|
||||
customLoginBanner: '',
|
||||
snapshotInterval: '',
|
||||
enableTelemetry: false,
|
||||
templatesUrl: '',
|
||||
edgeAgentCheckinInterval: '',
|
||||
};
|
||||
|
||||
this.state = {
|
||||
isDemo: false,
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
async function saveApplicationSettings() {
|
||||
$async(async () => {
|
||||
const appSettingsPayload = {
|
||||
SnapshotInterval: this.settings.SnapshotInterval,
|
||||
LogoURL: this.formValues.customLogo ? this.settings.LogoURL : '',
|
||||
EnableTelemetry: this.settings.EnableTelemetry,
|
||||
CustomLoginBanner: this.formValues.customLoginBanner ? this.settings.CustomLoginBanner : '',
|
||||
TemplatesURL: this.settings.TemplatesURL,
|
||||
EdgeAgentCheckinInterval: this.settings.EdgeAgentCheckinInterval,
|
||||
};
|
||||
|
||||
this.state.actionInProgress = true;
|
||||
await this.onSubmit(appSettingsPayload, 'Application settings updated');
|
||||
this.state.actionInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onToggleCustomLogo(logoEnabled) {
|
||||
this.onChangeFormValues({ logoEnabled });
|
||||
}
|
||||
|
||||
function onChangeCheckInInterval(edgeAgentCheckinInterval) {
|
||||
this.onChangeFormValues({ edgeAgentCheckinInterval });
|
||||
}
|
||||
|
||||
function onToggleEnableTelemetry(enableTelemetry) {
|
||||
this.onChangeFormValues({ enableTelemetry });
|
||||
}
|
||||
|
||||
function onToggleCustomLoginBanner(customLoginBannerEnabled) {
|
||||
this.onChangeFormValues({ customLoginBannerEnabled });
|
||||
}
|
||||
|
||||
function onChangeFormValues(newPartialValues) {
|
||||
$scope.$evalAsync(() => {
|
||||
this.formValues = {
|
||||
...this.formValues,
|
||||
...newPartialValues,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function $onInit() {
|
||||
const state = StateManager.getState();
|
||||
this.state.isDemo = state.application.demoEnvironment.enabled;
|
||||
|
||||
this.formValues = {
|
||||
logoURL: this.settings.LogoURL,
|
||||
logoEnabled: !!this.settings.LogoURL,
|
||||
customLoginBannerEnabled: !!this.settings.CustomLoginBanner,
|
||||
customLoginBanner: this.settings.CustomLoginBanner,
|
||||
snapshotInterval: this.settings.SnapshotInterval,
|
||||
enableTelemetry: this.settings.EnableTelemetry,
|
||||
templatesUrl: this.settings.TemplatesURL,
|
||||
edgeAgentCheckinInterval: this.settings.EdgeAgentCheckinInterval,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="settings" title-text="Application settings"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- snapshot-interval -->
|
||||
<div class="form-group">
|
||||
<label for="snapshot_interval" class="col-sm-2 control-label text-left">Snapshot interval</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="$ctrl.formValues.snapshotInterval" id="snapshot_interval" placeholder="e.g. 15m" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !snapshot-interval -->
|
||||
|
||||
<!-- checkin-interval -->
|
||||
<edge-checkin-interval-field
|
||||
size="'xsmall'"
|
||||
value="$ctrl.formValues.edgeAgentCheckinInterval"
|
||||
label="'Edge agent default poll frequency'"
|
||||
is-default-hidden="true"
|
||||
on-change="($ctrl.onChangeCheckInInterval)"
|
||||
></edge-checkin-interval-field>
|
||||
<!-- !checkin-interval -->
|
||||
|
||||
<!-- logo -->
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Use custom logo'"
|
||||
checked="$ctrl.formValues.logoEnabled"
|
||||
name="'toggle_logo'"
|
||||
disabled="$ctrl.state.isDemo"
|
||||
on-change="($ctrl.onToggleCustomLogo)"
|
||||
field-class="'col-sm-12'"
|
||||
label-class="'col-sm-2'"
|
||||
></por-switch-field>
|
||||
<div class="col-sm-12" ng-if="$ctrl.state.isDemo" style="margin-top: 10px">
|
||||
<span class="small text-muted">You cannot use this feature in the demo version of Portainer.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="$ctrl.formValues.logoEnabled">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small"> You can specify the URL to your logo here. For an optimal display, logo dimensions should be 155px by 55px. </span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="logo_url" class="col-sm-2 control-label text-left"> URL </label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="$ctrl.formValues.logoUrl" id="logo_url" placeholder="https://mycompany.com/logo.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !logo -->
|
||||
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Allow the collection of anonymous statistics'"
|
||||
checked="$ctrl.formValues.enableTelemetry"
|
||||
name="'toggle_enableTelemetry'"
|
||||
on-change="($ctrl.onToggleEnableTelemetry)"
|
||||
disabled="$ctrl.state.isDemo"
|
||||
field-class="'col-sm-12'"
|
||||
label-class="'col-sm-2'"
|
||||
></por-switch-field>
|
||||
<div class="col-sm-12" ng-if="$ctrl.state.isDemo" style="margin-top: 10px">
|
||||
<span class="small text-muted">You cannot use this feature in the demo version of Portainer.</span>
|
||||
</div>
|
||||
<div class="col-sm-12 text-muted small" style="margin-top: 10px">
|
||||
You can find more information about this in our
|
||||
<a href="https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/" target="_blank">privacy policy</a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- login screen banner -->
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Login screen banner'"
|
||||
checked="$ctrl.formValues.customLoginBannerEnabled"
|
||||
name="'toggle_login_banner'"
|
||||
disabled="$ctrl.state.isDemo"
|
||||
feature-id="$ctrl.customBannerFeatureId"
|
||||
on-change="($ctrl.onToggleCustomLoginBanner)"
|
||||
field-class="'col-sm-12'"
|
||||
label-class="'col-sm-2'"
|
||||
></por-switch-field>
|
||||
<div class="col-sm-12" ng-if="$ctrl.state.isDemo" style="margin-top: 10px">
|
||||
<span class="small text-muted">You cannot use this feature in the demo version of Portainer.</span>
|
||||
</div>
|
||||
<div class="col-sm-12 text-muted small" style="margin-top: 10px"> You can set a custom banner that will be shown to all users during login. </div>
|
||||
</div>
|
||||
|
||||
<div ng-if="$ctrl.formValues.customLoginBannerEnabled">
|
||||
<div class="form-group">
|
||||
<label for="custom_login_banner" class="col-sm-2 control-label required text-left">Details</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" rows="5" ng-model="$ctrl.formValues.customLoginBanner" id="custom_login_banner" placeholder="Banner details"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !login screen banner -->
|
||||
|
||||
<!-- templates -->
|
||||
<div class="col-sm-12 form-section-title"> App Templates </div>
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can specify the URL to your own template definitions file here. See
|
||||
<a href="https://docs.portainer.io/advanced/app-templates/build" target="_blank">Portainer documentation</a> for more details.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="templates_url" class="col-sm-2 control-label text-left"> URL </label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.templatesUrl"
|
||||
id="templates_url"
|
||||
placeholder="https://myserver.mydomain/templates.json"
|
||||
required
|
||||
data-cy="settings-templateUrl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !templates -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-click="$ctrl.saveApplicationSettings()"
|
||||
ng-disabled="$ctrl.state.actionInProgress || !$ctrl.formValues.templatesUrl || ($ctrl.formValues.customLoginBannerEnabled && !$ctrl.formValues.customLoginBanner)"
|
||||
button-spinner="$ctrl.state.actionInProgress"
|
||||
data-cy="settings-saveSettingsButton"
|
||||
>
|
||||
<span ng-hide="$ctrl.state.actionInProgress">Save application settings</span>
|
||||
<span ng-show="$ctrl.state.actionInProgress">Saving...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import controller from './application-settings-panel.controller';
|
||||
|
||||
angular.module('portainer.app').component('applicationSettingsPanel', {
|
||||
templateUrl: './application-settings-panel.html',
|
||||
controller,
|
||||
bindings: {
|
||||
settings: '<',
|
||||
onSubmit: '<',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { options } from '@/react/portainer/settings/SettingsView/backup-options';
|
||||
|
||||
/* @ngInject */
|
||||
export default function BackupSettingsPanelController($scope, $async, BackupService, Notifications, FileSaver) {
|
||||
this.$onInit = $onInit.bind(this);
|
||||
this.onBackupOptionsChange = onBackupOptionsChange.bind(this);
|
||||
this.onToggleAutoBackups = onToggleAutoBackups.bind(this);
|
||||
this.getS3SettingsPayload = getS3SettingsPayload.bind(this);
|
||||
|
||||
this.downloadBackup = downloadBackup.bind(this);
|
||||
this.saveS3BackupSettings = saveS3BackupSettings.bind(this);
|
||||
this.exportBackup = exportBackup.bind(this);
|
||||
|
||||
this.backupOptions = options;
|
||||
this.s3BackupFeatureId = FeatureId.S3_BACKUP_SETTING;
|
||||
this.BACKUP_FORM_TYPES = { S3: 's3', FILE: 'file' };
|
||||
|
||||
this.formValues = {
|
||||
passwordProtect: false,
|
||||
password: '',
|
||||
scheduleAutomaticBackups: true,
|
||||
cronRule: '',
|
||||
accessKeyId: '',
|
||||
secretAccessKey: '',
|
||||
region: '',
|
||||
bucketName: '',
|
||||
s3CompatibleHost: '',
|
||||
backupFormType: this.BACKUP_FORM_TYPES.FILE,
|
||||
};
|
||||
|
||||
this.state = {
|
||||
backupInProgress: false,
|
||||
featureLimited: false,
|
||||
};
|
||||
|
||||
function onToggleAutoBackups(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
this.formValues.scheduleAutomaticBackups = checked;
|
||||
});
|
||||
}
|
||||
|
||||
function onBackupOptionsChange(type, limited) {
|
||||
this.formValues.backupFormType = type;
|
||||
this.state.featureLimited = limited;
|
||||
}
|
||||
|
||||
function downloadBackup() {
|
||||
const payload = {};
|
||||
if (this.formValues.passwordProtect) {
|
||||
payload.password = this.formValues.password;
|
||||
}
|
||||
|
||||
this.state.backupInProgress = true;
|
||||
|
||||
BackupService.downloadBackup(payload)
|
||||
.then((data) => {
|
||||
const downloadData = new Blob([data.file], { type: 'application/gzip' });
|
||||
FileSaver.saveAs(downloadData, data.name);
|
||||
Notifications.success('Success', 'Backup successfully downloaded');
|
||||
})
|
||||
.catch((err) => {
|
||||
Notifications.error('Failure', err, 'Unable to download backup');
|
||||
})
|
||||
.finally(() => {
|
||||
this.state.backupInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function saveS3BackupSettings() {
|
||||
const payload = this.getS3SettingsPayload();
|
||||
BackupService.saveS3Settings(payload)
|
||||
.then(() => {
|
||||
Notifications.success('Success', 'S3 Backup settings successfully saved');
|
||||
})
|
||||
.catch((err) => {
|
||||
Notifications.error('Failure', err, 'Unable to save S3 backup settings');
|
||||
})
|
||||
.finally(() => {
|
||||
this.state.backupInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function exportBackup() {
|
||||
const payload = this.getS3SettingsPayload();
|
||||
BackupService.exportBackup(payload)
|
||||
.then(() => {
|
||||
Notifications.success('Success', 'Exported backup to S3 successfully');
|
||||
})
|
||||
.catch((err) => {
|
||||
Notifications.error('Failure', err, 'Unable to export backup to S3');
|
||||
})
|
||||
.finally(() => {
|
||||
this.state.backupInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function getS3SettingsPayload() {
|
||||
return {
|
||||
Password: this.formValues.passwordProtectS3 ? this.formValues.passwordS3 : '',
|
||||
CronRule: this.formValues.scheduleAutomaticBackups ? this.formValues.cronRule : '',
|
||||
AccessKeyID: this.formValues.accessKeyId,
|
||||
SecretAccessKey: this.formValues.secretAccessKey,
|
||||
Region: this.formValues.region,
|
||||
BucketName: this.formValues.bucketName,
|
||||
S3CompatibleHost: this.formValues.s3CompatibleHost,
|
||||
};
|
||||
}
|
||||
|
||||
function $onInit() {
|
||||
return $async(async () => {
|
||||
try {
|
||||
const data = isBE ? await BackupService.getS3Settings() : {};
|
||||
|
||||
this.formValues.passwordS3 = data.Password;
|
||||
this.formValues.cronRule = data.CronRule;
|
||||
this.formValues.accessKeyId = data.AccessKeyID;
|
||||
this.formValues.secretAccessKey = data.SecretAccessKey;
|
||||
this.formValues.region = data.Region;
|
||||
this.formValues.bucketName = data.BucketName;
|
||||
this.formValues.s3CompatibleHost = data.S3CompatibleHost;
|
||||
|
||||
this.formValues.scheduleAutomaticBackups = this.formValues.cronRule ? true : false;
|
||||
this.formValues.passwordProtectS3 = this.formValues.passwordS3 ? true : false;
|
||||
} catch (err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve S3 backup settings');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="download" title-text="Back up Portainer"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" ng-submit="$ctrl.backupPortainer()" name="$ctrl.backupPortainerForm">
|
||||
<div class="col-sm-12 form-section-title"> Backup configuration </div>
|
||||
<div class="text-muted small mb-3">This will back up your Portainer server configuration and does not include containers.</div>
|
||||
|
||||
<box-selector
|
||||
slim="true"
|
||||
options="$ctrl.backupOptions"
|
||||
value="$ctrl.formValues.backupFormType"
|
||||
on-change="($ctrl.onBackupOptionsChange)"
|
||||
radio-name="'backupOptions'"
|
||||
></box-selector>
|
||||
|
||||
<div ng-if="$ctrl.formValues.backupFormType === $ctrl.BACKUP_FORM_TYPES.S3">
|
||||
<!-- Schedule automatic backups -->
|
||||
<div class="form-group mt-3">
|
||||
<por-switch-field
|
||||
label="'Schedule automatic backups'"
|
||||
name="'s3-backup-setting'"
|
||||
feature-id="s3BackupFeatureId"
|
||||
checked="$ctrl.formValues.scheduleAutomaticBackups"
|
||||
field-class="'col-sm-10'"
|
||||
label-class="'col-sm-2'"
|
||||
on-change="($ctrl.onToggleAutoBackups)"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
<!-- !Schedule automatic backups -->
|
||||
<!-- Cron rule -->
|
||||
<div class="form-group" ng-if="$ctrl.formValues.scheduleAutomaticBackups">
|
||||
<label for="cron_rule" class="col-sm-2 control-label text-left">Cron rule</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="0 2 * * *"
|
||||
id="cron_rule"
|
||||
name="cron_rule"
|
||||
ng-model="$ctrl.formValues.cronRule"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
required
|
||||
cronRule
|
||||
data-cy="settings-backupCronRuleInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Cron rule -->
|
||||
<div class="form-group col-md-12" ng-show="$ctrl.backupPortainerForm.cron_rule.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="$ctrl.backupPortainerForm.cron_rule.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
<p ng-message="invalidCron">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
|
||||
Please enter a valid cron rule.</p
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Access key id -->
|
||||
<div class="form-group">
|
||||
<label for="access_key_id" class="col-sm-2 control-label text-left">Access key ID</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="access_key_id"
|
||||
name="access_key_id"
|
||||
ng-model="$ctrl.formValues.accessKeyId"
|
||||
ng-required="$ctrl.formValues.scheduleAutomaticBackups"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
data-cy="settings-accessKeyIdInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="$ctrl.backupPortainerForm.access_key_id.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="$ctrl.backupPortainerForm.access_key_id.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Access key id -->
|
||||
<!-- Secret access key -->
|
||||
<div class="form-group">
|
||||
<label for="secret_access_key" class="col-sm-2 control-label text-left">Secret access key</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="secret_access_key"
|
||||
name="secret_access_key"
|
||||
ng-model="$ctrl.formValues.secretAccessKey"
|
||||
ng-required="$ctrl.formValues.scheduleAutomaticBackups"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
data-cy="settings-secretAccessKeyInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="$ctrl.backupPortainerForm.secret_access_key.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="$ctrl.backupPortainerForm.secret_access_key.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Secret access key -->
|
||||
<!-- Region -->
|
||||
<div class="form-group">
|
||||
<label for="region" class="col-sm-2 control-label text-left">Region</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="default region is us-east-1 if left empty"
|
||||
id="region"
|
||||
name="region"
|
||||
ng-model="$ctrl.formValues.region"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
data-cy="settings-backupRegionInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Region -->
|
||||
<!-- Bucket name -->
|
||||
<div class="form-group">
|
||||
<label for="bucket_name" class="col-sm-2 control-label text-left">Bucket name</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="bucket_name"
|
||||
name="bucket_name"
|
||||
ng-model="$ctrl.formValues.bucketName"
|
||||
ng-required="$ctrl.formValues.scheduleAutomaticBackups"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
data-cy="settings-backupBucketNameInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="$ctrl.backupPortainerForm.bucket_name.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="$ctrl.backupPortainerForm.bucket_name.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Bucket name -->
|
||||
<!-- S3 compatible host -->
|
||||
<div class="form-group">
|
||||
<label for="s3_compatible_host" class="col-sm-2 control-label text-left">
|
||||
S3 compatible host
|
||||
<portainer-tooltip message="'Hostname of a S3 service'"></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="leave empty for AWS S3"
|
||||
id="s3_compatible_host"
|
||||
name="s3_compatible_host"
|
||||
ng-model="$ctrl.formValues.s3CompatibleHost"
|
||||
ng-pattern="/^https?://.+/"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-class="limited-be"
|
||||
data-cy="settings-backupS3CompatibleHostInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="$ctrl.backupPortainerForm.s3_compatible_host.$invalid">
|
||||
<div class="small text-warning">
|
||||
<p class="vertical-center">
|
||||
<pr-icon icon="'alert-triangle'"></pr-icon>
|
||||
<span>S3 host must begin with http:// or https://</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !S3 compatible host -->
|
||||
<div class="col-sm-12 form-section-title"> Security settings </div>
|
||||
<!-- Password protect S3 -->
|
||||
<div class="form-group">
|
||||
<label for="password_protect" class="col-sm-2 control-label text-left">Password protect</label>
|
||||
<div class="col-sm-10">
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="password_protect_s3"
|
||||
name="password_protect_s3"
|
||||
ng-model="$ctrl.formValues.passwordProtectS3"
|
||||
data-cy="settings-passwordProtectToggleS3"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
/><span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password protect S3 -->
|
||||
<!-- Password S3 -->
|
||||
<div class="form-group" ng-if="$ctrl.formValues.passwordProtectS3">
|
||||
<label for="password" class="col-sm-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" ng-model="$ctrl.formValues.passwordS3" id="password_S3" name="password_S3" required data-cy="settings-backups3pw" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="$ctrl.backupPortainerForm.password_S3.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="$ctrl.backupPortainerForm.password_S3.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password S3 -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="$ctrl.backupPortainerForm.$invalid"
|
||||
ng-click="$ctrl.exportBackup()"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
data-cy="settings-exportBackupS3Button"
|
||||
>
|
||||
<span>
|
||||
<pr-icon icon="'upload'" class-name="'mr-1'"></pr-icon>
|
||||
Export backup
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<hr />
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="$ctrl.backupPortainerForm.$invalid || $ctrl.state.backupInProgress"
|
||||
ng-click="$ctrl.saveS3BackupSettings()"
|
||||
limited-feature-dir="{{::$ctrl.s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
data-cy="settings-saveBackupSettingsButton"
|
||||
>
|
||||
<span>Save backup settings</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$ctrl.formValues.backupFormType === $ctrl.BACKUP_FORM_TYPES.FILE">
|
||||
<div class="col-sm-12 form-section-title"> Security settings </div>
|
||||
<!-- Password protect -->
|
||||
<div class="form-group">
|
||||
<label for="password_protect" class="col-sm-2 control-label text-left">Password protect</label>
|
||||
<div class="col-sm-2">
|
||||
<label class="switch" data-cy="settings-passwordProtectLocal">
|
||||
<input type="checkbox" id="password_protect" name="password_protect" ng-model="$ctrl.formValues.passwordProtect" />
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password protect -->
|
||||
|
||||
<!-- Password -->
|
||||
<div class="form-group" ng-if="$ctrl.formValues.passwordProtect">
|
||||
<label for="password" class="col-sm-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" ng-model="$ctrl.formValues.password" id="password" name="password" required data-cy="settings-backupLocalPassword" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="$ctrl.backupPortainerForm.password.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="$ctrl.backupPortainerForm.password.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password -->
|
||||
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-click="$ctrl.downloadBackup()"
|
||||
ng-disabled="$ctrl.backupPortainerForm.$invalid || $ctrl.state.backupInProgress || $ctrl.state.featureLimited"
|
||||
button-spinner="$ctrl.state.backupInProgress"
|
||||
data-cy="settings-downloadLocalBackup"
|
||||
>
|
||||
<span ng-hide="$ctrl.state.backupInProgress">
|
||||
<pr-icon icon="'download'"></pr-icon>
|
||||
Download backup
|
||||
</span>
|
||||
<span ng-show="$ctrl.state.backupInProgress">
|
||||
<pr-icon icon="'download'"></pr-icon>
|
||||
Downloading backup
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import controller from './backup-settings-panel.controller';
|
||||
|
||||
angular.module('portainer.app').component('backupSettingsPanel', {
|
||||
templateUrl: './backup-settings-panel.html',
|
||||
controller,
|
||||
bindings: {
|
||||
settings: '<',
|
||||
onSubmit: '<',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/* @ngInject */
|
||||
export default function HiddenContainersPanelController($async) {
|
||||
this.addFilteredContainerLabel = addFilteredContainerLabel.bind(this);
|
||||
this.removeFilteredContainerLabel = removeFilteredContainerLabel.bind(this);
|
||||
this.$onInit = $onInit.bind(this);
|
||||
this.handleSubmit = handleSubmit.bind(this);
|
||||
|
||||
this.state = {
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
this.formValues = {
|
||||
BlackListedLabels: [],
|
||||
labelName: '',
|
||||
labelValue: '',
|
||||
};
|
||||
|
||||
this.state = {
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
function addFilteredContainerLabel() {
|
||||
const label = {
|
||||
name: this.formValues.labelName,
|
||||
value: this.formValues.labelValue,
|
||||
};
|
||||
|
||||
const filteredSettings = [...this.formValues.BlackListedLabels, label];
|
||||
this.handleSubmit(filteredSettings);
|
||||
this.formValues.labelName = '';
|
||||
this.formValues.labelValue = '';
|
||||
}
|
||||
|
||||
function removeFilteredContainerLabel(index) {
|
||||
const filteredSettings = this.formValues.BlackListedLabels.filter((item, i) => i !== index);
|
||||
this.handleSubmit(filteredSettings);
|
||||
}
|
||||
|
||||
function handleSubmit(labels) {
|
||||
return $async(async () => {
|
||||
this.state.actionInProgress = true;
|
||||
await this.onSubmit({ BlackListedLabels: labels }, 'Hidden container settings updated');
|
||||
this.formValues.BlackListedLabels = labels;
|
||||
this.state.actionInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function $onInit() {
|
||||
this.formValues.BlackListedLabels = this.settings.BlackListedLabels;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="box" title-text="Hidden containers"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" ng-submit="$ctrl.addFilteredContainerLabel()" name="addTagForm">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small"> You can hide containers with specific labels from Portainer UI. You need to specify the label name and value. </span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="header_name" class="col-sm-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-11 col-md-4">
|
||||
<input type="text" required class="form-control" id="header_name" name="label_name" ng-model="$ctrl.formValues.labelName" placeholder="e.g. com.example.foo" />
|
||||
</div>
|
||||
<label for="header_value" class="col-sm-1 control-label text-left">Value</label>
|
||||
<div class="col-sm-11 col-md-4">
|
||||
<input type="text" class="form-control" id="header_value" ng-model="$ctrl.formValues.labelValue" placeholder="e.g. bar" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="$ctrl.state.actionInProgress || !$ctrl.formValues.labelName"
|
||||
><pr-icon icon="'plus'" class-name="'mr-1'"></pr-icon>Add filter</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 table-responsive">
|
||||
<table class="table-hover table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="label in $ctrl.formValues.BlackListedLabels">
|
||||
<td>{{ label.name }}</td>
|
||||
<td>{{ label.value }}</td>
|
||||
<td>
|
||||
<button type="button" ng-disabled="$ctrl.state.actionInProgress" class="btn btn-danger btn-xs" ng-click="$ctrl.removeFilteredContainerLabel($index)">
|
||||
<pr-icon icon="'trash-2'" class-name="'mr-1'"></pr-icon>
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.formValues.BlackListedLabels.length === 0">
|
||||
<td colspan="3" class="text-muted text-center">No filter available.</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.formValues.BlackListedLabels">
|
||||
<td colspan="3" class="text-muted text-center">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !filtered-labels -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import controller from './hidden-containers-panel.controller';
|
||||
|
||||
angular.module('portainer.app').component('hiddenContainersPanel', {
|
||||
templateUrl: './hidden-containers-panel.html',
|
||||
controller,
|
||||
bindings: {
|
||||
settings: '<',
|
||||
onSubmit: '<',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import controller from './kube-settings-panel.controller';
|
||||
|
||||
angular.module('portainer.app').component('kubeSettingsPanel', {
|
||||
templateUrl: './kube-settings-panel.html',
|
||||
controller,
|
||||
bindings: {
|
||||
settings: '<',
|
||||
onSubmit: '<',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
/* @ngInject */
|
||||
export default function KubeSettingsPanelController($scope, $async) {
|
||||
this.onToggleAddWithForm = onToggleAddWithForm.bind(this);
|
||||
this.onToggleHideWebEditor = onToggleHideWebEditor.bind(this);
|
||||
this.onToggleHideFileUpload = onToggleHideFileUpload.bind(this);
|
||||
this.onTogglePerEnvOverride = onTogglePerEnvOverride.bind(this);
|
||||
this.saveKubernetesSettings = saveKubernetesSettings.bind(this);
|
||||
this.$onInit = $onInit.bind(this);
|
||||
|
||||
this.enforceDeploymentOptions = FeatureId.ENFORCE_DEPLOYMENT_OPTIONS;
|
||||
|
||||
this.state = {
|
||||
availableKubeconfigExpiryOptions: [
|
||||
{
|
||||
key: '1 day',
|
||||
value: '24h',
|
||||
},
|
||||
{
|
||||
key: '7 days',
|
||||
value: `${24 * 7}h`,
|
||||
},
|
||||
{
|
||||
key: '30 days',
|
||||
value: `${24 * 30}h`,
|
||||
},
|
||||
{
|
||||
key: '1 year',
|
||||
value: `${24 * 30 * 12}h`,
|
||||
},
|
||||
{
|
||||
key: 'No expiry',
|
||||
value: '0',
|
||||
},
|
||||
],
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
this.formValues = {
|
||||
KubeconfigExpiry: undefined,
|
||||
HelmRepositoryURL: undefined,
|
||||
GlobalDeploymentOptions: {
|
||||
hideAddWithForm: false,
|
||||
perEnvOverride: false,
|
||||
hideWebEditor: false,
|
||||
hideFileUpload: false,
|
||||
},
|
||||
};
|
||||
|
||||
function onToggleAddWithForm(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
this.formValues.GlobalDeploymentOptions.hideAddWithForm = checked;
|
||||
this.formValues.GlobalDeploymentOptions.hideWebEditor = checked;
|
||||
this.formValues.GlobalDeploymentOptions.hideFileUpload = checked;
|
||||
});
|
||||
}
|
||||
|
||||
function onToggleHideWebEditor(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
this.formValues.GlobalDeploymentOptions.hideWebEditor = !checked;
|
||||
});
|
||||
}
|
||||
|
||||
function onToggleHideFileUpload(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
this.formValues.GlobalDeploymentOptions.hideFileUpload = !checked;
|
||||
});
|
||||
}
|
||||
|
||||
function onTogglePerEnvOverride(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
this.formValues.GlobalDeploymentOptions.perEnvOverride = checked;
|
||||
});
|
||||
}
|
||||
|
||||
async function saveKubernetesSettings() {
|
||||
$async(async () => {
|
||||
this.state.actionInProgress = true;
|
||||
await this.onSubmit(this.formValues, 'Kubernetes settings updated');
|
||||
this.state.actionInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function $onInit() {
|
||||
if (this.settings.GlobalDeploymentOptions) {
|
||||
this.formValues.GlobalDeploymentOptions = this.settings.GlobalDeploymentOptions;
|
||||
}
|
||||
this.formValues.KubeconfigExpiry = this.settings.KubeconfigExpiry;
|
||||
this.formValues.HelmRepositoryURL = this.settings.HelmRepositoryURL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="svg-kube" title-text="Kubernetes settings"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- helm charts -->
|
||||
<div class="col-sm-12 form-section-title"> Helm Repository </div>
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can specify the URL to your own helm repository here. See the
|
||||
<a href="https://helm.sh/docs/topics/chart_repository/" target="_blank">official documentation</a> for more details.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="helmrepository_url" class="col-sm-2 control-label text-left"> URL </label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="$ctrl.formValues.HelmRepositoryURL" id="helmrepository_url" placeholder="https://charts.bitnami.com/bitnami" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !helm charts -->
|
||||
<!-- kubeconfig -->
|
||||
<div class="col-sm-12 form-section-title"> Kubeconfig </div>
|
||||
<div class="form-group">
|
||||
<label for="kubeconfig_expiry" class="col-sm-2 control-label text-left"> Kubeconfig expiry </label>
|
||||
<div class="col-sm-10">
|
||||
<select
|
||||
id="kubeconfig_expiry"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.KubeconfigExpiry"
|
||||
ng-options="opt.value as opt.key for opt in $ctrl.state.availableKubeconfigExpiryOptions"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !kubeconfig -->
|
||||
<!-- deployment options -->
|
||||
<div class="col-sm-12 form-section-title"> Deployment Options </div>
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Enforce code-based deployment'"
|
||||
checked="$ctrl.formValues.GlobalDeploymentOptions.hideAddWithForm"
|
||||
name="'toggle_hideAddWithForm'"
|
||||
feature-id="$ctrl.enforceDeploymentOptions"
|
||||
on-change="($ctrl.onToggleAddWithForm)"
|
||||
field-class="'col-sm-10'"
|
||||
label-class="'col-sm-2'"
|
||||
tooltip="'Hides the \'Add with form\' buttons and prevents adding/editing of resources via forms'"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
<div class="form-group flex flex-col gap-y-1" ng-if="$ctrl.formValues.GlobalDeploymentOptions.hideAddWithForm">
|
||||
<por-switch-field
|
||||
label="'Allow web editor and custom template use'"
|
||||
checked="!$ctrl.formValues.GlobalDeploymentOptions.hideWebEditor"
|
||||
name="'toggle_hideWebEditor'"
|
||||
on-change="($ctrl.onToggleHideWebEditor)"
|
||||
field-class="'col-sm-10'"
|
||||
label-class="'col-sm-2 !pl-4'"
|
||||
></por-switch-field>
|
||||
<por-switch-field
|
||||
label="'Allow specifying of a manifest via a URL'"
|
||||
checked="!$ctrl.formValues.GlobalDeploymentOptions.hideFileUpload"
|
||||
name="'toggle_hideFileUpload'"
|
||||
on-change="($ctrl.onToggleHideFileUpload)"
|
||||
field-class="'col-sm-10'"
|
||||
label-class="'col-sm-2 !pl-4'"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
<div class="form-group" limited-feature-dir="{{::$ctrl.enforceDeploymentOptions}}" limited-feature-class="hidden">
|
||||
<por-switch-field
|
||||
label="'Allow per environment override'"
|
||||
checked="$ctrl.formValues.GlobalDeploymentOptions.perEnvOverride"
|
||||
name="'toggle_perEnvOverride'"
|
||||
on-change="($ctrl.onTogglePerEnvOverride)"
|
||||
field-class="'col-sm-10'"
|
||||
label-class="'col-sm-2'"
|
||||
tooltip="'Allows overriding of deployment options in the Cluster setup screen of each environment'"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
<!-- ! deployment options -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-click="$ctrl.saveKubernetesSettings()"
|
||||
ng-disabled="$ctrl.state.actionInProgress"
|
||||
button-spinner="$ctrl.state.actionInProgress"
|
||||
data-cy="settings-saveKubeSettingsButton"
|
||||
>
|
||||
<span ng-hide="$ctrl.state.actionInProgress">Save Kubernetes settings</span>
|
||||
<span ng-show="$ctrl.state.actionInProgress">Saving...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,511 +1,13 @@
|
||||
<page-header title="'Settings'" breadcrumbs="['Settings']"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="settings" title-text="Application settings"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- snapshot-interval -->
|
||||
<div class="form-group">
|
||||
<label for="snapshot_interval" class="col-sm-2 control-label text-left">Snapshot interval</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="settings.SnapshotInterval" id="snapshot_interval" placeholder="e.g. 15m" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !snapshot-interval -->
|
||||
<application-settings-panel ng-if="settings" settings="settings" on-submit="(updateSettings)"></application-settings-panel>
|
||||
|
||||
<!-- checkin-interval -->
|
||||
<edge-checkin-interval-field
|
||||
size="'xsmall'"
|
||||
value="settings.EdgeAgentCheckinInterval"
|
||||
label="'Edge agent default poll frequency'"
|
||||
is-default-hidden="true"
|
||||
on-change="(onChangeCheckInInterval)"
|
||||
></edge-checkin-interval-field>
|
||||
<!-- !checkin-interval -->
|
||||
|
||||
<!-- logo -->
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Use custom logo'"
|
||||
checked="formValues.customLogo"
|
||||
name="'toggle_logo'"
|
||||
disabled="state.isDemo"
|
||||
on-change="(onToggleCustomLogo)"
|
||||
field-class="'col-sm-12'"
|
||||
label-class="'col-sm-2'"
|
||||
></por-switch-field>
|
||||
<div class="col-sm-12" ng-if="state.isDemo" style="margin-top: 10px">
|
||||
<span class="small text-muted">You cannot use this feature in the demo version of Portainer.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="formValues.customLogo">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small"> You can specify the URL to your logo here. For an optimal display, logo dimensions should be 155px by 55px. </span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="logo_url" class="col-sm-2 control-label text-left"> URL </label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="settings.LogoURL" id="logo_url" placeholder="https://mycompany.com/logo.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !logo -->
|
||||
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Allow the collection of anonymous statistics'"
|
||||
checked="formValues.enableTelemetry"
|
||||
name="'toggle_enableTelemetry'"
|
||||
on-change="(onToggleEnableTelemetry)"
|
||||
disabled="state.isDemo"
|
||||
field-class="'col-sm-12'"
|
||||
label-class="'col-sm-2'"
|
||||
></por-switch-field>
|
||||
<div class="col-sm-12" ng-if="state.isDemo" style="margin-top: 10px">
|
||||
<span class="small text-muted">You cannot use this feature in the demo version of Portainer.</span>
|
||||
</div>
|
||||
<div class="col-sm-12 text-muted small" style="margin-top: 10px">
|
||||
You can find more information about this in our
|
||||
<a href="https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/" target="_blank">privacy policy</a>.
|
||||
</div>
|
||||
</div>
|
||||
<!-- login screen banner -->
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Login screen banner'"
|
||||
name="'toggle_login_banner'"
|
||||
feature-id="customBannerFeatureId"
|
||||
checked="$formValues.customLoginBanner"
|
||||
on-change="(onToggleCustomLoginBanner)"
|
||||
field-class="'col-sm-12'"
|
||||
label-class="'col-sm-2'"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
<!-- !login screen banner -->
|
||||
<!-- templates -->
|
||||
<div class="col-sm-12 form-section-title"> App Templates </div>
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can specify the URL to your own template definitions file here. See
|
||||
<a href="https://docs.portainer.io/advanced/app-templates/build" target="_blank">Portainer documentation</a> for more details.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="templates_url" class="col-sm-2 control-label text-left"> URL </label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="settings.TemplatesURL"
|
||||
id="templates_url"
|
||||
placeholder="https://myserver.mydomain/templates.json"
|
||||
required
|
||||
data-cy="settings-templateUrl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !templates -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-click="saveApplicationSettings()"
|
||||
ng-disabled="state.actionInProgress || !settings.TemplatesURL"
|
||||
button-spinner="state.actionInProgress"
|
||||
data-cy="settings-saveSettingsButton"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress">Save application settings</span>
|
||||
<span ng-show="state.actionInProgress">Saving...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="svg-kube" title-text="Kubernetes settings"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- helm charts -->
|
||||
<div class="col-sm-12 form-section-title"> Helm Repository </div>
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can specify the URL to your own helm repository here. See the
|
||||
<a href="https://helm.sh/docs/topics/chart_repository/" target="_blank">official documentation</a> for more details.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="helmrepository_url" class="col-sm-2 control-label text-left"> URL </label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="settings.HelmRepositoryURL" id="helmrepository_url" placeholder="https://charts.bitnami.com/bitnami" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !helm charts -->
|
||||
<!-- kube -->
|
||||
<div class="col-sm-12 form-section-title"> Kubeconfig </div>
|
||||
<div class="form-group">
|
||||
<label for="kubeconfig_expiry" class="col-sm-2 control-label text-left"> Kubeconfig expiry </label>
|
||||
<div class="col-sm-10">
|
||||
<select
|
||||
id="kubeconfig_expiry"
|
||||
class="form-control"
|
||||
ng-model="formValues.KubeconfigExpiry"
|
||||
ng-options="opt.value as opt.key for opt in state.availableKubeconfigExpiryOptions"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ! kube -->
|
||||
<!-- deployment options -->
|
||||
<div class="col-sm-12 form-section-title"> Deployment Options </div>
|
||||
<div class="form-group">
|
||||
<por-switch-field
|
||||
label="'Enforce code-based deployment'"
|
||||
name="'toggle_hideAddWithForm'"
|
||||
feature-id="enforceDeploymentOptions"
|
||||
disabled="true"
|
||||
checked="false"
|
||||
field-class="'col-sm-10'"
|
||||
label-class="'col-sm-2'"
|
||||
tooltip="'Hides the \'Add with form\' buttons and prevents adding/editing of resources via forms'"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
<!-- !deployment options -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-click="saveKubernetesSettings()"
|
||||
ng-disabled="state.kubeSettingsActionInProgress"
|
||||
button-spinner="state.kubeSettingsActionInProgress"
|
||||
data-cy="settings-saveKubeSettingsButton"
|
||||
>
|
||||
<span ng-hide="state.kubeSettingsActionInProgress">Save Kubernetes settings</span>
|
||||
<span ng-show="state.kubeSettingsActionInProgress">Saving...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
<kube-settings-panel ng-if="settings" settings="settings" on-submit="(updateSettings)"></kube-settings-panel>
|
||||
|
||||
<ssl-ca-file-settings></ssl-ca-file-settings>
|
||||
<ssl-certificate-settings ng-show="state.showHTTPS"></ssl-certificate-settings>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="box" title-text="Hidden containers"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" ng-submit="addFilteredContainerLabel()" name="addTagForm">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small"> You can hide containers with specific labels from Portainer UI. You need to specify the label name and value. </span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="header_name" class="col-sm-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-11 col-md-4">
|
||||
<input type="text" required class="form-control" id="header_name" name="label_name" ng-model="formValues.labelName" placeholder="e.g. com.example.foo" />
|
||||
</div>
|
||||
<label for="header_value" class="col-sm-1 control-label text-left">Value</label>
|
||||
<div class="col-sm-11 col-md-4">
|
||||
<input type="text" class="form-control" id="header_value" ng-model="formValues.labelValue" placeholder="e.g. bar" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="!formValues.labelName"><pr-icon icon="'plus'" class-name="'mr-1'"></pr-icon>Add filter</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 table-responsive">
|
||||
<table class="table-hover table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="label in formValues.BlackListedLabels">
|
||||
<td>{{ label.name }}</td>
|
||||
<td>{{ label.value }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-danger btn-xs" ng-click="removeFilteredContainerLabel($index)">
|
||||
<pr-icon icon="'trash-2'" class-name="'mr-1'"></pr-icon>
|
||||
Remove</button
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="formValues.BlackListedLabels.length === 0">
|
||||
<td colspan="3" class="text-muted text-center">No filter available.</td>
|
||||
</tr>
|
||||
<tr ng-if="!formValues.BlackListedLabels">
|
||||
<td colspan="3" class="text-muted text-center">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !filtered-labels -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
<hidden-containers-panel ng-if="settings" settings="settings" on-submit="(updateSettings)"></hidden-containers-panel>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="download" title-text="Back up Portainer"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" ng-submit="backupPortainer()" name="backupPortainerForm">
|
||||
<div class="col-sm-12 form-section-title"> Backup configuration </div>
|
||||
<div class="text-muted small mb-3">This will back up your Portainer server configuration and does not include containers.</div>
|
||||
|
||||
<box-selector slim="true" options="backupOptions" value="formValues.backupFormType" on-change="(onBackupOptionsChange)" radio-name="'backupOptions'"></box-selector>
|
||||
|
||||
<div ng-if="formValues.backupFormType === BACKUP_FORM_TYPES.S3">
|
||||
<!-- Schedule automatic backups -->
|
||||
<div class="form-group mt-3">
|
||||
<por-switch-field
|
||||
label="'Schedule automatic backups'"
|
||||
name="'s3-backup-setting'"
|
||||
feature-id="s3BackupFeatureId"
|
||||
checked="formValues.scheduleAutomaticBackups"
|
||||
field-class="'col-sm-10'"
|
||||
label-class="'col-sm-2'"
|
||||
on-change="(onToggleAutoBackups)"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
<!-- !Schedule automatic backups -->
|
||||
<!-- Cron rule -->
|
||||
<div class="form-group" ng-if="formValues.scheduleAutomaticBackups">
|
||||
<label for="cron_rule" class="col-sm-2 control-label text-left">Cron rule</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="0 2 * * *"
|
||||
id="cron_rule"
|
||||
name="cron_rule"
|
||||
ng-model="formValues.cronRule"
|
||||
limited-feature-dir="{{::s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
required
|
||||
cronRule
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Cron rule -->
|
||||
<!-- Access key id -->
|
||||
<div class="form-group">
|
||||
<label for="access_key_id" class="col-sm-2 control-label text-left">Access Key ID</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="access_key_id"
|
||||
name="access_key_id"
|
||||
ng-model="formValues.accessKeyId"
|
||||
ng-required="formValues.scheduleAutomaticBackups"
|
||||
limited-feature-dir="{{::s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Access key id -->
|
||||
<!-- Secret access key -->
|
||||
<div class="form-group">
|
||||
<label for="secret_access_key" class="col-sm-2 control-label text-left">Secret Access Key</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="secret_access_key"
|
||||
name="secret_access_key"
|
||||
ng-model="formValues.secretAccessKey"
|
||||
ng-required="formValues.scheduleAutomaticBackups"
|
||||
limited-feature-dir="{{::s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Secret access key -->
|
||||
<!-- Region -->
|
||||
<div class="form-group">
|
||||
<label for="region" class="col-sm-2 control-label text-left">Region</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="default region is us-east-1 if left empty"
|
||||
id="region"
|
||||
name="region"
|
||||
ng-model="formValues.region"
|
||||
ng-required="formValues.scheduleAutomaticBackups"
|
||||
limited-feature-dir="{{::s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Region -->
|
||||
<!-- Bucket name -->
|
||||
<div class="form-group">
|
||||
<label for="bucket_name" class="col-sm-2 control-label text-left">Bucket name</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="bucket_name"
|
||||
name="bucket_name"
|
||||
ng-model="formValues.bucketName"
|
||||
ng-required="formValues.scheduleAutomaticBackups"
|
||||
limited-feature-dir="{{::s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Bucket name -->
|
||||
<div class="col-sm-12 form-section-title"> Security settings </div>
|
||||
<!-- Password protect S3 -->
|
||||
<div class="form-group">
|
||||
<label for="password_protect" class="col-sm-2 control-label text-left">Password protect</label>
|
||||
<div class="col-sm-10">
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="password_protect_s3"
|
||||
name="password_protect_s3"
|
||||
ng-model="formValues.passwordProtectS3"
|
||||
data-cy="settings-passwordProtectToggleS3"
|
||||
disabled
|
||||
/><span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password protect S3 -->
|
||||
<!-- Password S3 -->
|
||||
<div class="form-group" ng-if="formValues.passwordProtectS3">
|
||||
<label for="password" class="col-sm-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" ng-model="formValues.passwordS3" id="password_S3" name="password_S3" required data-cy="settings-backups3pw" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="backupPortainerForm.password_S3.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="backupPortainerForm.password_S3.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password S3 -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="backupPortainerForm.$invalid"
|
||||
ng-click="exportBackup()"
|
||||
limited-feature-dir="{{::s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
>
|
||||
<span>
|
||||
<pr-icon icon="'upload'" class-name="'mr-1'"></pr-icon>
|
||||
Export backup
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<hr />
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="backupPortainerForm.$invalid ||state.backupInProgress"
|
||||
ng-click="saveS3BackupSettings()"
|
||||
limited-feature-dir="{{::s3BackupFeatureId}}"
|
||||
limited-feature-disabled
|
||||
limited-feature-class="limited-be"
|
||||
>
|
||||
<span>Save backup settings</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="formValues.backupFormType === BACKUP_FORM_TYPES.FILE">
|
||||
<div class="col-sm-12 form-section-title"> Security settings </div>
|
||||
<!-- Password protect -->
|
||||
<div class="form-group">
|
||||
<label for="password_protect" class="col-sm-2 control-label text-left">Password protect</label>
|
||||
<div class="col-sm-2">
|
||||
<label class="switch" data-cy="settings-passwordProtectLocal">
|
||||
<input type="checkbox" id="password_protect" name="password_protect" ng-model="formValues.passwordProtect" /><span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password protect -->
|
||||
|
||||
<!-- Password -->
|
||||
<div class="form-group" ng-if="formValues.passwordProtect">
|
||||
<label for="password" class="col-sm-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" ng-model="formValues.password" id="password" name="password" required data-cy="settings-backupLocalPassword" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="backupPortainerForm.password.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="backupPortainerForm.password.$error">
|
||||
<p ng-message="required"> <pr-icon icon="'alert-triangle'"></pr-icon> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !Password -->
|
||||
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-click="downloadBackup()"
|
||||
ng-disabled="backupPortainerForm.$invalid || state.backupInProgress || state.featureLimited"
|
||||
button-spinner="state.backupInProgress"
|
||||
data-cy="settings-downloadLocalBackup"
|
||||
>
|
||||
<span ng-hide="state.backupInProgress">Download backup</span>
|
||||
<span ng-show="state.backupInProgress">Downloading backup</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
<!-- backup -->
|
||||
<backup-settings-panel ng-if="settings" settings="settings" on-submit="(updateSettings)"></backup-settings-panel>
|
||||
|
||||
@@ -1,201 +1,35 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
import { options } from '@/react/portainer/settings/SettingsView/backup-options';
|
||||
|
||||
angular.module('portainer.app').controller('SettingsController', [
|
||||
'$scope',
|
||||
'Notifications',
|
||||
'SettingsService',
|
||||
'StateManager',
|
||||
'BackupService',
|
||||
'FileSaver',
|
||||
function ($scope, Notifications, SettingsService, StateManager, BackupService, FileSaver) {
|
||||
$scope.customBannerFeatureId = FeatureId.CUSTOM_LOGIN_BANNER;
|
||||
$scope.s3BackupFeatureId = FeatureId.S3_BACKUP_SETTING;
|
||||
$scope.enforceDeploymentOptions = FeatureId.ENFORCE_DEPLOYMENT_OPTIONS;
|
||||
|
||||
$scope.backupOptions = options;
|
||||
|
||||
$scope.state = {
|
||||
isDemo: false,
|
||||
actionInProgress: false,
|
||||
availableKubeconfigExpiryOptions: [
|
||||
{
|
||||
key: '1 day',
|
||||
value: '24h',
|
||||
},
|
||||
{
|
||||
key: '7 days',
|
||||
value: `${24 * 7}h`,
|
||||
},
|
||||
{
|
||||
key: '30 days',
|
||||
value: `${24 * 30}h`,
|
||||
},
|
||||
{
|
||||
key: '1 year',
|
||||
value: `${24 * 30 * 12}h`,
|
||||
},
|
||||
{
|
||||
key: 'No expiry',
|
||||
value: '0',
|
||||
},
|
||||
],
|
||||
backupInProgress: false,
|
||||
featureLimited: false,
|
||||
showHTTPS: !window.ddExtension,
|
||||
};
|
||||
|
||||
$scope.BACKUP_FORM_TYPES = { S3: 's3', FILE: 'file' };
|
||||
|
||||
$scope.formValues = {
|
||||
customLogo: false,
|
||||
KubeconfigExpiry: undefined,
|
||||
HelmRepositoryURL: undefined,
|
||||
BlackListedLabels: [],
|
||||
labelName: '',
|
||||
labelValue: '',
|
||||
enableTelemetry: false,
|
||||
passwordProtect: false,
|
||||
password: '',
|
||||
backupFormType: $scope.BACKUP_FORM_TYPES.FILE,
|
||||
};
|
||||
|
||||
$scope.initialFormValues = {};
|
||||
|
||||
$scope.onToggleEnableTelemetry = function onToggleEnableTelemetry(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
$scope.formValues.enableTelemetry = checked;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onToggleCustomLogo = function onToggleCustomLogo(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
$scope.formValues.customLogo = checked;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onToggleAutoBackups = function onToggleAutoBackups(checked) {
|
||||
$scope.$evalAsync(() => {
|
||||
$scope.formValues.scheduleAutomaticBackups = checked;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onBackupOptionsChange = function (type, limited) {
|
||||
$scope.formValues.backupFormType = type;
|
||||
$scope.state.featureLimited = limited;
|
||||
};
|
||||
|
||||
$scope.onChangeCheckInInterval = function (interval) {
|
||||
$scope.$evalAsync(() => {
|
||||
var settings = $scope.settings;
|
||||
settings.EdgeAgentCheckinInterval = interval;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeFilteredContainerLabel = function (index) {
|
||||
const filteredSettings = $scope.formValues.BlackListedLabels.filter((_, i) => i !== index);
|
||||
const filteredSettingsPayload = { BlackListedLabels: filteredSettings };
|
||||
updateSettings(filteredSettingsPayload, 'Hidden container settings updated');
|
||||
};
|
||||
|
||||
$scope.addFilteredContainerLabel = function () {
|
||||
var label = {
|
||||
name: $scope.formValues.labelName,
|
||||
value: $scope.formValues.labelValue,
|
||||
};
|
||||
|
||||
const filteredSettings = [...$scope.formValues.BlackListedLabels, label];
|
||||
const filteredSettingsPayload = { BlackListedLabels: filteredSettings };
|
||||
updateSettings(filteredSettingsPayload, 'Hidden container settings updated');
|
||||
};
|
||||
|
||||
$scope.downloadBackup = function () {
|
||||
const payload = {};
|
||||
if ($scope.formValues.passwordProtect) {
|
||||
payload.password = $scope.formValues.password;
|
||||
}
|
||||
|
||||
$scope.state.backupInProgress = true;
|
||||
|
||||
BackupService.downloadBackup(payload)
|
||||
.then(function success(data) {
|
||||
const downloadData = new Blob([data.file], { type: 'application/gzip' });
|
||||
FileSaver.saveAs(downloadData, data.name);
|
||||
Notifications.success('Success', 'Backup successfully downloaded');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to download backup');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.backupInProgress = false;
|
||||
});
|
||||
};
|
||||
|
||||
// only update the values from the app settings widget. In future separate the api endpoints
|
||||
$scope.saveApplicationSettings = function () {
|
||||
const appSettingsPayload = {
|
||||
SnapshotInterval: $scope.settings.SnapshotInterval,
|
||||
LogoURL: $scope.formValues.customLogo ? $scope.settings.LogoURL : '',
|
||||
EnableTelemetry: $scope.formValues.enableTelemetry,
|
||||
TemplatesURL: $scope.settings.TemplatesURL,
|
||||
EdgeAgentCheckinInterval: $scope.settings.EdgeAgentCheckinInterval,
|
||||
};
|
||||
|
||||
$scope.state.actionInProgress = true;
|
||||
updateSettings(appSettingsPayload, 'Application settings updated');
|
||||
};
|
||||
|
||||
// only update the values from the kube settings widget. In future separate the api endpoints
|
||||
$scope.saveKubernetesSettings = function () {
|
||||
const kubeSettingsPayload = {
|
||||
KubeconfigExpiry: $scope.formValues.KubeconfigExpiry,
|
||||
HelmRepositoryURL: $scope.formValues.HelmRepositoryURL,
|
||||
GlobalDeploymentOptions: $scope.formValues.GlobalDeploymentOptions,
|
||||
};
|
||||
|
||||
$scope.state.kubeSettingsActionInProgress = true;
|
||||
updateSettings(kubeSettingsPayload, 'Kubernetes settings updated');
|
||||
};
|
||||
function ($scope, Notifications, SettingsService, StateManager) {
|
||||
$scope.updateSettings = updateSettings;
|
||||
|
||||
function updateSettings(settings, successMessage = 'Settings updated') {
|
||||
SettingsService.update(settings)
|
||||
.then(function success(response) {
|
||||
// ignore CloudApiKeys to avoid overriding them
|
||||
//
|
||||
// it is not ideal solution as API still accepts CloudAPIKeys
|
||||
// which may override the cloud provider API keys
|
||||
settings.CloudApiKeys = undefined;
|
||||
return SettingsService.update(settings)
|
||||
.then(function success() {
|
||||
Notifications.success('Success', successMessage);
|
||||
StateManager.updateLogo(settings.LogoURL);
|
||||
StateManager.updateSnapshotInterval(settings.SnapshotInterval);
|
||||
StateManager.updateEnableTelemetry(settings.EnableTelemetry);
|
||||
$scope.initialFormValues.enableTelemetry = response.EnableTelemetry;
|
||||
$scope.formValues.BlackListedLabels = response.BlackListedLabels;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update settings');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.kubeSettingsActionInProgress = false;
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
const state = StateManager.getState();
|
||||
$scope.state.isDemo = state.application.demoEnvironment.enabled;
|
||||
|
||||
SettingsService.settings()
|
||||
.then(function success(data) {
|
||||
var settings = data;
|
||||
.then(function success(settings) {
|
||||
$scope.settings = settings;
|
||||
|
||||
if (settings.LogoURL !== '') {
|
||||
$scope.formValues.customLogo = true;
|
||||
}
|
||||
|
||||
$scope.formValues.enableTelemetry = settings.EnableTelemetry;
|
||||
$scope.formValues.KubeconfigExpiry = settings.KubeconfigExpiry;
|
||||
$scope.formValues.HelmRepositoryURL = settings.HelmRepositoryURL;
|
||||
$scope.formValues.BlackListedLabels = settings.BlackListedLabels;
|
||||
$scope.initialFormValues.enableTelemetry = settings.EnableTelemetry;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve application settings');
|
||||
|
||||
Reference in New Issue
Block a user