mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
implementing after trigger
This commit is contained in:
parent
e2864117b2
commit
509a398639
@ -436,7 +436,7 @@ export class SettingsMWs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async updateTasksSettings(req: Request, res: Response, next: NextFunction) {
|
public static async updateJobSettings(req: Request, res: Response, next: NextFunction) {
|
||||||
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
|
if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) {
|
||||||
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
|
return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed'));
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ import {JobProgressDTO} from '../../../common/entities/settings/JobProgressDTO';
|
|||||||
import {IJob} from './jobs/IJob';
|
import {IJob} from './jobs/IJob';
|
||||||
import {JobRepository} from './JobRepository';
|
import {JobRepository} from './JobRepository';
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {JobScheduleDTO, JobTriggerType} from '../../../common/entities/job/JobScheduleDTO';
|
import {AfterJobTrigger, JobScheduleDTO, JobTriggerType} from '../../../common/entities/job/JobScheduleDTO';
|
||||||
import {Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
|
import {NotificationManager} from '../NotifocationManager';
|
||||||
|
|
||||||
declare var global: NodeJS.Global;
|
declare var global: NodeJS.Global;
|
||||||
|
|
||||||
@ -32,7 +33,9 @@ export class JobManager implements IJobManager {
|
|||||||
async run<T>(jobName: string, config: T): Promise<void> {
|
async run<T>(jobName: string, config: T): Promise<void> {
|
||||||
const t = this.findJob(jobName);
|
const t = this.findJob(jobName);
|
||||||
if (t) {
|
if (t) {
|
||||||
await t.start(config);
|
await t.start(config, () => {
|
||||||
|
this.onJobFinished(t);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
Logger.warn(LOG_TAG, 'cannot find job to start:' + jobName);
|
Logger.warn(LOG_TAG, 'cannot find job to start:' + jobName);
|
||||||
}
|
}
|
||||||
@ -50,6 +53,22 @@ export class JobManager implements IJobManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onJobFinished(job: IJob<any>): Promise<void> {
|
||||||
|
const sch = Config.Server.Jobs.scheduled.find(s => s.jobName === job.Name);
|
||||||
|
if (sch) {
|
||||||
|
console.log('found parent');
|
||||||
|
const children = Config.Server.Jobs.scheduled.filter(s => s.trigger.type === JobTriggerType.after &&
|
||||||
|
(<AfterJobTrigger>s.trigger).afterScheduleName === sch.name);
|
||||||
|
for (let i = 0; i < children.length; ++i) {
|
||||||
|
try {
|
||||||
|
await this.run(children[i].jobName, children[i].config);
|
||||||
|
} catch (e) {
|
||||||
|
NotificationManager.warning('Job running error:' + children[i].name, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getAvailableJobs(): IJob<any>[] {
|
getAvailableJobs(): IJob<any>[] {
|
||||||
return JobRepository.Instance.getAvailableJobs();
|
return JobRepository.Instance.getAvailableJobs();
|
||||||
}
|
}
|
||||||
@ -65,58 +84,12 @@ export class JobManager implements IJobManager {
|
|||||||
Config.Server.Jobs.scheduled.forEach(s => this.runSchedule(s));
|
Config.Server.Jobs.scheduled.forEach(s => this.runSchedule(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getNextDayOfTheWeek(refDate: Date, dayOfWeek: number) {
|
|
||||||
const date = new Date(refDate);
|
|
||||||
date.setDate(refDate.getDate() + (dayOfWeek + 1 + 7 - refDate.getDay()) % 7);
|
|
||||||
if (date.getDay() === refDate.getDay()) {
|
|
||||||
return new Date(refDate);
|
|
||||||
}
|
|
||||||
date.setHours(0, 0, 0, 0);
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected nextValidDate(date: Date, h: number, m: number, dayDiff: number): Date {
|
|
||||||
|
|
||||||
date.setSeconds(0);
|
|
||||||
if (date.getHours() < h || (date.getHours() === h && date.getMinutes() < m)) {
|
|
||||||
date.setHours(h);
|
|
||||||
date.setMinutes(m);
|
|
||||||
} else {
|
|
||||||
date.setTime(date.getTime() + dayDiff);
|
|
||||||
date.setHours(h);
|
|
||||||
date.setMinutes(m);
|
|
||||||
}
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getDateFromSchedule(refDate: Date, schedule: JobScheduleDTO): Date {
|
|
||||||
switch (schedule.trigger.type) {
|
|
||||||
case JobTriggerType.scheduled:
|
|
||||||
return new Date(schedule.trigger.time);
|
|
||||||
|
|
||||||
case JobTriggerType.periodic:
|
|
||||||
|
|
||||||
|
|
||||||
const hour = Math.floor(schedule.trigger.atTime / 1000 / (60 * 60));
|
|
||||||
const minute = (schedule.trigger.atTime / 1000 / 60) % 60;
|
|
||||||
|
|
||||||
if (schedule.trigger.periodicity <= 6) { // Between Monday and Sunday
|
|
||||||
const nextRunDate = this.getNextDayOfTheWeek(refDate, schedule.trigger.periodicity);
|
|
||||||
return this.nextValidDate(nextRunDate, hour, minute, 7 * 24 * 60 * 60 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// every day
|
|
||||||
return this.nextValidDate(new Date(refDate), hour, minute, 24 * 60 * 60 * 1000);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected findJob<T = any>(jobName: string): IJob<T> {
|
protected findJob<T = any>(jobName: string): IJob<T> {
|
||||||
return this.getAvailableJobs().find(t => t.Name === jobName);
|
return this.getAvailableJobs().find(t => t.Name === jobName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private runSchedule(schedule: JobScheduleDTO) {
|
private runSchedule(schedule: JobScheduleDTO) {
|
||||||
const nextDate = this.getDateFromSchedule(new Date(), schedule);
|
const nextDate = JobScheduleDTO.getNextRunningDate(new Date(), schedule);
|
||||||
if (nextDate && nextDate.getTime() > Date.now()) {
|
if (nextDate && nextDate.getTime() > Date.now()) {
|
||||||
Logger.debug(LOG_TAG, 'running schedule: ' + schedule.jobName +
|
Logger.debug(LOG_TAG, 'running schedule: ' + schedule.jobName +
|
||||||
' at ' + nextDate.toLocaleString(undefined, {hour12: false}));
|
' at ' + nextDate.toLocaleString(undefined, {hour12: false}));
|
||||||
|
@ -10,7 +10,7 @@ import {Logger} from '../../../Logger';
|
|||||||
declare var global: NodeJS.Global;
|
declare var global: NodeJS.Global;
|
||||||
|
|
||||||
|
|
||||||
const LOG_TAG = '[FileTask]';
|
const LOG_TAG = '[FileJob]';
|
||||||
|
|
||||||
|
|
||||||
export abstract class FileJob<T, S = void> extends Job<S> {
|
export abstract class FileJob<T, S = void> extends Job<S> {
|
||||||
@ -56,7 +56,7 @@ export abstract class FileJob<T, S = void> extends Job<S> {
|
|||||||
await this.processFile(file);
|
await this.processFile(file);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
Logger.error(LOG_TAG, 'Error during processing file: ' + e.toString());
|
Logger.error(LOG_TAG, 'Error during processing file.' + ', ' + e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.progress;
|
return this.progress;
|
||||||
|
@ -6,7 +6,7 @@ export interface IJob<T> extends JobDTO {
|
|||||||
Supported: boolean;
|
Supported: boolean;
|
||||||
Progress: JobProgressDTO;
|
Progress: JobProgressDTO;
|
||||||
|
|
||||||
start(config: T): Promise<void>;
|
start(config: T, onFinishCB?: () => void): Promise<void>;
|
||||||
|
|
||||||
stop(): void;
|
stop(): void;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import {JobProgressDTO, JobState} from '../../../../common/entities/settings/Job
|
|||||||
import {Logger} from '../../../Logger';
|
import {Logger} from '../../../Logger';
|
||||||
import {IJob} from './IJob';
|
import {IJob} from './IJob';
|
||||||
import {ConfigTemplateEntry, JobDTO} from '../../../../common/entities/job/JobDTO';
|
import {ConfigTemplateEntry, JobDTO} from '../../../../common/entities/job/JobDTO';
|
||||||
import * as rimraf from 'rimraf';
|
|
||||||
|
|
||||||
declare const process: any;
|
declare const process: any;
|
||||||
|
|
||||||
@ -16,20 +15,19 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
protected prResolve: () => void;
|
protected prResolve: () => void;
|
||||||
protected IsInstant = false;
|
protected IsInstant = false;
|
||||||
|
|
||||||
|
|
||||||
public abstract get Supported(): boolean;
|
public abstract get Supported(): boolean;
|
||||||
|
|
||||||
public abstract get Name(): string;
|
public abstract get Name(): string;
|
||||||
|
|
||||||
|
|
||||||
public abstract get ConfigTemplate(): ConfigTemplateEntry[];
|
public abstract get ConfigTemplate(): ConfigTemplateEntry[];
|
||||||
|
|
||||||
|
|
||||||
public get Progress(): JobProgressDTO {
|
public get Progress(): JobProgressDTO {
|
||||||
return this.progress;
|
return this.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(config: T): Promise<void> {
|
public start(config: T, onFinishCB = () => {
|
||||||
|
}): Promise<void> {
|
||||||
|
this.OnFinishCB = onFinishCB;
|
||||||
if (this.state === JobState.idle && this.Supported) {
|
if (this.state === JobState.idle && this.Supported) {
|
||||||
Logger.info(LOG_TAG, 'Running job: ' + this.Name);
|
Logger.info(LOG_TAG, 'Running job: ' + this.Name);
|
||||||
this.config = config;
|
this.config = config;
|
||||||
@ -72,6 +70,9 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected OnFinishCB = () => {
|
||||||
|
};
|
||||||
|
|
||||||
protected abstract async step(): Promise<JobProgressDTO>;
|
protected abstract async step(): Promise<JobProgressDTO>;
|
||||||
|
|
||||||
protected abstract async init(): Promise<void>;
|
protected abstract async init(): Promise<void>;
|
||||||
@ -85,6 +86,7 @@ export abstract class Job<T = void> implements IJob<T> {
|
|||||||
if (this.IsInstant) {
|
if (this.IsInstant) {
|
||||||
this.prResolve();
|
this.prResolve();
|
||||||
}
|
}
|
||||||
|
this.OnFinishCB();
|
||||||
}
|
}
|
||||||
|
|
||||||
private run() {
|
private run() {
|
||||||
|
@ -104,10 +104,10 @@ export class SettingsRouter {
|
|||||||
SettingsMWs.updateIndexingSettings,
|
SettingsMWs.updateIndexingSettings,
|
||||||
RenderingMWs.renderOK
|
RenderingMWs.renderOK
|
||||||
);
|
);
|
||||||
app.put('/api/settings/tasks',
|
app.put('/api/settings/jobs',
|
||||||
AuthenticationMWs.authenticate,
|
AuthenticationMWs.authenticate,
|
||||||
AuthenticationMWs.authorise(UserRoles.Admin),
|
AuthenticationMWs.authorise(UserRoles.Admin),
|
||||||
SettingsMWs.updateTasksSettings,
|
SettingsMWs.updateJobSettings,
|
||||||
RenderingMWs.renderOK
|
RenderingMWs.renderOK
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -76,17 +76,15 @@ export class PrivateConfigDefaultsClass extends PublicConfigClass implements IPr
|
|||||||
},
|
},
|
||||||
Jobs: {
|
Jobs: {
|
||||||
scheduled: [{
|
scheduled: [{
|
||||||
|
name: DefaultsJobs[DefaultsJobs['Database Reset']],
|
||||||
jobName: DefaultsJobs[DefaultsJobs['Database Reset']],
|
jobName: DefaultsJobs[DefaultsJobs['Database Reset']],
|
||||||
config: {},
|
config: {},
|
||||||
trigger: {type: JobTriggerType.never}
|
trigger: {type: JobTriggerType.never}
|
||||||
}, {
|
}, {
|
||||||
|
name: DefaultsJobs[DefaultsJobs.Indexing],
|
||||||
jobName: DefaultsJobs[DefaultsJobs.Indexing],
|
jobName: DefaultsJobs[DefaultsJobs.Indexing],
|
||||||
config: {},
|
config: {},
|
||||||
trigger: {type: JobTriggerType.never}
|
trigger: {type: JobTriggerType.never}
|
||||||
}, {
|
|
||||||
jobName: DefaultsJobs[DefaultsJobs['Video Converting']],
|
|
||||||
config: {},
|
|
||||||
trigger: {type: JobTriggerType.never}
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export enum JobTriggerType {
|
export enum JobTriggerType {
|
||||||
never = 1, scheduled = 2, periodic = 3
|
never = 1, scheduled = 2, periodic = 3, after = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JobTrigger {
|
export interface JobTrigger {
|
||||||
@ -21,8 +21,64 @@ export interface PeriodicJobTrigger extends JobTrigger {
|
|||||||
atTime: number; // day time
|
atTime: number; // day time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AfterJobTrigger extends JobTrigger {
|
||||||
|
type: JobTriggerType.after;
|
||||||
|
afterScheduleName: string; // runs after schedule
|
||||||
|
}
|
||||||
|
|
||||||
export interface JobScheduleDTO {
|
export interface JobScheduleDTO {
|
||||||
|
name: string;
|
||||||
jobName: string;
|
jobName: string;
|
||||||
config: any;
|
config: any;
|
||||||
trigger: NeverJobTrigger | ScheduledJobTrigger | PeriodicJobTrigger;
|
trigger: NeverJobTrigger | ScheduledJobTrigger | PeriodicJobTrigger | AfterJobTrigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export module JobScheduleDTO {
|
||||||
|
|
||||||
|
const getNextDayOfTheWeek = (refDate: Date, dayOfWeek: number) => {
|
||||||
|
const date = new Date(refDate);
|
||||||
|
date.setDate(refDate.getDate() + (dayOfWeek + 1 + 7 - refDate.getDay()) % 7);
|
||||||
|
if (date.getDay() === refDate.getDay()) {
|
||||||
|
return new Date(refDate);
|
||||||
|
}
|
||||||
|
date.setHours(0, 0, 0, 0);
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextValidDate = (date: Date, h: number, m: number, dayDiff: number): Date => {
|
||||||
|
|
||||||
|
date.setSeconds(0);
|
||||||
|
if (date.getHours() < h || (date.getHours() === h && date.getMinutes() < m)) {
|
||||||
|
date.setHours(h);
|
||||||
|
date.setMinutes(m);
|
||||||
|
} else {
|
||||||
|
date.setTime(date.getTime() + dayDiff);
|
||||||
|
date.setHours(h);
|
||||||
|
date.setMinutes(m);
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getNextRunningDate = (refDate: Date, schedule: JobScheduleDTO): Date => {
|
||||||
|
switch (schedule.trigger.type) {
|
||||||
|
case JobTriggerType.scheduled:
|
||||||
|
return new Date(schedule.trigger.time);
|
||||||
|
|
||||||
|
case JobTriggerType.periodic:
|
||||||
|
|
||||||
|
|
||||||
|
const hour = Math.floor(schedule.trigger.atTime / 1000 / (60 * 60));
|
||||||
|
const minute = (schedule.trigger.atTime / 1000 / 60) % 60;
|
||||||
|
|
||||||
|
if (schedule.trigger.periodicity <= 6) { // Between Monday and Sunday
|
||||||
|
const nextRunDate = getNextDayOfTheWeek(refDate, schedule.trigger.periodicity);
|
||||||
|
return nextValidDate(nextRunDate, hour, minute, 7 * 24 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// every day
|
||||||
|
return nextValidDate(new Date(refDate), hour, minute, 24 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -64,47 +64,47 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<app-settings-basic #setting #basic
|
<!-- <app-settings-basic #setting #basic-->
|
||||||
[simplifiedMode]="simplifiedMode"
|
<!-- [simplifiedMode]="simplifiedMode"-->
|
||||||
[hidden]="!basic.HasAvailableSettings"></app-settings-basic>
|
<!-- [hidden]="!basic.HasAvailableSettings"></app-settings-basic>-->
|
||||||
<app-settings-usermanager #setting #userManager
|
<!-- <app-settings-usermanager #setting #userManager-->
|
||||||
[hidden]="!userManager.HasAvailableSettings"></app-settings-usermanager>
|
<!-- [hidden]="!userManager.HasAvailableSettings"></app-settings-usermanager>-->
|
||||||
<app-settings-database #setting #database
|
<!-- <app-settings-database #setting #database-->
|
||||||
[simplifiedMode]="simplifiedMode"
|
<!-- [simplifiedMode]="simplifiedMode"-->
|
||||||
[hidden]="!database.HasAvailableSettings"></app-settings-database>
|
<!-- [hidden]="!database.HasAvailableSettings"></app-settings-database>-->
|
||||||
<app-settings-photo #setting #photo
|
<!-- <app-settings-photo #setting #photo-->
|
||||||
[hidden]="!photo.HasAvailableSettings"
|
<!-- [hidden]="!photo.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-photo>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-photo>-->
|
||||||
<app-settings-video #setting #video
|
<!-- <app-settings-video #setting #video-->
|
||||||
[hidden]="!video.HasAvailableSettings"
|
<!-- [hidden]="!video.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-video>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-video>-->
|
||||||
<app-settings-thumbnail #setting #thumbnail
|
<!-- <app-settings-thumbnail #setting #thumbnail-->
|
||||||
[hidden]="!thumbnail.HasAvailableSettings"
|
<!-- [hidden]="!thumbnail.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-thumbnail>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-thumbnail>-->
|
||||||
<app-settings-search #setting #search
|
<!-- <app-settings-search #setting #search-->
|
||||||
[hidden]="!search.HasAvailableSettings"
|
<!-- [hidden]="!search.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-search>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-search>-->
|
||||||
<app-settings-share #setting #share
|
<!-- <app-settings-share #setting #share-->
|
||||||
[hidden]="!share.HasAvailableSettings"
|
<!-- [hidden]="!share.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-share>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-share>-->
|
||||||
<app-settings-map #setting #map
|
<!-- <app-settings-map #setting #map-->
|
||||||
[hidden]="!map.HasAvailableSettings"
|
<!-- [hidden]="!map.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-map>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-map>-->
|
||||||
<app-settings-meta-file #setting #metaFile
|
<!-- <app-settings-meta-file #setting #metaFile-->
|
||||||
[hidden]="!metaFile.HasAvailableSettings"
|
<!-- [hidden]="!metaFile.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-meta-file>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-meta-file>-->
|
||||||
<app-settings-other #setting #other
|
<!-- <app-settings-other #setting #other-->
|
||||||
[hidden]="!other.HasAvailableSettings"
|
<!-- [hidden]="!other.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-other>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-other>-->
|
||||||
<app-settings-random-photo #setting #random
|
<!-- <app-settings-random-photo #setting #random-->
|
||||||
[hidden]="!random.HasAvailableSettings"
|
<!-- [hidden]="!random.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-random-photo>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-random-photo>-->
|
||||||
<app-settings-faces #setting #faces
|
<!-- <app-settings-faces #setting #faces-->
|
||||||
[hidden]="!faces.HasAvailableSettings"
|
<!-- [hidden]="!faces.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-faces>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-faces>-->
|
||||||
<app-settings-indexing #setting #indexing
|
<!-- <app-settings-indexing #setting #indexing-->
|
||||||
[hidden]="!indexing.HasAvailableSettings"
|
<!-- [hidden]="!indexing.HasAvailableSettings"-->
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-indexing>
|
<!-- [simplifiedMode]="simplifiedMode"></app-settings-indexing>-->
|
||||||
<app-settings-jobs #setting #jobs
|
<app-settings-jobs #setting #jobs
|
||||||
[hidden]="!jobs.HasAvailableSettings"
|
[hidden]="!jobs.HasAvailableSettings"
|
||||||
[simplifiedMode]="simplifiedMode"></app-settings-jobs>
|
[simplifiedMode]="simplifiedMode"></app-settings-jobs>
|
||||||
|
@ -27,8 +27,8 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
|
|||||||
public inProgress = false;
|
public inProgress = false;
|
||||||
public error: string = null;
|
public error: string = null;
|
||||||
public changed = false;
|
public changed = false;
|
||||||
public settings: T = <any>{};
|
public settings: T = <T>{};
|
||||||
public original: T = <any>{};
|
public original: T = <T>{};
|
||||||
text = {
|
text = {
|
||||||
Enabled: 'Enabled',
|
Enabled: 'Enabled',
|
||||||
Disabled: 'Disabled',
|
Disabled: 'Disabled',
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
<form #settingsForm="ngForm" class="form-horizontal">
|
<form #settingsForm="ngForm" class="form-horizontal">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<h5 class="card-header">
|
<h5 class="card-header">
|
||||||
{{Name}}<ng-container *ngIf="changed">*</ng-container>
|
{{Name}}
|
||||||
|
<ng-container *ngIf="changed">*</ng-container>
|
||||||
</h5>
|
</h5>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
|
||||||
|
|
||||||
<div *ngFor="let schedule of settings.scheduled; let i= index">
|
<div *ngFor="let schedule of sortedSchedules() as schedules; let i= index">
|
||||||
<div class="card bg-light">
|
<div class="card bg-light {{shouldIdent(schedule,schedules[i-1])? 'ml-4' : ''}}">
|
||||||
<div class="card-header clickable" (click)="showDetails[i]=!showDetails[i]">
|
<div class="card-header clickable" (click)="showDetails[schedule.name]=!showDetails[schedule.name]">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
{{schedule.jobName}} @<!--
|
{{schedule.name}} @<!--
|
||||||
-->
|
-->
|
||||||
<ng-container [ngSwitch]="schedule.trigger.type">
|
<ng-container [ngSwitch]="schedule.trigger.type">
|
||||||
<ng-container *ngSwitchCase="JobTriggerType.periodic">
|
<ng-container *ngSwitchCase="JobTriggerType.periodic">
|
||||||
@ -20,28 +21,24 @@
|
|||||||
<ng-container
|
<ng-container
|
||||||
*ngSwitchCase="JobTriggerType.scheduled">{{schedule.trigger.time | date:"medium"}}</ng-container>
|
*ngSwitchCase="JobTriggerType.scheduled">{{schedule.trigger.time | date:"medium"}}</ng-container>
|
||||||
<ng-container *ngSwitchCase="JobTriggerType.never" i18n>never</ng-container>
|
<ng-container *ngSwitchCase="JobTriggerType.never" i18n>never</ng-container>
|
||||||
|
<ng-container *ngSwitchCase="JobTriggerType.after">
|
||||||
|
<ng-container i18n>after</ng-container>
|
||||||
|
{{schedule.trigger.afterScheduleName}}
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<button class="btn btn-danger button-delete" (click)="remove(i)"><span class="oi oi-trash"></span>
|
<button class="btn btn-danger button-delete" (click)="remove(i)"><span class="oi oi-trash"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body" [hidden]="!showDetails[i]">
|
<div class="card-body" [hidden]="!showDetails[schedule.name]">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-md-2 control-label" [for]="'jobName'+i" i18n>Job:</label>
|
<label class="col-md-2 control-label" [for]="'jobName'+i" i18n>Job:</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<select class="form-control" (change)="jobTypeChanged(schedule)" [(ngModel)]="schedule.jobName"
|
{{schedule.jobName}}
|
||||||
[name]="'jobName'+i" required>
|
|
||||||
<option *ngFor="let availableJob of _settingsService.availableJobs | async"
|
|
||||||
[ngValue]="availableJob.Name">{{availableJob.Name}}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<small class="form-text text-muted"
|
|
||||||
i18n>Select a job to schedule.
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
@ -60,6 +57,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group 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-control" [(ngModel)]="schedule.trigger.afterScheduleName"
|
||||||
|
[name]="'triggerAfter'+i" required>
|
||||||
|
<ng-container *ngFor="let sch of settings.scheduled">
|
||||||
|
<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="form-group row" *ngIf="schedule.trigger.type == JobTriggerType.scheduled">
|
<div class="form-group row" *ngIf="schedule.trigger.type == JobTriggerType.scheduled">
|
||||||
<label class="col-md-2 control-label" [for]="'triggerTime'+i" i18n>At:</label>
|
<label class="col-md-2 control-label" [for]="'triggerTime'+i" i18n>At:</label>
|
||||||
@ -191,10 +205,47 @@
|
|||||||
(click)="reset()" i18n>Reset
|
(click)="reset()" i18n>Reset
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary float-right"
|
<button class="btn btn-primary float-right"
|
||||||
(click)="addNewJob()" i18n>+ Add Job
|
(click)="prepareNewJob()" i18n>+ Add Job
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 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="close" (click)="jobModal.hide()" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form #jobModalForm="ngForm">
|
||||||
|
<div class="modal-body">
|
||||||
|
<select class="form-control" (change)="jobTypeChanged(newSchedule)" [(ngModel)]="newSchedule.jobName"
|
||||||
|
name="newJobName" required>
|
||||||
|
<option *ngFor="let availableJob of _settingsService.availableJobs | async"
|
||||||
|
[ngValue]="availableJob.Name">{{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>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, OnChanges, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnChanges, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||||
import {JobsSettingsService} from './jobs.settings.service';
|
import {JobsSettingsService} from './jobs.settings.service';
|
||||||
import {AuthenticationService} from '../../../model/network/authentication.service';
|
import {AuthenticationService} from '../../../model/network/authentication.service';
|
||||||
import {NavigationService} from '../../../model/navigation.service';
|
import {NavigationService} from '../../../model/navigation.service';
|
||||||
@ -8,6 +8,7 @@ import {I18n} from '@ngx-translate/i18n-polyfill';
|
|||||||
import {ErrorDTO} from '../../../../../common/entities/Error';
|
import {ErrorDTO} from '../../../../../common/entities/Error';
|
||||||
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
import {ScheduledJobsService} from '../scheduled-jobs.service';
|
||||||
import {
|
import {
|
||||||
|
AfterJobTrigger,
|
||||||
JobScheduleDTO,
|
JobScheduleDTO,
|
||||||
JobTriggerType,
|
JobTriggerType,
|
||||||
NeverJobTrigger,
|
NeverJobTrigger,
|
||||||
@ -18,6 +19,8 @@ import {Utils} from '../../../../../common/Utils';
|
|||||||
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||||
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
|
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
|
||||||
import {JobState} from '../../../../../common/entities/settings/JobProgressDTO';
|
import {JobState} from '../../../../../common/entities/settings/JobProgressDTO';
|
||||||
|
import {Job} from '../../../../../backend/model/jobs/jobs/Job';
|
||||||
|
import {ModalDirective} from 'ngx-bootstrap/modal';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-settings-jobs',
|
selector: 'app-settings-jobs',
|
||||||
@ -29,12 +32,21 @@ import {JobState} from '../../../../../common/entities/settings/JobProgressDTO';
|
|||||||
export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobConfig, JobsSettingsService>
|
export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobConfig, JobsSettingsService>
|
||||||
implements OnInit, OnDestroy, OnChanges {
|
implements OnInit, OnDestroy, OnChanges {
|
||||||
|
|
||||||
|
@ViewChild('jobModal', {static: false}) public jobModal: ModalDirective;
|
||||||
disableButtons = false;
|
disableButtons = false;
|
||||||
JobTriggerTypeMap: { key: number, value: string }[];
|
JobTriggerTypeMap: { key: number, value: string }[];
|
||||||
JobTriggerType = JobTriggerType;
|
JobTriggerType = JobTriggerType;
|
||||||
periods: string[] = [];
|
periods: string[] = [];
|
||||||
showDetails: boolean[] = [];
|
showDetails: boolean[] = [];
|
||||||
JobState = JobState;
|
JobState = JobState;
|
||||||
|
newSchedule: JobScheduleDTO = {
|
||||||
|
name: '',
|
||||||
|
config: null,
|
||||||
|
jobName: '',
|
||||||
|
trigger: {
|
||||||
|
type: JobTriggerType.never
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
constructor(_authService: AuthenticationService,
|
constructor(_authService: AuthenticationService,
|
||||||
_navigation: NavigationService,
|
_navigation: NavigationService,
|
||||||
@ -63,7 +75,6 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
this.i18n('day')]; // 7
|
this.i18n('day')]; // 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
|
getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
|
||||||
const job = this._settingsService.availableJobs.value.find(t => t.Name === JobName);
|
const job = this._settingsService.availableJobs.value.find(t => t.Name === JobName);
|
||||||
if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) {
|
if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) {
|
||||||
@ -83,7 +94,6 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
this.jobsService.unsubscribeFromProgress();
|
this.jobsService.unsubscribeFromProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async start(schedule: JobScheduleDTO) {
|
public async start(schedule: JobScheduleDTO) {
|
||||||
this.error = '';
|
this.error = '';
|
||||||
try {
|
try {
|
||||||
@ -126,7 +136,6 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
this.settings.scheduled.splice(index, 1);
|
this.settings.scheduled.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
jobTypeChanged(schedule: JobScheduleDTO) {
|
jobTypeChanged(schedule: JobScheduleDTO) {
|
||||||
const job = this._settingsService.availableJobs.value.find(t => t.Name === schedule.jobName);
|
const job = this._settingsService.availableJobs.value.find(t => t.Name === schedule.jobName);
|
||||||
schedule.config = schedule.config || {};
|
schedule.config = schedule.config || {};
|
||||||
@ -135,9 +144,10 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewJob() {
|
prepareNewJob() {
|
||||||
const jobName = this._settingsService.availableJobs.value[0].Name;
|
const jobName = this._settingsService.availableJobs.value[0].Name;
|
||||||
const newSchedule: JobScheduleDTO = {
|
this.newSchedule = {
|
||||||
|
name: 'new job',
|
||||||
jobName: jobName,
|
jobName: jobName,
|
||||||
config: <any>{},
|
config: <any>{},
|
||||||
trigger: {
|
trigger: {
|
||||||
@ -146,11 +156,12 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
};
|
};
|
||||||
|
|
||||||
const job = this._settingsService.availableJobs.value.find(t => t.Name === jobName);
|
const job = this._settingsService.availableJobs.value.find(t => t.Name === jobName);
|
||||||
newSchedule.config = newSchedule.config || {};
|
this.newSchedule.config = this.newSchedule.config || {};
|
||||||
if (job.ConfigTemplate) {
|
if (job.ConfigTemplate) {
|
||||||
job.ConfigTemplate.forEach(ct => newSchedule.config[ct.id] = ct.defaultValue);
|
job.ConfigTemplate.forEach(ct => this.newSchedule.config[ct.id] = ct.defaultValue);
|
||||||
}
|
}
|
||||||
this.settings.scheduled.push(newSchedule);
|
|
||||||
|
this.jobModal.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
jobTriggerTypeChanged(triggerType: JobTriggerType, schedule: JobScheduleDTO) {
|
jobTriggerTypeChanged(triggerType: JobTriggerType, schedule: JobScheduleDTO) {
|
||||||
@ -183,6 +194,39 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
|||||||
return configElement[id].join('; ');
|
return configElement[id].join('; ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public shouldIdent(curr: JobScheduleDTO, prev: JobScheduleDTO) {
|
||||||
|
return curr && curr.trigger.type === JobTriggerType.after && prev && prev.name === curr.trigger.afterScheduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sortedSchedules() {
|
||||||
|
return this.settings.scheduled.slice().sort((a, b) => {
|
||||||
|
return this.getNextRunningDate(a, this.settings.scheduled) - this.getNextRunningDate(b, this.settings.scheduled);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewJob() {
|
||||||
|
const jobName = this.newSchedule.jobName;
|
||||||
|
const count = this.settings.scheduled.filter(s => s.jobName === jobName).length;
|
||||||
|
this.newSchedule.name = count === 0 ? jobName : jobName + ' ' + (count + 1);
|
||||||
|
this.settings.scheduled.push(this.newSchedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNextRunningDate(sch: JobScheduleDTO, list: JobScheduleDTO[], depth: number = 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 === (<AfterJobTrigger>sch.trigger).afterScheduleName);
|
||||||
|
if (parent) {
|
||||||
|
return this.getNextRunningDate(parent, list, depth + 1) + 0.001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const d = JobScheduleDTO.getNextRunningDate(new Date(), sch);
|
||||||
|
return d !== null ? d.getTime() : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user