#2 Ajout du support pour les configuration sécurisées
This commit is contained in:
parent
68d8a2e0da
commit
28cf2d04cd
|
@ -8,18 +8,18 @@ services:
|
||||||
MYSQL_ROOT_PASSWORD: "pass"
|
MYSQL_ROOT_PASSWORD: "pass"
|
||||||
MYSQL_DATABASE: "cre"
|
MYSQL_DATABASE: "cre"
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- "3306:3306"
|
||||||
backend:
|
backend:
|
||||||
image: fyloz.dev:5443/color-recipes-explorer/backend:master
|
image: registry.fyloz.dev:5443/colorrecipesexplorer/backend:latest
|
||||||
environment:
|
environment:
|
||||||
spring_profiles_active: "mysql,debug"
|
spring_profiles_active: "mysql,debug"
|
||||||
cre_database_url: "mysql://database:3306/cre"
|
cre_database_url: "mysql://database:3306/cre"
|
||||||
cre_database_username: "root"
|
cre_database_username: "root"
|
||||||
cre_database_password: "pass"
|
cre_database_password: "pass"
|
||||||
CRE_ENABLE_DB_UPDATE: 0
|
CRE_ENABLE_DB_UPDATE: 1
|
||||||
server_port: 9090
|
server_port: 9090
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- "9090:9090"
|
||||||
volumes:
|
volumes:
|
||||||
- cre_data:/usr/bin/cre/data
|
- cre_data:/usr/bin/cre/data
|
||||||
- cre_config:/usr/bin/cre/config
|
- cre_config:/usr/bin/cre/config
|
||||||
|
|
|
@ -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);
|
|
@ -1,4 +1,4 @@
|
||||||
@import '../../../../../custom-theme'
|
@import "~src/variables"
|
||||||
|
|
||||||
mat-expansion-panel
|
mat-expansion-panel
|
||||||
width: 48rem
|
width: 48rem
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
CreImageConfig,
|
CreImageConfig,
|
||||||
CreConfigList,
|
CreConfigList,
|
||||||
CreConfigActions,
|
CreConfigActions,
|
||||||
CreConfigTooltip, CrePeriodConfig, CreBoolConfig, CreDateConfig
|
CreConfigTooltip, CrePeriodConfig, CreBoolConfig, CreDateConfig, CreSecureConfig
|
||||||
} from './config'
|
} from './config'
|
||||||
import {SharedModule} from '../shared/shared.module'
|
import {SharedModule} from '../shared/shared.module'
|
||||||
import {CreInputsModule} from '../shared/components/inputs/inputs.module'
|
import {CreInputsModule} from '../shared/components/inputs/inputs.module'
|
||||||
|
@ -26,7 +26,8 @@ import {CreButtonsModule} from '../shared/components/buttons/buttons.module'
|
||||||
CreConfigActions,
|
CreConfigActions,
|
||||||
CreBoolConfig,
|
CreBoolConfig,
|
||||||
CrePeriodConfig,
|
CrePeriodConfig,
|
||||||
CreDateConfig
|
CreDateConfig,
|
||||||
|
CreSecureConfig
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
|
|
@ -50,3 +50,6 @@ cre-image-config
|
||||||
|
|
||||||
mat-hint
|
mat-hint
|
||||||
margin-top: .2em
|
margin-top: .2em
|
||||||
|
|
||||||
|
//cre-secure-config button
|
||||||
|
//
|
||||||
|
|
|
@ -18,7 +18,8 @@ import {ActivatedRoute, Router} from '@angular/router'
|
||||||
import {formatDate, formatDateTime, getFileUrl, readFile} from '../shared/utils/utils'
|
import {formatDate, formatDateTime, getFileUrl, readFile} from '../shared/utils/utils'
|
||||||
import {FormControl, Validators} from '@angular/forms'
|
import {FormControl, Validators} from '@angular/forms'
|
||||||
import {ConfirmBoxComponent} from '../shared/components/confirm-box/confirm-box.component'
|
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({
|
@Directive({
|
||||||
selector: 'cre-config-label'
|
selector: 'cre-config-label'
|
||||||
|
@ -108,7 +109,7 @@ export class CreConfig extends SubscribingComponent {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config: Config) {
|
protected setConfig(config: Config) {
|
||||||
this.configuration = config
|
this.configuration = config
|
||||||
this.config.control.setValue(config.content)
|
this.config.control.setValue(config.content)
|
||||||
if (!config.editable) {
|
if (!config.editable) {
|
||||||
|
@ -158,7 +159,7 @@ export class CreImageConfig extends CreConfig {
|
||||||
templateUrl: 'bool.html'
|
templateUrl: 'bool.html'
|
||||||
})
|
})
|
||||||
export class CreBoolConfig extends CreConfig {
|
export class CreBoolConfig extends CreConfig {
|
||||||
setConfig(config: Config) {
|
protected setConfig(config: Config) {
|
||||||
super.setConfig(config)
|
super.setConfig(config)
|
||||||
this.config.control.setValue(config.content === 'true')
|
this.config.control.setValue(config.content === 'true')
|
||||||
}
|
}
|
||||||
|
@ -176,12 +177,46 @@ export class CrePeriodConfig extends CreConfig {
|
||||||
templateUrl: 'date.html'
|
templateUrl: 'date.html'
|
||||||
})
|
})
|
||||||
export class CreDateConfig extends CreConfig {
|
export class CreDateConfig extends CreConfig {
|
||||||
setConfig(config: Config) {
|
protected setConfig(config: Config) {
|
||||||
super.setConfig(config);
|
super.setConfig(config)
|
||||||
this.config.control.setValue(formatDate(config.content))
|
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({
|
@Component({
|
||||||
selector: 'cre-config-editor',
|
selector: 'cre-config-editor',
|
||||||
templateUrl: 'editor.html'
|
templateUrl: 'editor.html'
|
||||||
|
@ -218,7 +253,7 @@ export class CreConfigEditor extends ErrorHandlingComponent {
|
||||||
super(errorService, activatedRoute, router)
|
super(errorService, activatedRoute, router)
|
||||||
|
|
||||||
for (let key in this.keys) {
|
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) {
|
getConfig(key: string) {
|
||||||
return {key, control: this.controls[key]}
|
return {key, control: this.controls.get(key)}
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
|
|
|
@ -11,12 +11,12 @@
|
||||||
<cre-config-section *ngIf="emergencyMode === 'false'">
|
<cre-config-section *ngIf="emergencyMode === 'false'">
|
||||||
<cre-config-label>Apparence</cre-config-label>
|
<cre-config-label>Apparence</cre-config-label>
|
||||||
<cre-config-list>
|
<cre-config-list>
|
||||||
<!-- <cre-config [config]="getConfig(keys.INSTANCE_NAME)">-->
|
<!-- <cre-config [config]="getConfig(keys.INSTANCE_NAME)">-->
|
||||||
<!-- <cre-config-label>Nom de l'instance</cre-config-label>-->
|
<!-- <cre-config-label>Nom de l'instance</cre-config-label>-->
|
||||||
<!-- <cre-config-tooltip>-->
|
<!-- <cre-config-tooltip>-->
|
||||||
<!-- Affiché dans la barre de titre du navigateur ou en survolant l'onglet de la page dans le navigateur.-->
|
<!-- Affiché dans la barre de titre du navigateur ou en survolant l'onglet de la page dans le navigateur.-->
|
||||||
<!-- </cre-config-tooltip>-->
|
<!-- </cre-config-tooltip>-->
|
||||||
<!-- </cre-config>-->
|
<!-- </cre-config>-->
|
||||||
|
|
||||||
<cre-image-config [config]="getConfig(keys.INSTANCE_LOGO_PATH)" previewWidth="170px"
|
<cre-image-config [config]="getConfig(keys.INSTANCE_LOGO_PATH)" previewWidth="170px"
|
||||||
(invalidFormat)="invalidFormatConfirmBox.show()">
|
(invalidFormat)="invalidFormatConfirmBox.show()">
|
||||||
|
@ -50,7 +50,8 @@
|
||||||
<cre-period-config [config]="getConfig(keys.TOUCH_UP_KIT_EXPIRATION)">
|
<cre-period-config [config]="getConfig(keys.TOUCH_UP_KIT_EXPIRATION)">
|
||||||
<cre-config-label>Période d'expiration des kits de retouches complets</cre-config-label>
|
<cre-config-label>Période d'expiration des kits de retouches complets</cre-config-label>
|
||||||
<cre-config-tooltip>
|
<cre-config-tooltip>
|
||||||
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.
|
||||||
</cre-config-tooltip>
|
</cre-config-tooltip>
|
||||||
</cre-period-config>
|
</cre-period-config>
|
||||||
|
|
||||||
|
@ -83,9 +84,15 @@
|
||||||
<cre-config-label>Utilisateur de la base de données</cre-config-label>
|
<cre-config-label>Utilisateur de la base de données</cre-config-label>
|
||||||
</cre-config>
|
</cre-config>
|
||||||
|
|
||||||
<cre-config [config]="getConfig(keys.DATABASE_PASSWORD)">
|
<!-- <cre-config [config]="getConfig(keys.DATABASE_PASSWORD)">-->
|
||||||
|
<!-- <cre-config-label>Mot de passe de la base de données</cre-config-label>-->
|
||||||
|
<!-- </cre-config>-->
|
||||||
|
|
||||||
|
<cre-secure-config
|
||||||
|
[config]="getConfig(keys.DATABASE_PASSWORD)"
|
||||||
|
buttonLabel="Modifier le mot de passe de la base de données">
|
||||||
<cre-config-label>Mot de passe de la base de données</cre-config-label>
|
<cre-config-label>Mot de passe de la base de données</cre-config-label>
|
||||||
</cre-config>
|
</cre-secure-config>
|
||||||
|
|
||||||
<cre-config [config]="getConfig(keys.DATABASE_VERSION)">
|
<cre-config [config]="getConfig(keys.DATABASE_VERSION)">
|
||||||
<cre-config-label>Version de la base de données</cre-config-label>
|
<cre-config-label>Version de la base de données</cre-config-label>
|
||||||
|
@ -114,7 +121,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<cre-confirm-box #invalidFormatConfirmBox message="Le format du fichier choisi n'est pas valide"></cre-confirm-box>
|
<cre-confirm-box #invalidFormatConfirmBox message="Le format du fichier choisi n'est pas valide"></cre-confirm-box>
|
||||||
<cre-confirm-box #restartConfirmBox message="Voulez-vous vraiment redémarrer le serveur? Les changements nécessitant un redémarrage seront appliqués."
|
<cre-confirm-box #restartConfirmBox
|
||||||
|
message="Voulez-vous vraiment redémarrer le serveur? Les changements nécessitant un redémarrage seront appliqués."
|
||||||
(confirm)="restart()"></cre-confirm-box>
|
(confirm)="restart()"></cre-confirm-box>
|
||||||
<cre-confirm-box #restartingConfirmBox message="Le serveur est en cours de redémarrage" (cancel)="reload()"
|
<cre-confirm-box #restartingConfirmBox message="Le serveur est en cours de redémarrage" (cancel)="reload()"
|
||||||
(confirm)="reload()"></cre-confirm-box>
|
(confirm)="reload()"></cre-confirm-box>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div *ngIf="configuration" [attr.title]="tooltip?.content">
|
||||||
|
<cre-primary-button
|
||||||
|
class="w-100 mb-3"
|
||||||
|
(click)="onOpen()">
|
||||||
|
{{buttonLabel}}
|
||||||
|
</cre-primary-button>
|
||||||
|
|
||||||
|
<cre-prompt-dialog
|
||||||
|
[title]="label.content"
|
||||||
|
(cancel)="onCancel()">
|
||||||
|
<cre-dialog-body>
|
||||||
|
<cre-input
|
||||||
|
[class.has-hint]="configuration.editable"
|
||||||
|
class="w-100"
|
||||||
|
type="password"
|
||||||
|
label="Nouvelle valeur"
|
||||||
|
[hint]="configuration.editable ? lastUpdated : null"
|
||||||
|
[control]="config.control"
|
||||||
|
[icon]="configuration.requireRestart ? 'alert' : null"
|
||||||
|
[iconTitle]="configuration.requireRestart ? 'Requiert un redémarrage' : null"
|
||||||
|
iconColor="warning">
|
||||||
|
</cre-input>
|
||||||
|
</cre-dialog-body>
|
||||||
|
</cre-prompt-dialog>
|
||||||
|
</div>
|
|
@ -0,0 +1,6 @@
|
||||||
|
cre-button, cre-primary-button, cre-accent-button, cre-warn-button
|
||||||
|
display: inline-block
|
||||||
|
width: inherit
|
||||||
|
|
||||||
|
button
|
||||||
|
width: 100%
|
|
@ -1,4 +1,4 @@
|
||||||
import {Component, Input} from '@angular/core'
|
import {Component, Input, ViewEncapsulation} from '@angular/core'
|
||||||
import {ThemePalette} from '@angular/material/core'
|
import {ThemePalette} from '@angular/material/core'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -7,7 +7,9 @@ import {ThemePalette} from '@angular/material/core'
|
||||||
<button mat-raised-button [color]="color" [disabled]="disabled">
|
<button mat-raised-button [color]="color" [disabled]="disabled">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</button>
|
</button>
|
||||||
`
|
`,
|
||||||
|
styleUrls: ['buttons.sass'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class CreButtonComponent {
|
export class CreButtonComponent {
|
||||||
@Input() color: ThemePalette
|
@Input() color: ThemePalette
|
||||||
|
@ -20,7 +22,8 @@ export class CreButtonComponent {
|
||||||
<cre-button color="primary" [disabled]="disabled">
|
<cre-button color="primary" [disabled]="disabled">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</cre-button>
|
</cre-button>
|
||||||
`
|
`,
|
||||||
|
styleUrls: ['buttons.sass']
|
||||||
})
|
})
|
||||||
export class CrePrimaryButtonComponent {
|
export class CrePrimaryButtonComponent {
|
||||||
@Input() disabled = false
|
@Input() disabled = false
|
||||||
|
@ -32,7 +35,8 @@ export class CrePrimaryButtonComponent {
|
||||||
<cre-button color="accent" [disabled]="disabled">
|
<cre-button color="accent" [disabled]="disabled">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</cre-button>
|
</cre-button>
|
||||||
`
|
`,
|
||||||
|
styleUrls: ['buttons.sass']
|
||||||
})
|
})
|
||||||
export class CreAccentButtonComponent {
|
export class CreAccentButtonComponent {
|
||||||
@Input() disabled = false
|
@Input() disabled = false
|
||||||
|
@ -44,7 +48,8 @@ export class CreAccentButtonComponent {
|
||||||
<cre-button color="warn" [disabled]="disabled">
|
<cre-button color="warn" [disabled]="disabled">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</cre-button>
|
</cre-button>
|
||||||
`
|
`,
|
||||||
|
styleUrls: ['buttons.sass']
|
||||||
})
|
})
|
||||||
export class CreWarnButtonComponent {
|
export class CreWarnButtonComponent {
|
||||||
@Input() disabled = false
|
@Input() disabled = false
|
||||||
|
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<D = CreDialogData> {
|
||||||
|
@ViewChild(TemplateRef) dialogTemplate: TemplateRef<any>
|
||||||
|
|
||||||
|
@Output() cancel = new EventEmitter<void>();
|
||||||
|
@Output() continue = new EventEmitter<void>();
|
||||||
|
|
||||||
|
private dialogRef: MatDialogRef<TemplateRef<any>> | 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<CrePromptDialogData> {
|
||||||
|
@Input() title: string
|
||||||
|
|
||||||
|
protected get data(): CrePromptDialogData {
|
||||||
|
return {
|
||||||
|
title: this.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CreDialogData {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class CrePromptDialogData extends CreDialogData {
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<ng-template let-data>
|
||||||
|
<h1 mat-dialog-title>{{data.title}}</h1>
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<ng-content select="cre-dialog-body"></ng-content>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<cre-primary-button (click)="onCancel()">Annuler</cre-primary-button>
|
||||||
|
<cre-accent-button (click)="onContinue()">Continuer</cre-accent-button>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
|
@ -1,4 +1,4 @@
|
||||||
@import "~src/custom-theme"
|
@import "~src/variables"
|
||||||
|
|
||||||
.info-banner-wrapper
|
.info-banner-wrapper
|
||||||
background-color: $color-primary
|
background-color: $color-primary
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "~src/custom-theme"
|
@import "../../../../../custom-theme"
|
||||||
|
|
||||||
cre-table
|
cre-table
|
||||||
display: block
|
display: block
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "../../../../../custom-theme"
|
@import "~src/variables"
|
||||||
|
|
||||||
p, labeled-icon
|
p, labeled-icon
|
||||||
margin: 0
|
margin: 0
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import {Form, FormControl} from '@angular/forms'
|
||||||
|
import {filterMap} from '../utils/map.utils'
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
static readonly INSTANCE_NAME = 'instance.name'
|
static readonly INSTANCE_NAME = 'instance.name'
|
||||||
static readonly INSTANCE_LOGO_PATH = 'instance.logo.path'
|
static readonly INSTANCE_LOGO_PATH = 'instance.logo.path'
|
||||||
|
@ -16,12 +19,46 @@ export class Config {
|
||||||
static readonly JAVA_VERSION = 'env.java.version'
|
static readonly JAVA_VERSION = 'env.java.version'
|
||||||
static readonly OPERATING_SYSTEM = 'env.os'
|
static readonly OPERATING_SYSTEM = 'env.os'
|
||||||
|
|
||||||
constructor(
|
static readonly IMAGE_CONFIG_KEYS = [
|
||||||
public key: string,
|
Config.INSTANCE_LOGO_PATH,
|
||||||
public content: string,
|
Config.INSTANCE_ICON_PATH
|
||||||
public lastUpdated: string,
|
]
|
||||||
public requireRestart: boolean,
|
|
||||||
public editable: boolean
|
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<string, FormControl>): Map<string, FormControl> {
|
||||||
|
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<string, FormControl>): Map<string, FormControl> {
|
||||||
|
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<string, FormControl>): ConfigKeyContent[] {
|
||||||
|
const array: ConfigKeyContent[] = []
|
||||||
|
map.forEach((control, key) => {
|
||||||
|
array.push(mapToConfigKeyContent(key, control))
|
||||||
|
})
|
||||||
|
return array
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import {Injectable} from '@angular/core'
|
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 {Observable} from 'rxjs'
|
||||||
import {ApiService} from './api.service'
|
import {ApiService} from './api.service'
|
||||||
import {FormControl} from '@angular/forms'
|
import {FormControl} from '@angular/forms'
|
||||||
|
import {transformMap} from '../utils/map.utils'
|
||||||
const imageConfigsKeys = [
|
|
||||||
Config.INSTANCE_LOGO_PATH,
|
|
||||||
Config.INSTANCE_ICON_PATH
|
|
||||||
]
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -23,27 +19,10 @@ export class ConfigService {
|
||||||
}
|
}
|
||||||
|
|
||||||
set(configs: Map<string, FormControl>): Observable<void> {
|
set(configs: Map<string, FormControl>): Observable<void> {
|
||||||
const body = []
|
const body = mapToConfigKeyContentArray(filterConfigKeyControlMap(configs))
|
||||||
for (let key in configs) {
|
const imageConfigs = filterImageConfigKeyControlMap(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()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.setImages(imageConfigs)
|
||||||
return this.api.put<void>('/config', body)
|
return this.api.put<void>('/config', body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,4 +37,19 @@ export class ConfigService {
|
||||||
restart(): Observable<void> {
|
restart(): Observable<void> {
|
||||||
return this.api.post<void>('/config/restart')
|
return this.api.post<void>('/config/restart')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setImages(configs: Map<string, FormControl>) {
|
||||||
|
const subscriptions = this.getImageConfigsSubscriptions(configs)
|
||||||
|
while (subscriptions.length > 0) {
|
||||||
|
const subscription = subscriptions.pop().subscribe({
|
||||||
|
next: () => subscription.unsubscribe()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getImageConfigsSubscriptions(configs: Map<string, FormControl>): Observable<void>[] {
|
||||||
|
return transformMap(configs, (key, control) => {
|
||||||
|
return this.setImage(key, control.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import {InfoBannerModule} from './components/info-banner/info-banner.module'
|
||||||
import {CreFormsModule} from './components/forms/forms.module'
|
import {CreFormsModule} from './components/forms/forms.module'
|
||||||
import {VarDirective} from './directives/var.directive'
|
import {VarDirective} from './directives/var.directive'
|
||||||
import {CreColorPreview} from './components/color-preview/color-preview'
|
import {CreColorPreview} from './components/color-preview/color-preview'
|
||||||
|
import {CreDialogsModule} from './components/dialogs/dialogs.module'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [VarDirective, HeaderComponent, UserMenuComponent, LabeledIconComponent, ConfirmBoxComponent, PermissionsListComponent, PermissionsFieldComponent, NavComponent, EntityListComponent, EntityAddComponent, EntityEditComponent, FileButtonComponent, GlobalAlertHandlerComponent, SliderFieldComponent, LoadingWheelComponent, CreColorPreview],
|
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,
|
InfoBannerModule,
|
||||||
CreFormsModule,
|
CreFormsModule,
|
||||||
VarDirective,
|
VarDirective,
|
||||||
CreColorPreview
|
CreColorPreview,
|
||||||
|
CreDialogsModule
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
export function filterMap<K, V>(map: Map<K, V>, predicate: (key: K, value: V) => boolean): Map<K, V> {
|
||||||
|
const filteredMap = new Map<K, V>()
|
||||||
|
map.forEach((value, key) => {
|
||||||
|
if (predicate(key, value)) {
|
||||||
|
filteredMap.set(key, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return filteredMap
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformMap<K, V, T>(map: Map<K, V>, transform: (key: K, value: V) => T): T[] {
|
||||||
|
const transformedArray = []
|
||||||
|
map.forEach((value, key) => {
|
||||||
|
transformedArray.push(transform(key, value))
|
||||||
|
})
|
||||||
|
return transformedArray
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
@import '~src/custom-theme'
|
@import '../../../../custom-theme'
|
||||||
|
|
||||||
.touchupkit-finish-container
|
.touchupkit-finish-container
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|
|
@ -122,10 +122,6 @@ $color-recipes-explorer-frontend-theme: mat-light-theme($theme-primary, $theme-a
|
||||||
// that you are using.
|
// that you are using.
|
||||||
@include angular-material-theme($color-recipes-explorer-frontend-theme);
|
@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 {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
@import 'assets/sass/modules/_fonts.sass'
|
@import "variables"
|
||||||
@import "custom-theme"
|
|
||||||
@import "~material-design-icons/iconfont/material-icons.css"
|
|
||||||
|
|
||||||
mat-card
|
mat-card
|
||||||
padding: 0 !important
|
padding: 0 !important
|
||||||
|
|
Loading…
Reference in New Issue