From 28cf2d04cd4a65aa76fe953dcdbb240888a64099 Mon Sep 17 00:00:00 2001 From: FyloZ Date: Sat, 7 Aug 2021 22:05:46 -0400 Subject: [PATCH] =?UTF-8?q?#2=20Ajout=20du=20support=20pour=20les=20config?= =?UTF-8?q?uration=20s=C3=A9curis=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 8 +- src/_variables.scss | 20 +++++ .../mix-table/mix-table.component.sass | 2 +- .../modules/configuration/config.module.ts | 5 +- src/app/modules/configuration/config.sass | 3 + src/app/modules/configuration/config.ts | 49 ++++++++++-- src/app/modules/configuration/editor.html | 28 ++++--- src/app/modules/configuration/secure.html | 25 +++++++ .../shared/components/buttons/buttons.sass | 6 ++ .../shared/components/buttons/buttons.ts | 15 ++-- .../components/dialogs/dialogs.module.ts | 21 ++++++ .../shared/components/dialogs/dialogs.scss | 26 +++++++ .../shared/components/dialogs/dialogs.ts | 74 +++++++++++++++++++ .../shared/components/dialogs/prompt.html | 10 +++ .../info-banner/info-banner.component.sass | 2 +- .../shared/components/tables/table.sass | 2 +- .../user-info/user-menu.component.sass | 2 +- src/app/modules/shared/model/config.model.ts | 53 +++++++++++-- .../modules/shared/service/config.service.ts | 46 +++++------- src/app/modules/shared/shared.module.ts | 4 +- src/app/modules/shared/utils/map.utils.ts | 17 +++++ .../touch-up-kit/components/finish.sass | 2 +- src/custom-theme.scss | 4 - src/styles.sass | 4 +- 24 files changed, 353 insertions(+), 75 deletions(-) create mode 100644 src/_variables.scss create mode 100644 src/app/modules/configuration/secure.html create mode 100644 src/app/modules/shared/components/buttons/buttons.sass create mode 100644 src/app/modules/shared/components/dialogs/dialogs.module.ts create mode 100644 src/app/modules/shared/components/dialogs/dialogs.scss create mode 100644 src/app/modules/shared/components/dialogs/dialogs.ts create mode 100644 src/app/modules/shared/components/dialogs/prompt.html create mode 100644 src/app/modules/shared/utils/map.utils.ts diff --git a/docker-compose.yml b/docker-compose.yml index 135ab08..11391cd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,18 +8,18 @@ services: MYSQL_ROOT_PASSWORD: "pass" MYSQL_DATABASE: "cre" ports: - - 3306:3306 + - "3306:3306" backend: - image: fyloz.dev:5443/color-recipes-explorer/backend:master + image: registry.fyloz.dev:5443/colorrecipesexplorer/backend:latest environment: spring_profiles_active: "mysql,debug" cre_database_url: "mysql://database:3306/cre" cre_database_username: "root" cre_database_password: "pass" - CRE_ENABLE_DB_UPDATE: 0 + CRE_ENABLE_DB_UPDATE: 1 server_port: 9090 ports: - - 9090:9090 + - "9090:9090" volumes: - cre_data:/usr/bin/cre/data - cre_config:/usr/bin/cre/config diff --git a/src/_variables.scss b/src/_variables.scss new file mode 100644 index 0000000..4ff7528 --- /dev/null +++ b/src/_variables.scss @@ -0,0 +1,20 @@ +@import "assets/sass/modules/fonts"; +@import "custom-theme"; +@import "~material-design-icons/iconfont/material-icons.css"; + +// Spacing +$spacer: 1rem; +$spacers: ( + 1: $spacer * 0.5, + 2: $spacer * 0.75, + 3: $spacer, + 4: $spacer * 1.5, + 5: $spacer * 2 +); + +// Colors +$color-primary: map-get($theme-primary, 500); +$text-color-primary: white; + +$color-accent: map-get($theme-accent, 500); +$color-warn: map-get($theme-error, 500); diff --git a/src/app/modules/colors/components/mix-table/mix-table.component.sass b/src/app/modules/colors/components/mix-table/mix-table.component.sass index 927de1b..1f8c7e4 100644 --- a/src/app/modules/colors/components/mix-table/mix-table.component.sass +++ b/src/app/modules/colors/components/mix-table/mix-table.component.sass @@ -1,4 +1,4 @@ -@import '../../../../../custom-theme' +@import "~src/variables" mat-expansion-panel width: 48rem diff --git a/src/app/modules/configuration/config.module.ts b/src/app/modules/configuration/config.module.ts index 8127ec2..6d16826 100644 --- a/src/app/modules/configuration/config.module.ts +++ b/src/app/modules/configuration/config.module.ts @@ -7,7 +7,7 @@ import { CreImageConfig, CreConfigList, CreConfigActions, - CreConfigTooltip, CrePeriodConfig, CreBoolConfig, CreDateConfig + CreConfigTooltip, CrePeriodConfig, CreBoolConfig, CreDateConfig, CreSecureConfig } from './config' import {SharedModule} from '../shared/shared.module' import {CreInputsModule} from '../shared/components/inputs/inputs.module' @@ -26,7 +26,8 @@ import {CreButtonsModule} from '../shared/components/buttons/buttons.module' CreConfigActions, CreBoolConfig, CrePeriodConfig, - CreDateConfig + CreDateConfig, + CreSecureConfig ], imports: [ SharedModule, diff --git a/src/app/modules/configuration/config.sass b/src/app/modules/configuration/config.sass index 6812169..062d34e 100644 --- a/src/app/modules/configuration/config.sass +++ b/src/app/modules/configuration/config.sass @@ -50,3 +50,6 @@ cre-image-config mat-hint margin-top: .2em + +//cre-secure-config button +// diff --git a/src/app/modules/configuration/config.ts b/src/app/modules/configuration/config.ts index a40e7de..9ba1d5b 100644 --- a/src/app/modules/configuration/config.ts +++ b/src/app/modules/configuration/config.ts @@ -18,7 +18,8 @@ import {ActivatedRoute, Router} from '@angular/router' import {formatDate, formatDateTime, getFileUrl, readFile} from '../shared/utils/utils' import {FormControl, Validators} from '@angular/forms' import {ConfirmBoxComponent} from '../shared/components/confirm-box/confirm-box.component' -import {environment} from '../../../environments/environment' +import {MatDialog} from '@angular/material/dialog' +import {CrePromptDialog} from '../shared/components/dialogs/dialogs' @Directive({ selector: 'cre-config-label' @@ -108,7 +109,7 @@ export class CreConfig extends SubscribingComponent { ) } - setConfig(config: Config) { + protected setConfig(config: Config) { this.configuration = config this.config.control.setValue(config.content) if (!config.editable) { @@ -158,7 +159,7 @@ export class CreImageConfig extends CreConfig { templateUrl: 'bool.html' }) export class CreBoolConfig extends CreConfig { - setConfig(config: Config) { + protected setConfig(config: Config) { super.setConfig(config) this.config.control.setValue(config.content === 'true') } @@ -176,12 +177,46 @@ export class CrePeriodConfig extends CreConfig { templateUrl: 'date.html' }) export class CreDateConfig extends CreConfig { - setConfig(config: Config) { - super.setConfig(config); + protected setConfig(config: Config) { + super.setConfig(config) this.config.control.setValue(formatDate(config.content)) } } +@Component({ + selector: 'cre-secure-config', + templateUrl: 'secure.html' +}) +export class CreSecureConfig extends CreConfig { + @ViewChild(CrePromptDialog) dialog: CrePromptDialog + + @Input() buttonLabel: string + + private initialValue: string | null + + constructor( + configService: ConfigService, + errorService: ErrorService, + activatedRoute: ActivatedRoute, + router: Router + ) { + super(configService, errorService, activatedRoute, router); + } + + protected setConfig(config: Config) { + super.setConfig(config) + } + + onOpen() { + this.initialValue = this.config.control.value + this.dialog.show() + } + + onCancel() { + this.config.control.setValue(this.initialValue) + } +} + @Component({ selector: 'cre-config-editor', templateUrl: 'editor.html' @@ -218,7 +253,7 @@ export class CreConfigEditor extends ErrorHandlingComponent { super(errorService, activatedRoute, router) for (let key in this.keys) { - this.controls[this.keys[key]] = new FormControl(null, Validators.required) + this.controls.set(this.keys[key], new FormControl(null, Validators.required)) } } @@ -232,7 +267,7 @@ export class CreConfigEditor extends ErrorHandlingComponent { } getConfig(key: string) { - return {key, control: this.controls[key]} + return {key, control: this.controls.get(key)} } save() { diff --git a/src/app/modules/configuration/editor.html b/src/app/modules/configuration/editor.html index 66cd181..ec78574 100644 --- a/src/app/modules/configuration/editor.html +++ b/src/app/modules/configuration/editor.html @@ -11,12 +11,12 @@ Apparence - - - - - - + + + + + + @@ -50,7 +50,8 @@ Période d'expiration des kits de retouches complets - Les kits de retouche complétés expirent après la période configurée. Les kits de retouche expirés seront supprimés automatiquement. + Les kits de retouche complétés expirent après la période configurée. Les kits de retouche expirés seront + supprimés automatiquement. @@ -83,9 +84,15 @@ Utilisateur de la base de données - + + + + + Mot de passe de la base de données - + Version de la base de données @@ -114,7 +121,8 @@ - diff --git a/src/app/modules/configuration/secure.html b/src/app/modules/configuration/secure.html new file mode 100644 index 0000000..1c1fcae --- /dev/null +++ b/src/app/modules/configuration/secure.html @@ -0,0 +1,25 @@ +
+ + {{buttonLabel}} + + + + + + + + +
diff --git a/src/app/modules/shared/components/buttons/buttons.sass b/src/app/modules/shared/components/buttons/buttons.sass new file mode 100644 index 0000000..3c36340 --- /dev/null +++ b/src/app/modules/shared/components/buttons/buttons.sass @@ -0,0 +1,6 @@ +cre-button, cre-primary-button, cre-accent-button, cre-warn-button + display: inline-block + width: inherit + + button + width: 100% diff --git a/src/app/modules/shared/components/buttons/buttons.ts b/src/app/modules/shared/components/buttons/buttons.ts index 7b3679e..a388f90 100644 --- a/src/app/modules/shared/components/buttons/buttons.ts +++ b/src/app/modules/shared/components/buttons/buttons.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core' +import {Component, Input, ViewEncapsulation} from '@angular/core' import {ThemePalette} from '@angular/material/core' @Component({ @@ -7,7 +7,9 @@ import {ThemePalette} from '@angular/material/core' - ` + `, + styleUrls: ['buttons.sass'], + encapsulation: ViewEncapsulation.None }) export class CreButtonComponent { @Input() color: ThemePalette @@ -20,7 +22,8 @@ export class CreButtonComponent { - ` + `, + styleUrls: ['buttons.sass'] }) export class CrePrimaryButtonComponent { @Input() disabled = false @@ -32,7 +35,8 @@ export class CrePrimaryButtonComponent { - ` + `, + styleUrls: ['buttons.sass'] }) export class CreAccentButtonComponent { @Input() disabled = false @@ -44,7 +48,8 @@ export class CreAccentButtonComponent { - ` + `, + styleUrls: ['buttons.sass'] }) export class CreWarnButtonComponent { @Input() disabled = false diff --git a/src/app/modules/shared/components/dialogs/dialogs.module.ts b/src/app/modules/shared/components/dialogs/dialogs.module.ts new file mode 100644 index 0000000..ac98cdc --- /dev/null +++ b/src/app/modules/shared/components/dialogs/dialogs.module.ts @@ -0,0 +1,21 @@ +import {NgModule} from '@angular/core' +import {CreDialogBody, CrePromptDialog} from './dialogs' +import {MatDialogModule} from '@angular/material/dialog' +import {CreButtonsModule} from '../buttons/buttons.module' + +@NgModule({ + declarations: [ + CrePromptDialog, + CreDialogBody + ], + exports: [ + CrePromptDialog, + CreDialogBody + ], + imports: [ + MatDialogModule, + CreButtonsModule + ] +}) +export class CreDialogsModule { +} diff --git a/src/app/modules/shared/components/dialogs/dialogs.scss b/src/app/modules/shared/components/dialogs/dialogs.scss new file mode 100644 index 0000000..3653e6c --- /dev/null +++ b/src/app/modules/shared/components/dialogs/dialogs.scss @@ -0,0 +1,26 @@ +@import "~src/variables"; + +.cre-dialog-panel { + min-width: 20rem; + + mat-dialog-container { + padding: 0; + + .mat-dialog-title, .mat-dialog-content, .mat-dialog-actions { + margin: 0; + padding: $spacer; + } + + .mat-dialog-title { + background-color: $color-primary; + color: $text-color-primary; + } + + .mat-dialog-actions { + min-height: auto; + justify-content: end; + gap: map-get($spacers, 1); + padding-top: 0; + } + } +} diff --git a/src/app/modules/shared/components/dialogs/dialogs.ts b/src/app/modules/shared/components/dialogs/dialogs.ts new file mode 100644 index 0000000..a06f602 --- /dev/null +++ b/src/app/modules/shared/components/dialogs/dialogs.ts @@ -0,0 +1,74 @@ +import {Component, Directive, EventEmitter, Input, Output, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core' +import {MatDialog, MatDialogRef} from '@angular/material/dialog' + +@Directive({ + selector: 'cre-dialog-body' +}) +export class CreDialogBody { +} + +@Directive() +abstract class CreDialog { + @ViewChild(TemplateRef) dialogTemplate: TemplateRef + + @Output() cancel = new EventEmitter(); + @Output() continue = new EventEmitter(); + + private dialogRef: MatDialogRef> | null + + constructor( + protected dialog: MatDialog + ) { + } + + protected abstract get data(): D + + show() { + this.open() + } + + onCancel() { + this.close() + this.cancel.emit(); + } + + onContinue() { + this.close() + this.continue.emit(); + } + + private open() { + const config = { + panelClass: 'cre-dialog-panel', + data: this.data + } + this.dialogRef = this.dialog.open(this.dialogTemplate, config) + } + + private close() { + this.dialogRef.close() + } +} + +@Component({ + selector: 'cre-prompt-dialog', + templateUrl: 'prompt.html', + styleUrls: ['dialogs.scss'], + encapsulation: ViewEncapsulation.None +}) +export class CrePromptDialog extends CreDialog { + @Input() title: string + + protected get data(): CrePromptDialogData { + return { + title: this.title + } + } +} + +abstract class CreDialogData { + title: string +} + +class CrePromptDialogData extends CreDialogData { +} diff --git a/src/app/modules/shared/components/dialogs/prompt.html b/src/app/modules/shared/components/dialogs/prompt.html new file mode 100644 index 0000000..0d14939 --- /dev/null +++ b/src/app/modules/shared/components/dialogs/prompt.html @@ -0,0 +1,10 @@ + +

{{data.title}}

+
+ +
+
+ Annuler + Continuer +
+
diff --git a/src/app/modules/shared/components/info-banner/info-banner.component.sass b/src/app/modules/shared/components/info-banner/info-banner.component.sass index 558af2c..31c5568 100644 --- a/src/app/modules/shared/components/info-banner/info-banner.component.sass +++ b/src/app/modules/shared/components/info-banner/info-banner.component.sass @@ -1,4 +1,4 @@ -@import "~src/custom-theme" +@import "~src/variables" .info-banner-wrapper background-color: $color-primary diff --git a/src/app/modules/shared/components/tables/table.sass b/src/app/modules/shared/components/tables/table.sass index 4458b6e..9cbb67d 100644 --- a/src/app/modules/shared/components/tables/table.sass +++ b/src/app/modules/shared/components/tables/table.sass @@ -1,4 +1,4 @@ -@import "~src/custom-theme" +@import "../../../../../custom-theme" cre-table display: block diff --git a/src/app/modules/shared/components/user-info/user-menu.component.sass b/src/app/modules/shared/components/user-info/user-menu.component.sass index d3ffcd7..329544c 100644 --- a/src/app/modules/shared/components/user-info/user-menu.component.sass +++ b/src/app/modules/shared/components/user-info/user-menu.component.sass @@ -1,4 +1,4 @@ -@import "../../../../../custom-theme" +@import "~src/variables" p, labeled-icon margin: 0 diff --git a/src/app/modules/shared/model/config.model.ts b/src/app/modules/shared/model/config.model.ts index 642a736..80a559a 100644 --- a/src/app/modules/shared/model/config.model.ts +++ b/src/app/modules/shared/model/config.model.ts @@ -1,3 +1,6 @@ +import {Form, FormControl} from '@angular/forms' +import {filterMap} from '../utils/map.utils' + export class Config { static readonly INSTANCE_NAME = 'instance.name' static readonly INSTANCE_LOGO_PATH = 'instance.logo.path' @@ -16,12 +19,46 @@ export class Config { static readonly JAVA_VERSION = 'env.java.version' static readonly OPERATING_SYSTEM = 'env.os' - constructor( - public key: string, - public content: string, - public lastUpdated: string, - public requireRestart: boolean, - public editable: boolean - ) { - } + static readonly IMAGE_CONFIG_KEYS = [ + Config.INSTANCE_LOGO_PATH, + Config.INSTANCE_ICON_PATH + ] + + public key: string + public requireRestart: boolean + public editable: boolean + public content?: string + public lastUpdated?: string +} + +export class ConfigKeyContent { + public key: string + public content: string +} + +export function filterConfigKeyControlMap(map: Map): Map { + return filterMap(map, (key, control) => { + return control.dirty && + Config.IMAGE_CONFIG_KEYS.indexOf(key) < 0 && // Filter image configs because they are sent to a different endpoint + control.value !== undefined && + control.value !== null + }) +} + +export function filterImageConfigKeyControlMap(map: Map): Map { + return filterMap(map, (key, control) => { + return Config.IMAGE_CONFIG_KEYS.indexOf(key) >= 0 && control.dirty + }) +} + +export function mapToConfigKeyContent(key: string, control: FormControl): ConfigKeyContent { + return {key, content: control.value} +} + +export function mapToConfigKeyContentArray(map: Map): ConfigKeyContent[] { + const array: ConfigKeyContent[] = [] + map.forEach((control, key) => { + array.push(mapToConfigKeyContent(key, control)) + }) + return array } diff --git a/src/app/modules/shared/service/config.service.ts b/src/app/modules/shared/service/config.service.ts index 880b697..7ebe376 100644 --- a/src/app/modules/shared/service/config.service.ts +++ b/src/app/modules/shared/service/config.service.ts @@ -1,13 +1,9 @@ import {Injectable} from '@angular/core' -import {Config} from '../model/config.model' +import {Config, filterConfigKeyControlMap, filterImageConfigKeyControlMap, mapToConfigKeyContentArray} from '../model/config.model' import {Observable} from 'rxjs' import {ApiService} from './api.service' import {FormControl} from '@angular/forms' - -const imageConfigsKeys = [ - Config.INSTANCE_LOGO_PATH, - Config.INSTANCE_ICON_PATH -] +import {transformMap} from '../utils/map.utils' @Injectable({ providedIn: 'root' @@ -23,27 +19,10 @@ export class ConfigService { } set(configs: Map): Observable { - const body = [] - for (let key in configs) { - const control = configs[key] - if (control.dirty && key.indexOf('path') < 0) { - body.push({key, content: control.value}) - } - } - - const subscriptions = [] - imageConfigsKeys.forEach(key => { - if (configs[key].dirty) { - subscriptions.push(this.setImage(key, configs[key].value)) - } - }) - - while (subscriptions.length > 0) { - const subscription = subscriptions.pop().subscribe({ - next: () => subscription.unsubscribe() - }) - } + const body = mapToConfigKeyContentArray(filterConfigKeyControlMap(configs)) + const imageConfigs = filterImageConfigKeyControlMap(configs) + this.setImages(imageConfigs) return this.api.put('/config', body) } @@ -58,4 +37,19 @@ export class ConfigService { restart(): Observable { return this.api.post('/config/restart') } + + private setImages(configs: Map) { + const subscriptions = this.getImageConfigsSubscriptions(configs) + while (subscriptions.length > 0) { + const subscription = subscriptions.pop().subscribe({ + next: () => subscription.unsubscribe() + }) + } + } + + private getImageConfigsSubscriptions(configs: Map): Observable[] { + return transformMap(configs, (key, control) => { + return this.setImage(key, control.value) + }) + } } diff --git a/src/app/modules/shared/shared.module.ts b/src/app/modules/shared/shared.module.ts index 1d7d62e..05aaf24 100644 --- a/src/app/modules/shared/shared.module.ts +++ b/src/app/modules/shared/shared.module.ts @@ -36,6 +36,7 @@ import {InfoBannerModule} from './components/info-banner/info-banner.module' import {CreFormsModule} from './components/forms/forms.module' import {VarDirective} from './directives/var.directive' import {CreColorPreview} from './components/color-preview/color-preview' +import {CreDialogsModule} from './components/dialogs/dialogs.module' @NgModule({ declarations: [VarDirective, HeaderComponent, UserMenuComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent, LoadingWheelComponent, CreColorPreview], @@ -71,7 +72,8 @@ import {CreColorPreview} from './components/color-preview/color-preview' InfoBannerModule, CreFormsModule, VarDirective, - CreColorPreview + CreColorPreview, + CreDialogsModule ], imports: [ MatTabsModule, diff --git a/src/app/modules/shared/utils/map.utils.ts b/src/app/modules/shared/utils/map.utils.ts new file mode 100644 index 0000000..ba99aec --- /dev/null +++ b/src/app/modules/shared/utils/map.utils.ts @@ -0,0 +1,17 @@ +export function filterMap(map: Map, predicate: (key: K, value: V) => boolean): Map { + const filteredMap = new Map() + map.forEach((value, key) => { + if (predicate(key, value)) { + filteredMap.set(key, value) + } + }) + return filteredMap +} + +export function transformMap(map: Map, transform: (key: K, value: V) => T): T[] { + const transformedArray = [] + map.forEach((value, key) => { + transformedArray.push(transform(key, value)) + }) + return transformedArray +} diff --git a/src/app/modules/touch-up-kit/components/finish.sass b/src/app/modules/touch-up-kit/components/finish.sass index 899e060..30c9991 100644 --- a/src/app/modules/touch-up-kit/components/finish.sass +++ b/src/app/modules/touch-up-kit/components/finish.sass @@ -1,4 +1,4 @@ -@import '~src/custom-theme' +@import '../../../../custom-theme' .touchupkit-finish-container display: inline-block diff --git a/src/custom-theme.scss b/src/custom-theme.scss index 5f9a53b..5f766ef 100644 --- a/src/custom-theme.scss +++ b/src/custom-theme.scss @@ -122,10 +122,6 @@ $color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-a // that you are using. @include angular-material-theme($color-recipes-explorer-frontend-theme); -$color-primary: map-get($theme-primary, 500); -$color-accent: map-get($theme-accent, 500); -$color-warn: map-get($theme-error, 500); - html, body { height: 100%; diff --git a/src/styles.sass b/src/styles.sass index e026f75..c096222 100644 --- a/src/styles.sass +++ b/src/styles.sass @@ -1,6 +1,4 @@ -@import 'assets/sass/modules/_fonts.sass' -@import "custom-theme" -@import "~material-design-icons/iconfont/material-icons.css" +@import "variables" mat-card padding: 0 !important