mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
Adding job schedules to config #569
This commit is contained in:
parent
9ac67ead63
commit
36d4641e9d
@ -1,6 +1,14 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-inferrable-types */
|
/* eslint-disable @typescript-eslint/no-inferrable-types */
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import {JobScheduleDTO, JobTrigger, JobTriggerType,} from '../../entities/job/JobScheduleDTO';
|
import {
|
||||||
|
AfterJobTrigger,
|
||||||
|
JobScheduleDTO,
|
||||||
|
JobTrigger,
|
||||||
|
JobTriggerType,
|
||||||
|
NeverJobTrigger,
|
||||||
|
PeriodicJobTrigger,
|
||||||
|
ScheduledJobTrigger,
|
||||||
|
} from '../../entities/job/JobScheduleDTO';
|
||||||
import {
|
import {
|
||||||
ClientConfig,
|
ClientConfig,
|
||||||
ClientGPXCompressingConfig,
|
ClientGPXCompressingConfig,
|
||||||
@ -334,8 +342,9 @@ export class ServerGPXCompressingConfig extends ClientGPXCompressingConfig {
|
|||||||
{
|
{
|
||||||
name: $localize`Min time delta`,
|
name: $localize`Min time delta`,
|
||||||
priority: ConfigPriority.underTheHood,
|
priority: ConfigPriority.underTheHood,
|
||||||
|
unit: 'ms',
|
||||||
uiDisabled: (sc: ServerGPXCompressingConfig, c: ServerConfig) => !c.Map.enabled || !sc.enabled || !c.MetaFile.gpx
|
uiDisabled: (sc: ServerGPXCompressingConfig, c: ServerConfig) => !c.Map.enabled || !sc.enabled || !c.MetaFile.gpx
|
||||||
},
|
} as TAGS,
|
||||||
description: $localize`Filters out entry that are closer than this in time in milliseconds.`
|
description: $localize`Filters out entry that are closer than this in time in milliseconds.`
|
||||||
})
|
})
|
||||||
minTimeDistance: number = 5000;
|
minTimeDistance: number = 5000;
|
||||||
@ -485,13 +494,13 @@ export class ServerLogConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SubConfigClass()
|
@SubConfigClass()
|
||||||
export class NeverJobTrigger implements JobTrigger {
|
export class NeverJobTriggerConfig implements NeverJobTrigger {
|
||||||
@ConfigProperty({type: JobTriggerType})
|
@ConfigProperty({type: JobTriggerType})
|
||||||
readonly type = JobTriggerType.never;
|
readonly type = JobTriggerType.never;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubConfigClass()
|
@SubConfigClass()
|
||||||
export class ScheduledJobTrigger implements JobTrigger {
|
export class ScheduledJobTriggerConfig implements ScheduledJobTrigger {
|
||||||
@ConfigProperty({type: JobTriggerType})
|
@ConfigProperty({type: JobTriggerType})
|
||||||
readonly type = JobTriggerType.scheduled;
|
readonly type = JobTriggerType.scheduled;
|
||||||
|
|
||||||
@ -500,17 +509,17 @@ export class ScheduledJobTrigger implements JobTrigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SubConfigClass()
|
@SubConfigClass()
|
||||||
export class PeriodicJobTrigger implements JobTrigger {
|
export class PeriodicJobTriggerConfig implements PeriodicJobTrigger {
|
||||||
@ConfigProperty({type: JobTriggerType})
|
@ConfigProperty({type: JobTriggerType})
|
||||||
readonly type = JobTriggerType.periodic;
|
readonly type = JobTriggerType.periodic;
|
||||||
@ConfigProperty({type: 'unsignedInt', max: 7})
|
@ConfigProperty({type: 'unsignedInt', max: 7})
|
||||||
periodicity: number | undefined; // 0-6: week days 7 every day
|
periodicity: number | undefined = 7; // 0-6: week days 7 every day
|
||||||
@ConfigProperty({type: 'unsignedInt', max: 23 * 60 + 59})
|
@ConfigProperty({type: 'unsignedInt', max: 23 * 60 + 59})
|
||||||
atTime: number | undefined; // day time
|
atTime: number | undefined = 0; // day time
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubConfigClass()
|
@SubConfigClass()
|
||||||
export class AfterJobTrigger implements JobTrigger {
|
export class AfterJobTriggerConfig implements AfterJobTrigger {
|
||||||
@ConfigProperty({type: JobTriggerType})
|
@ConfigProperty({type: JobTriggerType})
|
||||||
readonly type = JobTriggerType.after;
|
readonly type = JobTriggerType.after;
|
||||||
@ConfigProperty()
|
@ConfigProperty()
|
||||||
@ -528,42 +537,42 @@ export class JobScheduleConfig implements JobScheduleDTO {
|
|||||||
@ConfigProperty()
|
@ConfigProperty()
|
||||||
jobName: string;
|
jobName: string;
|
||||||
@ConfigProperty()
|
@ConfigProperty()
|
||||||
config: any = {};
|
config: Record<string, string | number | string[] | number[]> = {};
|
||||||
@ConfigProperty()
|
@ConfigProperty()
|
||||||
allowParallelRun: boolean;
|
allowParallelRun: boolean = false;
|
||||||
@ConfigProperty({
|
@ConfigProperty({
|
||||||
type: NeverJobTrigger,
|
type: NeverJobTriggerConfig,
|
||||||
typeBuilder: (v: JobTrigger) => {
|
typeBuilder: (v: JobTrigger) => {
|
||||||
const type = typeof v.type === 'number' ? v.type : JobTriggerType[v.type];
|
const type = typeof v.type === 'number' ? v.type : JobTriggerType[v.type];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case JobTriggerType.after:
|
case JobTriggerType.after:
|
||||||
return AfterJobTrigger;
|
return AfterJobTriggerConfig;
|
||||||
case JobTriggerType.never:
|
case JobTriggerType.never:
|
||||||
return NeverJobTrigger;
|
return NeverJobTriggerConfig;
|
||||||
case JobTriggerType.scheduled:
|
case JobTriggerType.scheduled:
|
||||||
return ScheduledJobTrigger;
|
return ScheduledJobTriggerConfig;
|
||||||
case JobTriggerType.periodic:
|
case JobTriggerType.periodic:
|
||||||
return PeriodicJobTrigger;
|
return PeriodicJobTriggerConfig;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
trigger:
|
trigger:
|
||||||
| AfterJobTrigger
|
| AfterJobTriggerConfig
|
||||||
| NeverJobTrigger
|
| NeverJobTriggerConfig
|
||||||
| PeriodicJobTrigger
|
| PeriodicJobTriggerConfig
|
||||||
| ScheduledJobTrigger;
|
| ScheduledJobTriggerConfig;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
name: string,
|
name: string,
|
||||||
jobName: string,
|
jobName: string,
|
||||||
allowParallelRun: boolean,
|
|
||||||
trigger:
|
trigger:
|
||||||
| AfterJobTrigger
|
| AfterJobTriggerConfig
|
||||||
| NeverJobTrigger
|
| NeverJobTriggerConfig
|
||||||
| PeriodicJobTrigger
|
| PeriodicJobTriggerConfig
|
||||||
| ScheduledJobTrigger,
|
| ScheduledJobTriggerConfig,
|
||||||
config: any
|
config: any = {},
|
||||||
|
allowParallelRun: boolean = false
|
||||||
) {
|
) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.jobName = jobName;
|
this.jobName = jobName;
|
||||||
@ -600,43 +609,43 @@ export class ServerJobConfig {
|
|||||||
new JobScheduleConfig(
|
new JobScheduleConfig(
|
||||||
DefaultsJobs[DefaultsJobs.Indexing],
|
DefaultsJobs[DefaultsJobs.Indexing],
|
||||||
DefaultsJobs[DefaultsJobs.Indexing],
|
DefaultsJobs[DefaultsJobs.Indexing],
|
||||||
false,
|
new NeverJobTriggerConfig(),
|
||||||
new NeverJobTrigger(),
|
{indexChangesOnly: true} // set config explicitly so it not undefined on the UI
|
||||||
{indexChangesOnly: true}
|
|
||||||
),
|
),
|
||||||
new JobScheduleConfig(
|
new JobScheduleConfig(
|
||||||
DefaultsJobs[DefaultsJobs['Preview Filling']],
|
DefaultsJobs[DefaultsJobs['Preview Filling']],
|
||||||
DefaultsJobs[DefaultsJobs['Preview Filling']],
|
DefaultsJobs[DefaultsJobs['Preview Filling']],
|
||||||
false,
|
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Indexing']]),
|
||||||
new NeverJobTrigger(),
|
|
||||||
{}
|
{}
|
||||||
),
|
),
|
||||||
new JobScheduleConfig(
|
new JobScheduleConfig(
|
||||||
DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
|
DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
|
||||||
DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
|
DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
|
||||||
false,
|
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Preview Filling']]),
|
||||||
new AfterJobTrigger(DefaultsJobs[DefaultsJobs['Preview Filling']]),
|
|
||||||
{sizes: [240], indexedOnly: true}
|
{sizes: [240], indexedOnly: true}
|
||||||
),
|
),
|
||||||
new JobScheduleConfig(
|
new JobScheduleConfig(
|
||||||
DefaultsJobs[DefaultsJobs['Photo Converting']],
|
DefaultsJobs[DefaultsJobs['Photo Converting']],
|
||||||
DefaultsJobs[DefaultsJobs['Photo Converting']],
|
DefaultsJobs[DefaultsJobs['Photo Converting']],
|
||||||
false,
|
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Thumbnail Generation']]),
|
||||||
new AfterJobTrigger(DefaultsJobs[DefaultsJobs['Thumbnail Generation']]),
|
|
||||||
{indexedOnly: true}
|
{indexedOnly: true}
|
||||||
),
|
),
|
||||||
new JobScheduleConfig(
|
new JobScheduleConfig(
|
||||||
DefaultsJobs[DefaultsJobs['Video Converting']],
|
DefaultsJobs[DefaultsJobs['Video Converting']],
|
||||||
DefaultsJobs[DefaultsJobs['Video Converting']],
|
DefaultsJobs[DefaultsJobs['Video Converting']],
|
||||||
false,
|
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Photo Converting']]),
|
||||||
new AfterJobTrigger(DefaultsJobs[DefaultsJobs['Photo Converting']]),
|
{indexedOnly: true}
|
||||||
|
),
|
||||||
|
new JobScheduleConfig(
|
||||||
|
DefaultsJobs[DefaultsJobs['GPX Compression']],
|
||||||
|
DefaultsJobs[DefaultsJobs['GPX Compression']],
|
||||||
|
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Video Converting']]),
|
||||||
{indexedOnly: true}
|
{indexedOnly: true}
|
||||||
),
|
),
|
||||||
new JobScheduleConfig(
|
new JobScheduleConfig(
|
||||||
DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']],
|
DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']],
|
||||||
DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']],
|
DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']],
|
||||||
false,
|
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['GPX Compression']]),
|
||||||
new AfterJobTrigger(DefaultsJobs[DefaultsJobs['Video Converting']]),
|
|
||||||
{indexedOnly: true}
|
{indexedOnly: true}
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -10,7 +10,7 @@ export interface JobTrigger {
|
|||||||
type: JobTriggerType;
|
type: JobTriggerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NeverJobTrigger {
|
export interface NeverJobTrigger extends JobTrigger {
|
||||||
type: JobTriggerType.never;
|
type: JobTriggerType.never;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ export interface AfterJobTrigger extends JobTrigger {
|
|||||||
export interface JobScheduleDTO {
|
export interface JobScheduleDTO {
|
||||||
name: string;
|
name: string;
|
||||||
jobName: string;
|
jobName: string;
|
||||||
config: any;
|
config: Record<string, string | number | string[] | number[]>;
|
||||||
allowParallelRun: boolean;
|
allowParallelRun: boolean;
|
||||||
trigger:
|
trigger:
|
||||||
| NeverJobTrigger
|
| NeverJobTrigger
|
||||||
|
@ -100,6 +100,9 @@ import {GallerySortingService} from './ui/gallery/navigator/sorting.service';
|
|||||||
import {FilterService} from './ui/gallery/filter/filter.service';
|
import {FilterService} from './ui/gallery/filter/filter.service';
|
||||||
import {TemplateComponent} from './ui/settings/template/template.component';
|
import {TemplateComponent} from './ui/settings/template/template.component';
|
||||||
import {AbstractSettingsService} from './ui/settings/_abstract/abstract.settings.service';
|
import {AbstractSettingsService} from './ui/settings/_abstract/abstract.settings.service';
|
||||||
|
import { WorkflowComponent } from './ui/settings/workflow/workflow.component';
|
||||||
|
import {JobProgressComponent} from './ui/settings/jobs/progress/job-progress.settings.component';
|
||||||
|
import {JobButtonComponent} from './ui/settings/jobs/button/job-button.settings.component';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MyHammerConfig extends HammerGestureConfig {
|
export class MyHammerConfig extends HammerGestureConfig {
|
||||||
@ -217,6 +220,8 @@ Marker.prototype.options.icon = iconDefault;
|
|||||||
// Settings
|
// Settings
|
||||||
SettingsEntryComponent,
|
SettingsEntryComponent,
|
||||||
TemplateComponent,
|
TemplateComponent,
|
||||||
|
JobProgressComponent,
|
||||||
|
JobButtonComponent,
|
||||||
/* UserMangerSettingsComponent,
|
/* UserMangerSettingsComponent,
|
||||||
DatabaseSettingsComponent,
|
DatabaseSettingsComponent,
|
||||||
MapSettingsComponent,
|
MapSettingsComponent,
|
||||||
@ -247,6 +252,7 @@ Marker.prototype.options.icon = iconDefault;
|
|||||||
StringifySearchQuery,
|
StringifySearchQuery,
|
||||||
FileDTOToPathPipe,
|
FileDTOToPathPipe,
|
||||||
PhotoFilterPipe,
|
PhotoFilterPipe,
|
||||||
|
WorkflowComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: HTTP_INTERCEPTORS, useClass: CSRFInterceptor, multi: true},
|
{provide: HTTP_INTERCEPTORS, useClass: CSRFInterceptor, multi: true},
|
||||||
|
@ -85,7 +85,14 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<app-settings-template #setting #server
|
<app-settings-template
|
||||||
|
*ngFor="let cp of configPaths"
|
||||||
|
#setting
|
||||||
|
#tmpl
|
||||||
|
icon="list"
|
||||||
|
[ConfigPath]="cp"
|
||||||
|
[hidden]="!tmpl.HasAvailableSettings"></app-settings-template>
|
||||||
|
<!-- <app-settings-template #setting #server
|
||||||
icon="list"
|
icon="list"
|
||||||
[ConfigPath]="'Server'"
|
[ConfigPath]="'Server'"
|
||||||
[hidden]="!server.HasAvailableSettings"></app-settings-template>
|
[hidden]="!server.HasAvailableSettings"></app-settings-template>
|
||||||
@ -140,7 +147,7 @@
|
|||||||
<app-settings-template #setting #indexing
|
<app-settings-template #setting #indexing
|
||||||
icon="pie-chart"
|
icon="pie-chart"
|
||||||
[ConfigPath]="'Indexing'"
|
[ConfigPath]="'Indexing'"
|
||||||
[hidden]="!indexing.HasAvailableSettings"></app-settings-template>
|
[hidden]="!indexing.HasAvailableSettings"></app-settings-template>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -7,10 +7,9 @@ import {NavigationService} from '../../model/navigation.service';
|
|||||||
import {ISettingsComponent} from '../settings/_abstract/ISettingsComponent';
|
import {ISettingsComponent} from '../settings/_abstract/ISettingsComponent';
|
||||||
import {PageHelper} from '../../model/page.helper';
|
import {PageHelper} from '../../model/page.helper';
|
||||||
import {SettingsService} from '../settings/settings.service';
|
import {SettingsService} from '../settings/settings.service';
|
||||||
import {CookieNames} from '../../../../common/CookieNames';
|
|
||||||
import {CookieService} from 'ngx-cookie-service';
|
|
||||||
import {ConfigPriority} from '../../../../common/config/public/ClientConfig';
|
import {ConfigPriority} from '../../../../common/config/public/ClientConfig';
|
||||||
import {Utils} from '../../../../common/Utils';
|
import {Utils} from '../../../../common/Utils';
|
||||||
|
import {WebConfig} from '../../../../common/config/private/WebConfig';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin',
|
selector: 'app-admin',
|
||||||
@ -24,6 +23,7 @@ export class AdminComponent implements OnInit, AfterViewInit {
|
|||||||
contents: ISettingsComponent[] = [];
|
contents: ISettingsComponent[] = [];
|
||||||
configPriorities: { key: number; value: string; }[];
|
configPriorities: { key: number; value: string; }[];
|
||||||
public readonly ConfigPriority = ConfigPriority;
|
public readonly ConfigPriority = ConfigPriority;
|
||||||
|
public readonly configPaths: string[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthenticationService,
|
private authService: AuthenticationService,
|
||||||
@ -32,6 +32,7 @@ export class AdminComponent implements OnInit, AfterViewInit {
|
|||||||
public settingsService: SettingsService,
|
public settingsService: SettingsService,
|
||||||
) {
|
) {
|
||||||
this.configPriorities = Utils.enumToArray(ConfigPriority);
|
this.configPriorities = Utils.enumToArray(ConfigPriority);
|
||||||
|
this.configPaths = Object.keys((new WebConfig()).State);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
|
@ -1,12 +1,4 @@
|
|||||||
import {
|
import {Directive, Input, OnDestroy, OnInit, ViewChild,} from '@angular/core';
|
||||||
Directive,
|
|
||||||
Input,
|
|
||||||
OnChanges,
|
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
Output,
|
|
||||||
ViewChild,
|
|
||||||
} from '@angular/core';
|
|
||||||
import {AuthenticationService} from '../../../model/network/authentication.service';
|
import {AuthenticationService} from '../../../model/network/authentication.service';
|
||||||
import {UserRoles} from '../../../../../common/entities/UserDTO';
|
import {UserRoles} from '../../../../../common/entities/UserDTO';
|
||||||
import {Utils} from '../../../../../common/Utils';
|
import {Utils} from '../../../../../common/Utils';
|
||||||
@ -20,12 +12,8 @@ import {WebConfig} from '../../../../../common/config/private/WebConfig';
|
|||||||
import {FormControl} from '@angular/forms';
|
import {FormControl} from '@angular/forms';
|
||||||
import {ConfigPriority, TAGS} from '../../../../../common/config/public/ClientConfig';
|
import {ConfigPriority, TAGS} from '../../../../../common/config/public/ClientConfig';
|
||||||
import {SettingsService} from '../settings.service';
|
import {SettingsService} from '../settings.service';
|
||||||
import {IConfigClass} from 'typeconfig/common';
|
import {IWebConfigClassPrivate} from '../../../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass';
|
||||||
import {IConfigClassPrivate} from '../../../../../../node_modules/typeconfig/src/decorators/class/IConfigClass';
|
|
||||||
import {IPropertyMetadata} from '../../../../../../node_modules/typeconfig/src/decorators/property/IPropertyState';
|
|
||||||
import {IWebConfigClass, IWebConfigClassPrivate} from '../../../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass';
|
|
||||||
import {WebConfigClassBuilder} from '../../../../../../node_modules/typeconfig/src/decorators/builders/WebConfigClassBuilder';
|
import {WebConfigClassBuilder} from '../../../../../../node_modules/typeconfig/src/decorators/builders/WebConfigClassBuilder';
|
||||||
import {ServerConfig} from '../../../../../common/config/private/PrivateConfig';
|
|
||||||
|
|
||||||
interface ConfigState<T = unknown> {
|
interface ConfigState<T = unknown> {
|
||||||
value: T;
|
value: T;
|
||||||
@ -73,13 +61,13 @@ export abstract class SettingsComponentDirective<
|
|||||||
public error: string = null;
|
public error: string = null;
|
||||||
public changed = false;
|
public changed = false;
|
||||||
public states: RecursiveState = {} as RecursiveState;
|
public states: RecursiveState = {} as RecursiveState;
|
||||||
|
protected name: string;
|
||||||
|
|
||||||
private subscription: Subscription = null;
|
private subscription: Subscription = null;
|
||||||
private settingsSubscription: Subscription = null;
|
private settingsSubscription: Subscription = null;
|
||||||
protected sliceFN?: (s: WebConfig) => T;
|
protected sliceFN?: (s: WebConfig) => T;
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
protected name: string,
|
|
||||||
protected authService: AuthenticationService,
|
protected authService: AuthenticationService,
|
||||||
private navigation: NavigationService,
|
private navigation: NavigationService,
|
||||||
public settingsService: AbstractSettingsService,
|
public settingsService: AbstractSettingsService,
|
||||||
@ -128,7 +116,8 @@ export abstract class SettingsComponentDirective<
|
|||||||
|
|
||||||
// if all sub elements are hidden, hide the parent too.
|
// if all sub elements are hidden, hide the parent too.
|
||||||
if (state.isConfigType) {
|
if (state.isConfigType) {
|
||||||
if (Object.keys(state.value.__state).findIndex(k => !st.value.__state[k].shouldHide()) === -1) {
|
if (state.value.__state &&
|
||||||
|
Object.keys(state.value.__state).findIndex(k => !st.value.__state[k].shouldHide()) === -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,14 +125,17 @@ export abstract class SettingsComponentDirective<
|
|||||||
|
|
||||||
if (state.isConfigArrayType) {
|
if (state.isConfigArrayType) {
|
||||||
for (let i = 0; i < state.value?.length; ++i) {
|
for (let i = 0; i < state.value?.length; ++i) {
|
||||||
if (Object.keys(state.value[i].__state).findIndex(k => !(st.value[i].__state[k].shouldHide && st.value[i].__state[k].shouldHide())) === -1) {
|
if (state.value[i].__state &&
|
||||||
|
Object.keys(state.value[i].__state).findIndex(k => !(st.value[i].__state[k].shouldHide && st.value[i].__state[k].shouldHide())) === -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
state.tags?.priority > this.globalSettingsService.configPriority &&
|
(state.tags?.priority > this.globalSettingsService.configPriority ||
|
||||||
|
(this.globalSettingsService.configPriority === ConfigPriority.basic &&
|
||||||
|
state.tags?.dockerSensitive && this.globalSettingsService.settings.value.Environment.isDocker)) && //if this value should not change in Docker, lets hide it
|
||||||
Utils.equalsFilter(state.value, state.default,
|
Utils.equalsFilter(state.value, state.default,
|
||||||
['__propPath', '__created', '__prototype', '__rootConfig']) &&
|
['__propPath', '__created', '__prototype', '__rootConfig']) &&
|
||||||
Utils.equalsFilter(state.original, state.default,
|
Utils.equalsFilter(state.original, state.default,
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
[disabled]="state.readonly || Disabled"
|
[disabled]="state.readonly || Disabled"
|
||||||
(change)="onChange($event)"
|
(change)="onChange($event)"
|
||||||
placeholder="Search Query">
|
placeholder="Search Query">
|
||||||
|
|
||||||
</app-gallery-search-field>
|
</app-gallery-search-field>
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -35,6 +34,7 @@
|
|||||||
Type !== 'SearchQuery' &&
|
Type !== 'SearchQuery' &&
|
||||||
ArrayType !== 'MapLayers' &&
|
ArrayType !== 'MapLayers' &&
|
||||||
ArrayType !== 'NavigationLinkConfig' &&
|
ArrayType !== 'NavigationLinkConfig' &&
|
||||||
|
ArrayType !== 'JobScheduleConfig' &&
|
||||||
ArrayType !== 'UserConfig'"
|
ArrayType !== 'UserConfig'"
|
||||||
[type]="type" [min]="state.min" [max]="state.max" class="form-control"
|
[type]="type" [min]="state.min" [max]="state.max" class="form-control"
|
||||||
[placeholder]="PlaceHolder"
|
[placeholder]="PlaceHolder"
|
||||||
@ -82,6 +82,17 @@
|
|||||||
[(ngModel)]="state.value">
|
[(ngModel)]="state.value">
|
||||||
</bSwitch>
|
</bSwitch>
|
||||||
|
|
||||||
|
|
||||||
|
<app-settings-workflow
|
||||||
|
class="w-100"
|
||||||
|
*ngIf="ArrayType === 'JobScheduleConfig'"
|
||||||
|
[(ngModel)]="state.value"
|
||||||
|
[id]="idName"
|
||||||
|
[name]="idName"
|
||||||
|
[title]="title"
|
||||||
|
(ngModelChange)="onChange($event)">
|
||||||
|
</app-settings-workflow>
|
||||||
|
|
||||||
<ng-container *ngIf="ArrayType === 'MapLayers'">
|
<ng-container *ngIf="ArrayType === 'MapLayers'">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
} from '../../../../../../common/config/public/ClientConfig';
|
} from '../../../../../../common/config/public/ClientConfig';
|
||||||
import {SettingsService} from '../../settings.service';
|
import {SettingsService} from '../../settings.service';
|
||||||
import {WebConfig} from '../../../../../../common/config/private/WebConfig';
|
import {WebConfig} from '../../../../../../common/config/private/WebConfig';
|
||||||
import {UserConfig} from '../../../../../../common/config/private/PrivateConfig';
|
import {JobScheduleConfig, UserConfig} from '../../../../../../common/config/private/PrivateConfig';
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
shouldHide(): boolean;
|
shouldHide(): boolean;
|
||||||
@ -78,7 +78,6 @@ export class SettingsEntryComponent
|
|||||||
title: string;
|
title: string;
|
||||||
idName: string;
|
idName: string;
|
||||||
private readonly GUID = Utils.GUID();
|
private readonly GUID = Utils.GUID();
|
||||||
public NavigationLinkTypesEnum = Utils.enumToArray(NavigationLinkTypes);
|
|
||||||
NavigationLinkTypes = NavigationLinkTypes;
|
NavigationLinkTypes = NavigationLinkTypes;
|
||||||
|
|
||||||
constructor(private searchQueryParserService: SearchQueryParserService,
|
constructor(private searchQueryParserService: SearchQueryParserService,
|
||||||
@ -142,6 +141,10 @@ export class SettingsEntryComponent
|
|||||||
if (this.state.arrayType === UserConfig) {
|
if (this.state.arrayType === UserConfig) {
|
||||||
return 'UserConfig';
|
return 'UserConfig';
|
||||||
}
|
}
|
||||||
|
if (this.state.arrayType === JobScheduleConfig) {
|
||||||
|
return 'JobScheduleConfig';
|
||||||
|
}
|
||||||
|
|
||||||
this.state.arrayType;
|
this.state.arrayType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,15 +7,18 @@ import {WebConfigClassBuilder} from 'typeconfig/src/decorators/builders/WebConfi
|
|||||||
import {ConfigPriority} from '../../../../common/config/public/ClientConfig';
|
import {ConfigPriority} from '../../../../common/config/public/ClientConfig';
|
||||||
import {CookieNames} from '../../../../common/CookieNames';
|
import {CookieNames} from '../../../../common/CookieNames';
|
||||||
import {CookieService} from 'ngx-cookie-service';
|
import {CookieService} from 'ngx-cookie-service';
|
||||||
|
import {JobDTO} from '../../../../common/entities/job/JobDTO';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SettingsService {
|
export class SettingsService {
|
||||||
public configPriority = ConfigPriority.basic;
|
public configPriority = ConfigPriority.basic;
|
||||||
public settings: BehaviorSubject<WebConfig>;
|
public settings: BehaviorSubject<WebConfig>;
|
||||||
private fetchingSettings = false;
|
private fetchingSettings = false;
|
||||||
|
public availableJobs: BehaviorSubject<JobDTO[]>;
|
||||||
|
|
||||||
constructor(private networkService: NetworkService,
|
constructor(private networkService: NetworkService,
|
||||||
private cookieService: CookieService) {
|
private cookieService: CookieService) {
|
||||||
|
this.availableJobs = new BehaviorSubject([]);
|
||||||
this.settings = new BehaviorSubject<WebConfig>(new WebConfig());
|
this.settings = new BehaviorSubject<WebConfig>(new WebConfig());
|
||||||
this.getSettings().catch(console.error);
|
this.getSettings().catch(console.error);
|
||||||
|
|
||||||
@ -27,6 +30,12 @@ export class SettingsService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAvailableJobs(): Promise<void> {
|
||||||
|
this.availableJobs.next(
|
||||||
|
await this.networkService.getJson<JobDTO[]>('/admin/jobs/available')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public async getSettings(): Promise<void> {
|
public async getSettings(): Promise<void> {
|
||||||
if (this.fetchingSettings === true) {
|
if (this.fetchingSettings === true) {
|
||||||
return;
|
return;
|
||||||
|
@ -25,7 +25,6 @@ export class TemplateComponent extends SettingsComponentDirective<any> implement
|
|||||||
globalSettingsService: SettingsService
|
globalSettingsService: SettingsService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
`Template`,
|
|
||||||
authService,
|
authService,
|
||||||
navigation,
|
navigation,
|
||||||
settingsService,
|
settingsService,
|
||||||
|
257
src/frontend/app/ui/settings/workflow/workflow.component.html
Normal file
257
src/frontend/app/ui/settings/workflow/workflow.component.html
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
||||||
|
<div *ngFor="let schedule of sortedSchedules() as sortedSchedules; let i= index">
|
||||||
|
<div class="card bg-light mt-2 mb-2 no-changed-settings {{shouldIdent(schedule,sortedSchedules[i-1])? 'ms-4' : ''}}">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div (click)="showDetails[schedule.name]=!showDetails[schedule.name]">
|
||||||
|
<span class="oi oi-chevron-{{showDetails[schedule.name] ? 'bottom' : 'right'}}"></span>
|
||||||
|
{{schedule.name}}
|
||||||
|
<ng-container [ngSwitch]="schedule.trigger.type">
|
||||||
|
<ng-container *ngSwitchCase="JobTriggerType.periodic">
|
||||||
|
<span class="badge bg-primary" i18n>every</span>
|
||||||
|
{{periods[$any(schedule.trigger).periodicity]}} {{atTimeLocal($any(schedule.trigger).atTime) | date:"HH:mm (z)"}}
|
||||||
|
</ng-container>
|
||||||
|
<ng-container
|
||||||
|
*ngSwitchCase="JobTriggerType.scheduled">@{{$any(schedule.trigger).time | date:"medium"}}</ng-container>
|
||||||
|
<span class="badge bg-secondary" *ngSwitchCase="JobTriggerType.never" i18n>never</span>
|
||||||
|
<ng-container *ngSwitchCase="JobTriggerType.after">
|
||||||
|
<span class="badge bg-primary" i18n>after</span>
|
||||||
|
{{$any(schedule.trigger).afterScheduleName}}
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-danger ms-0" (click)="remove(schedule)"><span
|
||||||
|
class="oi oi-trash"></span>
|
||||||
|
</button>
|
||||||
|
<app-settings-job-button class="ms-md-2 mt-2 mt-md-0"
|
||||||
|
(jobError)="error=$event"
|
||||||
|
[allowParallelRun]="schedule.allowParallelRun"
|
||||||
|
[jobName]="schedule.jobName" [config]="schedule.config"
|
||||||
|
[shortName]="true"></app-settings-job-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card-body" [hidden]="!showDetails[schedule.name]">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="mb-1 row">
|
||||||
|
<label class="col-md-2 control-label" [for]="'jobName'+i" i18n>Job:</label>
|
||||||
|
<div class="col-md-4">
|
||||||
|
{{backendTextService.getJobName(schedule.jobName)}}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<app-settings-job-button class="float-end"
|
||||||
|
[jobName]="schedule.jobName"
|
||||||
|
[allowParallelRun]="schedule.allowParallelRun"
|
||||||
|
(jobError)="error=$event"
|
||||||
|
[config]="schedule.config"></app-settings-job-button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-1 row">
|
||||||
|
<label class="col-md-2 control-label" [for]="'repeatType'+i" i18n>Periodicity:</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<select class="form-select" [(ngModel)]="schedule.trigger.type"
|
||||||
|
(ngModelChange)="jobTriggerTypeChanged($event,schedule); onChange($event);"
|
||||||
|
[name]="'repeatType'+i" required>
|
||||||
|
<option *ngFor="let jobTrigger of JobTriggerTypeMap"
|
||||||
|
[ngValue]="jobTrigger.key">{{jobTrigger.value}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<small class="form-text text-muted"
|
||||||
|
i18n>Set the time to run the job.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 row" *ngIf="schedule.trigger.type == JobTriggerType.after">
|
||||||
|
<label class="col-md-2 control-label" [for]="'triggerAfter'+i" i18n>After:</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<select class="form-select"
|
||||||
|
[(ngModel)]="schedule.trigger.afterScheduleName"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
[name]="'triggerAfter'+i" required>
|
||||||
|
<ng-container *ngFor="let sch of sortedSchedules">
|
||||||
|
<option *ngIf="sch.name !== schedule.name"
|
||||||
|
[ngValue]="sch.name">{{sch.name}}
|
||||||
|
</option>
|
||||||
|
</ng-container>
|
||||||
|
</select>
|
||||||
|
<small class="form-text text-muted"
|
||||||
|
i18n>The job will run after that job finishes.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="mb-3 row" *ngIf="schedule.trigger.type == JobTriggerType.scheduled">
|
||||||
|
<label class="col-md-2 control-label" [for]="'triggerTime'+i" i18n>At:</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<app-timestamp-datepicker
|
||||||
|
[name]="'triggerTime'+i"
|
||||||
|
(timestampChange)="onChange($event)"
|
||||||
|
[(timestamp)]="schedule.trigger.time"></app-timestamp-datepicker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 row" *ngIf="schedule.trigger.type == JobTriggerType.periodic">
|
||||||
|
<label class="col-md-2 control-label" [for]="'periodicity'+i" i18n>At:</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
[(ngModel)]="schedule.trigger.periodicity"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
[name]="'periodicity' + i"
|
||||||
|
required>
|
||||||
|
<option *ngFor="let period of periods; let i = index"
|
||||||
|
[ngValue]="i">
|
||||||
|
<ng-container i18n>every</ng-container>
|
||||||
|
{{period}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<app-timestamp-timepicker
|
||||||
|
[name]="'atTime'+i"
|
||||||
|
(timestampChange)="onChange($event)"
|
||||||
|
[(timestamp)]="schedule.trigger.atTime"></app-timestamp-timepicker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label class="col-md-2 control-label" [for]="'allowParallelRun'+'_'+i" i18n>Allow parallel run</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<bSwitch
|
||||||
|
class="switch"
|
||||||
|
[name]="'allowParallelRun'+'_'+i"
|
||||||
|
[id]="'allowParallelRun'+'_'+i"
|
||||||
|
switch-on-color="primary"
|
||||||
|
[switch-inverse]="true"
|
||||||
|
switch-off-text="Disabled"
|
||||||
|
switch-on-text="Enabled"
|
||||||
|
i18n-switch-off-text i18n-switch-on-text
|
||||||
|
[switch-handle-width]="100"
|
||||||
|
[switch-label-width]="20"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
[(ngModel)]="schedule.allowParallelRun">
|
||||||
|
</bSwitch>
|
||||||
|
<small class="form-text text-muted ms-2"
|
||||||
|
i18n>Enables the job to start even if another job is already running.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="getConfigTemplate(schedule.jobName) ">
|
||||||
|
<hr/>
|
||||||
|
<div *ngFor="let configEntry of getConfigTemplate(schedule.jobName)">
|
||||||
|
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label class="col-md-2 control-label"
|
||||||
|
[for]="configEntry.id+'_'+i">{{backendTextService.get(configEntry.name)}}</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<ng-container [ngSwitch]="configEntry.type">
|
||||||
|
<ng-container *ngSwitchCase="'boolean'">
|
||||||
|
<bSwitch
|
||||||
|
class="switch"
|
||||||
|
[name]="configEntry.id+'_'+i"
|
||||||
|
[id]="configEntry.id+'_'+i"
|
||||||
|
switch-on-color="primary"
|
||||||
|
[switch-inverse]="true"
|
||||||
|
switch-off-text="Disabled"
|
||||||
|
switch-on-text="Enabled"
|
||||||
|
i18n-switch-off-text i18n-switch-on-text
|
||||||
|
[switch-handle-width]="100"
|
||||||
|
[switch-label-width]="20"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
[(ngModel)]="schedule.config[configEntry.id]">
|
||||||
|
</bSwitch>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'string'">
|
||||||
|
<input type="text" class="form-control" [name]="configEntry.id+'_'+i"
|
||||||
|
[id]="configEntry.id+'_'+i"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
[(ngModel)]="schedule.config[configEntry.id]" required>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'number'">
|
||||||
|
<input type="number" class="form-control" [name]="configEntry.id+'_'+i"
|
||||||
|
[id]="configEntry.id+'_'+i"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
[(ngModel)]="schedule.config[configEntry.id]" required>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'number-array'">
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
[name]="configEntry.id+'_'+i"
|
||||||
|
[id]="configEntry.id+'_'+i"
|
||||||
|
(ngModelChange)="setNumberArray(schedule.config,configEntry.id,$event); onChange($event);"
|
||||||
|
[ngModel]="getNumberArray($any(schedule.config),configEntry.id)" required>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<small class="form-text text-muted ms-2">
|
||||||
|
<ng-container *ngIf="configEntry.type == 'number-array'" i18n>';' separated integers.
|
||||||
|
</ng-container>{{backendTextService.get(configEntry.description)}}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<app-settings-job-progress
|
||||||
|
class="card-footer bg-transparent"
|
||||||
|
*ngIf="getProgress(schedule)"
|
||||||
|
[progress]="getProgress(schedule)">
|
||||||
|
</app-settings-job-progress>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary float-end mt-2"
|
||||||
|
(click)="prepareNewJob()" i18n>+ Add Job
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div bsModal #jobModal="bs-modal" class="modal fade" id="jobModal" tabindex="-1" role="dialog"
|
||||||
|
aria-labelledby="jobModalLabel">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="jobModalLabel" i18n>Add new job</h5>
|
||||||
|
<button type="button" class="btn-close" (click)="jobModal.hide()" data-dismiss="modal" aria-label="Close">
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form #jobModalForm="ngForm">
|
||||||
|
<div class="modal-body">
|
||||||
|
<select class="form-select"
|
||||||
|
(change)="jobTypeChanged(newSchedule)"
|
||||||
|
[(ngModel)]="newSchedule.jobName"
|
||||||
|
name="newJobName" required>
|
||||||
|
<option *ngFor="let availableJob of settingsService.availableJobs | async"
|
||||||
|
[ngValue]="availableJob.Name">{{backendTextService.getJobName(availableJob.Name)}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<small class="form-text text-muted"
|
||||||
|
i18n>Select a job to schedule.
|
||||||
|
</small>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" (click)="jobModal.hide()" i18n>Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-dismiss="modal"
|
||||||
|
(click)="addNewJob()"
|
||||||
|
[disabled]="!jobModalForm.form.valid" i18n>Add Job
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
294
src/frontend/app/ui/settings/workflow/workflow.component.ts
Normal file
294
src/frontend/app/ui/settings/workflow/workflow.component.ts
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
import {Component, OnDestroy, OnInit, QueryList, ViewChildren} from '@angular/core';
|
||||||
|
import {forwardRef} from '../../../../../../node_modules/@angular/core';
|
||||||
|
import {ModalDirective} from 'ngx-bootstrap/modal';
|
||||||
|
import {
|
||||||
|
AfterJobTrigger,
|
||||||
|
JobScheduleDTO,
|
||||||
|
JobScheduleDTOUtils,
|
||||||
|
JobTriggerType,
|
||||||
|
PeriodicJobTrigger,
|
||||||
|
ScheduledJobTrigger
|
||||||
|
} from '../../../../../common/entities/job/JobScheduleDTO';
|
||||||
|
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
||||||
|
import {BackendtextService} from '../../../model/backendtext.service';
|
||||||
|
import {SettingsService} from '../settings.service';
|
||||||
|
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
|
||||||
|
import {JobProgressDTO, JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO';
|
||||||
|
import {
|
||||||
|
AfterJobTriggerConfig,
|
||||||
|
JobScheduleConfig,
|
||||||
|
NeverJobTriggerConfig,
|
||||||
|
PeriodicJobTriggerConfig,
|
||||||
|
ScheduledJobTriggerConfig
|
||||||
|
} from '../../../../../common/config/private/PrivateConfig';
|
||||||
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
NG_VALIDATORS,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
ValidationErrors,
|
||||||
|
Validator
|
||||||
|
} from '../../../../../../node_modules/@angular/forms';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-settings-workflow',
|
||||||
|
templateUrl: './workflow.component.html',
|
||||||
|
styleUrls: ['./workflow.component.css'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => WorkflowComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => WorkflowComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class WorkflowComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy {
|
||||||
|
|
||||||
|
|
||||||
|
public schedules: JobScheduleConfig[] = [];
|
||||||
|
|
||||||
|
@ViewChildren('jobModal') public jobModalQL: QueryList<ModalDirective>;
|
||||||
|
|
||||||
|
public disableButtons = false;
|
||||||
|
public JobTriggerTypeMap: { key: number; value: string }[];
|
||||||
|
public JobTriggerType = JobTriggerType;
|
||||||
|
public periods: string[] = [];
|
||||||
|
public showDetails: { [key: string]: boolean } = {};
|
||||||
|
public JobProgressStates = JobProgressStates;
|
||||||
|
public newSchedule: JobScheduleDTO = {
|
||||||
|
name: '',
|
||||||
|
config: null,
|
||||||
|
jobName: '',
|
||||||
|
trigger: {
|
||||||
|
type: JobTriggerType.never,
|
||||||
|
},
|
||||||
|
allowParallelRun: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
error: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public settingsService: SettingsService,
|
||||||
|
public jobsService: ScheduledJobsService,
|
||||||
|
public backendTextService: BackendtextService,
|
||||||
|
) {
|
||||||
|
this.JobTriggerTypeMap = [
|
||||||
|
{key: JobTriggerType.after, value: $localize`after`},
|
||||||
|
{key: JobTriggerType.never, value: $localize`never`},
|
||||||
|
{key: JobTriggerType.periodic, value: $localize`periodic`},
|
||||||
|
{key: JobTriggerType.scheduled, value: $localize`scheduled`},
|
||||||
|
];
|
||||||
|
this.periods = [
|
||||||
|
$localize`Monday`, // 0
|
||||||
|
$localize`Tuesday`, // 1
|
||||||
|
$localize`Wednesday`, // 2
|
||||||
|
$localize`Thursday`,
|
||||||
|
$localize`Friday`,
|
||||||
|
$localize`Saturday`,
|
||||||
|
$localize`Sunday`,
|
||||||
|
$localize`day`,
|
||||||
|
]; // 7
|
||||||
|
}
|
||||||
|
|
||||||
|
atTimeLocal(atTime: number): Date {
|
||||||
|
const d = new Date();
|
||||||
|
d.setUTCHours(Math.floor(atTime / 60));
|
||||||
|
d.setUTCMinutes(Math.floor(atTime % 60));
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
|
||||||
|
const job = this.settingsService.availableJobs.value.find(
|
||||||
|
(t) => t.Name === JobName
|
||||||
|
);
|
||||||
|
if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) {
|
||||||
|
return job.ConfigTemplate;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.jobsService.subscribeToProgress();
|
||||||
|
this.settingsService.getAvailableJobs().catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.jobsService.unsubscribeFromProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(schedule: JobScheduleDTO): void {
|
||||||
|
this.schedules.splice(
|
||||||
|
this.schedules.indexOf(schedule),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
jobTypeChanged(schedule: JobScheduleDTO): void {
|
||||||
|
const job = this.settingsService.availableJobs.value.find(
|
||||||
|
(t) => t.Name === schedule.jobName
|
||||||
|
);
|
||||||
|
schedule.config = schedule.config || {};
|
||||||
|
if (job.ConfigTemplate) {
|
||||||
|
job.ConfigTemplate.forEach(
|
||||||
|
(ct) => (schedule.config[ct.id] = ct.defaultValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jobTriggerTypeChanged(
|
||||||
|
triggerType: JobTriggerType,
|
||||||
|
schedule: JobScheduleDTO
|
||||||
|
): void {
|
||||||
|
switch (triggerType) {
|
||||||
|
case JobTriggerType.never:
|
||||||
|
schedule.trigger = new NeverJobTriggerConfig();
|
||||||
|
break;
|
||||||
|
case JobTriggerType.scheduled:
|
||||||
|
schedule.trigger = new ScheduledJobTriggerConfig();
|
||||||
|
(schedule.trigger as unknown as ScheduledJobTrigger).time = Date.now();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JobTriggerType.periodic:
|
||||||
|
schedule.trigger = new PeriodicJobTriggerConfig();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JobTriggerType.after:
|
||||||
|
schedule.trigger = new AfterJobTriggerConfig();
|
||||||
|
if (!(schedule.trigger as unknown as AfterJobTrigger).afterScheduleName && this.schedules.length > 1) {
|
||||||
|
(schedule.trigger as unknown as AfterJobTrigger).afterScheduleName = this.schedules.find(s => s.name !== schedule.name).name;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setNumberArray(configElement: any, id: string, value: string): void {
|
||||||
|
value = value.replace(new RegExp(',', 'g'), ';');
|
||||||
|
value = value.replace(new RegExp(' ', 'g'), ';');
|
||||||
|
configElement[id] = value
|
||||||
|
.split(';')
|
||||||
|
.map((s: string) => parseInt(s, 10))
|
||||||
|
.filter((i: number) => !isNaN(i) && i > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNumberArray(configElement: Record<string, number[]>, id: string): string {
|
||||||
|
return configElement[id] ? configElement[id].join('; ') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public shouldIdent(curr: JobScheduleDTO, prev: JobScheduleDTO): boolean {
|
||||||
|
return (
|
||||||
|
curr &&
|
||||||
|
curr.trigger.type === JobTriggerType.after &&
|
||||||
|
prev &&
|
||||||
|
prev.name === curr.trigger.afterScheduleName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sortedSchedules(): JobScheduleDTO[] {
|
||||||
|
return (this.schedules || [])
|
||||||
|
.slice()
|
||||||
|
.sort((a: JobScheduleDTO, b: JobScheduleDTO) => {
|
||||||
|
return (
|
||||||
|
this.getNextRunningDate(a, this.schedules) -
|
||||||
|
this.getNextRunningDate(b, this.schedules)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareNewJob(): void {
|
||||||
|
const jobName = this.settingsService.availableJobs.value[0].Name;
|
||||||
|
this.newSchedule = new JobScheduleConfig('new job',
|
||||||
|
jobName,
|
||||||
|
new NeverJobTriggerConfig());
|
||||||
|
|
||||||
|
// setup job specific config
|
||||||
|
const job = this.settingsService.availableJobs.value.find(
|
||||||
|
(t) => t.Name === jobName
|
||||||
|
);
|
||||||
|
this.newSchedule.config = this.newSchedule.config || {};
|
||||||
|
if (job.ConfigTemplate) {
|
||||||
|
job.ConfigTemplate.forEach(
|
||||||
|
(ct) => (this.newSchedule.config[ct.id] = ct.defaultValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.jobModalQL.first.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewJob(): void {
|
||||||
|
// make unique job name
|
||||||
|
const jobName = this.newSchedule.jobName;
|
||||||
|
const count = this.schedules.filter(
|
||||||
|
(s: JobScheduleDTO) => s.jobName === jobName
|
||||||
|
).length;
|
||||||
|
this.newSchedule.name =
|
||||||
|
count === 0
|
||||||
|
? jobName
|
||||||
|
: this.backendTextService.getJobName(jobName) + ' ' + (count + 1);
|
||||||
|
this.schedules.push(this.newSchedule);
|
||||||
|
|
||||||
|
this.jobModalQL.first.hide();
|
||||||
|
this.onChange(null); // trigger change detection after adding new job
|
||||||
|
}
|
||||||
|
|
||||||
|
getProgress(schedule: JobScheduleDTO): JobProgressDTO {
|
||||||
|
return this.jobsService.getProgress(schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNextRunningDate(
|
||||||
|
sch: JobScheduleDTO,
|
||||||
|
list: JobScheduleDTO[],
|
||||||
|
depth = 0
|
||||||
|
): number {
|
||||||
|
if (depth > list.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (sch.trigger.type === JobTriggerType.never) {
|
||||||
|
return (
|
||||||
|
list
|
||||||
|
.map((s) => s.name)
|
||||||
|
.sort()
|
||||||
|
.indexOf(sch.name) * -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (sch.trigger.type === JobTriggerType.after) {
|
||||||
|
const parent = list.find(
|
||||||
|
(s) => s.name === (sch.trigger as AfterJobTrigger).afterScheduleName
|
||||||
|
);
|
||||||
|
if (parent) {
|
||||||
|
return this.getNextRunningDate(parent, list, depth + 1) + 0.001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const d = JobScheduleDTOUtils.getNextRunningDate(new Date(), sch);
|
||||||
|
return d !== null ? d.getTime() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): ValidationErrors {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onChange = (value: unknown): void => {
|
||||||
|
// empty
|
||||||
|
};
|
||||||
|
|
||||||
|
public onTouched = (): void => {
|
||||||
|
// empty
|
||||||
|
};
|
||||||
|
|
||||||
|
public writeValue(obj: JobScheduleConfig[]): void {
|
||||||
|
this.schedules = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnChange(fn: (v: JobScheduleConfig[]) => void): void {
|
||||||
|
this.onChange = () => fn(this.schedules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnTouched(fn: () => void): void {
|
||||||
|
this.onTouched = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,11 +13,16 @@ a {
|
|||||||
margin-left: -4px;
|
margin-left: -4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changed-settings .bootstrap-switch {
|
|
||||||
|
.changed-settings.bootstrap-switch {
|
||||||
border-color: var(--bs-primary);
|
border-color: var(--bs-primary);
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.changed-settings .no-changed-settings .bootstrap-switch {
|
||||||
|
border-color: var(--bs-gray-400) !important;
|
||||||
|
}
|
||||||
|
|
||||||
#toast-container > div {
|
#toast-container > div {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user