mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-01-10 04:07:35 +02:00
Refactoring messenger to prepare extension support #753
This commit is contained in:
parent
7208a3b4fe
commit
50b8f7a81d
@ -2,18 +2,19 @@ import {NextFunction, Request, Response} from 'express';
|
|||||||
import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
|
import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error';
|
||||||
import {ObjectManagers} from '../../model/ObjectManagers';
|
import {ObjectManagers} from '../../model/ObjectManagers';
|
||||||
import {StatisticDTO} from '../../../common/entities/settings/StatisticDTO';
|
import {StatisticDTO} from '../../../common/entities/settings/StatisticDTO';
|
||||||
|
import {MessengerRepository} from '../../model/messenger/MessengerRepository';
|
||||||
|
|
||||||
export class AdminMWs {
|
export class AdminMWs {
|
||||||
public static async loadStatistic(
|
public static async loadStatistic(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|
||||||
const galleryManager = ObjectManagers.getInstance()
|
const galleryManager = ObjectManagers.getInstance()
|
||||||
.GalleryManager;
|
.GalleryManager;
|
||||||
const personManager = ObjectManagers.getInstance()
|
const personManager = ObjectManagers.getInstance()
|
||||||
.PersonManager;
|
.PersonManager;
|
||||||
try {
|
try {
|
||||||
req.resultPipe = {
|
req.resultPipe = {
|
||||||
directories: await galleryManager.countDirectories(),
|
directories: await galleryManager.countDirectories(),
|
||||||
@ -26,87 +27,87 @@ export class AdminMWs {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.GENERAL_ERROR,
|
ErrorCodes.GENERAL_ERROR,
|
||||||
'Error while getting statistic: ' + err.toString(),
|
'Error while getting statistic: ' + err.toString(),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.GENERAL_ERROR,
|
ErrorCodes.GENERAL_ERROR,
|
||||||
'Error while getting statistic',
|
'Error while getting statistic',
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getDuplicates(
|
public static async getDuplicates(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.resultPipe = await ObjectManagers.getInstance()
|
req.resultPipe = await ObjectManagers.getInstance()
|
||||||
.GalleryManager.getPossibleDuplicates();
|
.GalleryManager.getPossibleDuplicates();
|
||||||
return next();
|
return next();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.GENERAL_ERROR,
|
ErrorCodes.GENERAL_ERROR,
|
||||||
'Error while getting duplicates: ' + err.toString(),
|
'Error while getting duplicates: ' + err.toString(),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.GENERAL_ERROR,
|
ErrorCodes.GENERAL_ERROR,
|
||||||
'Error while getting duplicates',
|
'Error while getting duplicates',
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async startJob(
|
public static async startJob(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const id = req.params['id'];
|
const id = req.params['id'];
|
||||||
const JobConfig: unknown = req.body.config;
|
const JobConfig: Record<string, unknown> = req.body.config;
|
||||||
const soloRun: boolean = req.body.soloRun;
|
const soloRun: boolean = req.body.soloRun;
|
||||||
const allowParallelRun: boolean = req.body.allowParallelRun;
|
const allowParallelRun: boolean = req.body.allowParallelRun;
|
||||||
await ObjectManagers.getInstance().JobManager.run(
|
await ObjectManagers.getInstance().JobManager.run(
|
||||||
id,
|
id,
|
||||||
JobConfig,
|
JobConfig,
|
||||||
soloRun,
|
soloRun,
|
||||||
allowParallelRun
|
allowParallelRun
|
||||||
);
|
);
|
||||||
req.resultPipe = 'ok';
|
req.resultPipe = 'ok';
|
||||||
return next();
|
return next();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + err.toString(),
|
'Job error: ' + err.toString(),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + JSON.stringify(err, null, ' '),
|
'Job error: ' + JSON.stringify(err, null, ' '),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,56 +121,85 @@ export class AdminMWs {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + err.toString(),
|
'Job error: ' + err.toString(),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return next(
|
return next(
|
||||||
|
new ErrorDTO(
|
||||||
|
ErrorCodes.JOB_ERROR,
|
||||||
|
'Job error: ' + JSON.stringify(err, null, ' '),
|
||||||
|
err
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static getAvailableMessengers(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): void {
|
||||||
|
try {
|
||||||
|
req.resultPipe = MessengerRepository.Instance.getAll().map(msgr => msgr.Name);
|
||||||
|
return next();
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + JSON.stringify(err, null, ' '),
|
'Messenger error: ' + err.toString(),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return next(
|
||||||
|
new ErrorDTO(
|
||||||
|
ErrorCodes.JOB_ERROR,
|
||||||
|
'Messenger error: ' + JSON.stringify(err, null, ' '),
|
||||||
|
err
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getAvailableJobs(
|
public static getAvailableJobs(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
): void {
|
): void {
|
||||||
try {
|
try {
|
||||||
req.resultPipe =
|
req.resultPipe =
|
||||||
ObjectManagers.getInstance().JobManager.getAvailableJobs();
|
ObjectManagers.getInstance().JobManager.getAvailableJobs();
|
||||||
return next();
|
return next();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + err.toString(),
|
'Job error: ' + err.toString(),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + JSON.stringify(err, null, ' '),
|
'Job error: ' + JSON.stringify(err, null, ' '),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getJobProgresses(
|
public static getJobProgresses(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
): void {
|
): void {
|
||||||
try {
|
try {
|
||||||
req.resultPipe = ObjectManagers.getInstance().JobManager.getProgresses();
|
req.resultPipe = ObjectManagers.getInstance().JobManager.getProgresses();
|
||||||
@ -177,19 +207,19 @@ export class AdminMWs {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + err.toString(),
|
'Job error: ' + err.toString(),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return next(
|
return next(
|
||||||
new ErrorDTO(
|
new ErrorDTO(
|
||||||
ErrorCodes.JOB_ERROR,
|
ErrorCodes.JOB_ERROR,
|
||||||
'Job error: ' + JSON.stringify(err, null, ' '),
|
'Job error: ' + JSON.stringify(err, null, ' '),
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ export class JobManager implements IJobListener, IObjectManager {
|
|||||||
return prg;
|
return prg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async run<T>(
|
public async run<T extends Record<string, unknown>>(
|
||||||
jobName: string,
|
jobName: string,
|
||||||
config: T,
|
config: T,
|
||||||
soloRun: boolean,
|
soloRun: boolean,
|
||||||
@ -86,7 +86,7 @@ export class JobManager implements IJobListener, IObjectManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onJobFinished = async (
|
onJobFinished = async (
|
||||||
job: IJob<unknown>,
|
job: IJob,
|
||||||
state: JobProgressStates,
|
state: JobProgressStates,
|
||||||
soloRun: boolean
|
soloRun: boolean
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
@ -121,7 +121,7 @@ export class JobManager implements IJobListener, IObjectManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getAvailableJobs(): IJob<unknown>[] {
|
getAvailableJobs(): IJob[] {
|
||||||
return JobRepository.Instance.getAvailableJobs();
|
return JobRepository.Instance.getAvailableJobs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ export class JobManager implements IJobListener, IObjectManager {
|
|||||||
Config.Jobs.scheduled.forEach((s): void => this.runSchedule(s));
|
Config.Jobs.scheduled.forEach((s): void => this.runSchedule(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected findJob<T = unknown>(jobName: string): IJob<T> {
|
protected findJob(jobName: string): IJob {
|
||||||
return this.getAvailableJobs().find((t): boolean => t.Name === jobName);
|
return this.getAvailableJobs().find((t): boolean => t.Name === jobName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import {AlbumCoverRestJob} from './jobs/AlbumCoverResetJob';
|
|||||||
|
|
||||||
export class JobRepository {
|
export class JobRepository {
|
||||||
private static instance: JobRepository = null;
|
private static instance: JobRepository = null;
|
||||||
availableJobs: { [key: string]: IJob<unknown> } = {};
|
availableJobs: { [key: string]: IJob } = {};
|
||||||
|
|
||||||
public static get Instance(): JobRepository {
|
public static get Instance(): JobRepository {
|
||||||
if (JobRepository.instance == null) {
|
if (JobRepository.instance == null) {
|
||||||
@ -23,11 +23,11 @@ export class JobRepository {
|
|||||||
return JobRepository.instance;
|
return JobRepository.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvailableJobs(): IJob<unknown>[] {
|
getAvailableJobs(): IJob[] {
|
||||||
return Object.values(this.availableJobs).filter((t) => t.Supported);
|
return Object.values(this.availableJobs).filter((t) => t.Supported);
|
||||||
}
|
}
|
||||||
|
|
||||||
register(job: IJob<unknown>): void {
|
register(job: IJob): void {
|
||||||
if (typeof this.availableJobs[job.Name] !== 'undefined') {
|
if (typeof this.availableJobs[job.Name] !== 'undefined') {
|
||||||
throw new Error('Job already exist:' + job.Name);
|
throw new Error('Job already exist:' + job.Name);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import {JobDTO} from '../../../../common/entities/job/JobDTO';
|
|||||||
import {JobProgress} from './JobProgress';
|
import {JobProgress} from './JobProgress';
|
||||||
import {IJobListener} from './IJobListener';
|
import {IJobListener} from './IJobListener';
|
||||||
|
|
||||||
export interface IJob<T> extends JobDTO {
|
export interface IJob<T extends Record<string, unknown> = Record<string, unknown>> extends JobDTO {
|
||||||
Name: string;
|
Name: string;
|
||||||
Supported: boolean;
|
Supported: boolean;
|
||||||
Progress: JobProgress;
|
Progress: JobProgress;
|
||||||
|
@ -4,7 +4,7 @@ import {JobProgressStates} from '../../../../common/entities/job/JobProgressDTO'
|
|||||||
|
|
||||||
export interface IJobListener {
|
export interface IJobListener {
|
||||||
onJobFinished(
|
onJobFinished(
|
||||||
job: IJob<unknown>,
|
job: IJob,
|
||||||
state: JobProgressStates,
|
state: JobProgressStates,
|
||||||
soloRun: boolean
|
soloRun: boolean
|
||||||
): void;
|
): void;
|
||||||
|
@ -35,7 +35,7 @@ export class ThumbnailGenerationJob extends FileJob<{
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!config || !config.sizes || !Array.isArray(config.sizes) || config.sizes.length === 0) {
|
if (!config || !config.sizes || !Array.isArray(config.sizes) || config.sizes.length === 0) {
|
||||||
config = config || {};
|
config = config || {};
|
||||||
config.sizes = this.ConfigTemplate.find(ct => ct.id == 'sizes').defaultValue;
|
config.sizes = this.ConfigTemplate.find(ct => ct.id == 'sizes').defaultValue as number[];
|
||||||
}
|
}
|
||||||
for (const item of config.sizes) {
|
for (const item of config.sizes) {
|
||||||
if (Config.Media.Thumbnail.thumbnailSizes.indexOf(item) === -1) {
|
if (Config.Media.Thumbnail.thumbnailSizes.indexOf(item) === -1) {
|
||||||
|
@ -1,64 +1,67 @@
|
|||||||
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
|
import {DefaultMessengers, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
|
||||||
import {Job} from './Job';
|
import {Job} from './Job';
|
||||||
import {backendTexts} from '../../../../common/BackendTexts';
|
import {backendTexts} from '../../../../common/BackendTexts';
|
||||||
import {SortByTypes} from '../../../../common/entities/SortingMethods';
|
import {SortByTypes} from '../../../../common/entities/SortingMethods';
|
||||||
import {DatePatternFrequency, DatePatternSearch, SearchQueryTypes} from '../../../../common/entities/SearchQueryDTO';
|
import {DatePatternFrequency, DatePatternSearch, SearchQueryTypes} from '../../../../common/entities/SearchQueryDTO';
|
||||||
import {ObjectManagers} from '../../ObjectManagers';
|
import {ObjectManagers} from '../../ObjectManagers';
|
||||||
import {PhotoEntity} from '../../database/enitites/PhotoEntity';
|
import {PhotoEntity} from '../../database/enitites/PhotoEntity';
|
||||||
import {EmailMediaMessenger} from '../../mediamessengers/EmailMediaMessenger';
|
|
||||||
import {MediaPickDTO} from '../../../../common/entities/MediaPickDTO';
|
import {MediaPickDTO} from '../../../../common/entities/MediaPickDTO';
|
||||||
import {MediaDTOUtils} from '../../../../common/entities/MediaDTO';
|
import {MediaDTOUtils} from '../../../../common/entities/MediaDTO';
|
||||||
import {DynamicConfig} from '../../../../common/entities/DynamicConfig';
|
import {DynamicConfig} from '../../../../common/entities/DynamicConfig';
|
||||||
|
import {MessengerRepository} from '../../messenger/MessengerRepository';
|
||||||
|
import {Utils} from '../../../../common/Utils';
|
||||||
|
|
||||||
|
|
||||||
export class TopPickSendJob extends Job<{
|
export class TopPickSendJob extends Job<{
|
||||||
mediaPick: MediaPickDTO[],
|
mediaPick: MediaPickDTO[],
|
||||||
|
messenger: string,
|
||||||
emailTo: string,
|
emailTo: string,
|
||||||
emailFrom: string,
|
|
||||||
emailSubject: string,
|
emailSubject: string,
|
||||||
emailText: string,
|
emailText: string,
|
||||||
}> {
|
}> {
|
||||||
public readonly Name = DefaultsJobs[DefaultsJobs['Top Pick Sending']];
|
public readonly Name = DefaultsJobs[DefaultsJobs['Top Pick Sending']];
|
||||||
public readonly Supported: boolean = true;
|
public readonly Supported: boolean = true;
|
||||||
public readonly ConfigTemplate: DynamicConfig[] = [
|
public readonly ConfigTemplate: DynamicConfig[];
|
||||||
{
|
|
||||||
id: 'mediaPick',
|
|
||||||
type: 'MediaPickDTO-array',
|
|
||||||
name: backendTexts.mediaPick.name,
|
|
||||||
description: backendTexts.mediaPick.description,
|
|
||||||
defaultValue: [{
|
|
||||||
searchQuery: {
|
|
||||||
type: SearchQueryTypes.date_pattern,
|
|
||||||
daysLength: 7,
|
|
||||||
frequency: DatePatternFrequency.every_year
|
|
||||||
} as DatePatternSearch,
|
|
||||||
sortBy: [{method: SortByTypes.Rating, ascending: false},
|
|
||||||
{method: SortByTypes.PersonCount, ascending: false}],
|
|
||||||
pick: 5
|
|
||||||
}] as MediaPickDTO[],
|
|
||||||
}, {
|
|
||||||
id: 'emailTo',
|
|
||||||
type: 'string-array',
|
|
||||||
name: backendTexts.emailTo.name,
|
|
||||||
description: backendTexts.emailTo.description,
|
|
||||||
defaultValue: [],
|
|
||||||
}, {
|
|
||||||
id: 'emailSubject',
|
|
||||||
type: 'string',
|
|
||||||
name: backendTexts.emailSubject.name,
|
|
||||||
description: backendTexts.emailSubject.description,
|
|
||||||
defaultValue: 'Latest photos for you',
|
|
||||||
}, {
|
|
||||||
id: 'emailText',
|
|
||||||
type: 'string',
|
|
||||||
name: backendTexts.emailText.name,
|
|
||||||
description: backendTexts.emailText.description,
|
|
||||||
defaultValue: 'I hand picked these photos just for you:',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
private status: 'Listing' | 'Sending' = 'Listing';
|
private status: 'Listing' | 'Sending' = 'Listing';
|
||||||
private mediaList: PhotoEntity[] = [];
|
private mediaList: PhotoEntity[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.ConfigTemplate = [
|
||||||
|
{
|
||||||
|
id: 'mediaPick',
|
||||||
|
type: 'MediaPickDTO-array',
|
||||||
|
name: backendTexts.mediaPick.name,
|
||||||
|
description: backendTexts.mediaPick.description,
|
||||||
|
defaultValue: [{
|
||||||
|
searchQuery: {
|
||||||
|
type: SearchQueryTypes.date_pattern,
|
||||||
|
daysLength: 7,
|
||||||
|
frequency: DatePatternFrequency.every_year
|
||||||
|
} as DatePatternSearch,
|
||||||
|
sortBy: [{method: SortByTypes.Rating, ascending: false},
|
||||||
|
{method: SortByTypes.PersonCount, ascending: false}],
|
||||||
|
pick: 5
|
||||||
|
}] as MediaPickDTO[],
|
||||||
|
}, {
|
||||||
|
id: 'messenger',
|
||||||
|
type: 'messenger',
|
||||||
|
name: backendTexts.messenger.name,
|
||||||
|
description: backendTexts.messenger.description,
|
||||||
|
defaultValue: DefaultMessengers[DefaultMessengers.Email]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// add all messenger's config to the config template
|
||||||
|
MessengerRepository.Instance.getAll()
|
||||||
|
.forEach(msgr => Utils.clone(msgr.ConfigTemplate)
|
||||||
|
.forEach(ct => {
|
||||||
|
const c = Utils.clone(ct);
|
||||||
|
c.validIf = {configFiled: 'messenger', equalsValue: msgr.Name};
|
||||||
|
this.ConfigTemplate.push(c);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected async init(): Promise<void> {
|
protected async init(): Promise<void> {
|
||||||
this.status = 'Listing';
|
this.status = 'Listing';
|
||||||
@ -86,15 +89,15 @@ export class TopPickSendJob extends Job<{
|
|||||||
this.mediaList = [];
|
this.mediaList = [];
|
||||||
for (let i = 0; i < this.config.mediaPick.length; ++i) {
|
for (let i = 0; i < this.config.mediaPick.length; ++i) {
|
||||||
const media = await ObjectManagers.getInstance().SearchManager
|
const media = await ObjectManagers.getInstance().SearchManager
|
||||||
.getNMedia(this.config.mediaPick[i].searchQuery, this.config.mediaPick[i].sortBy, this.config.mediaPick[i].pick);
|
.getNMedia(this.config.mediaPick[i].searchQuery, this.config.mediaPick[i].sortBy, this.config.mediaPick[i].pick);
|
||||||
this.Progress.log('Find ' + media.length + ' photos and videos from ' + (i + 1) + '. load');
|
this.Progress.log('Find ' + media.length + ' photos and videos from ' + (i + 1) + '. load');
|
||||||
this.mediaList = this.mediaList.concat(media);
|
this.mediaList = this.mediaList.concat(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make the list unique
|
// make the list unique
|
||||||
this.mediaList = this.mediaList
|
this.mediaList = this.mediaList
|
||||||
.filter((value, index, arr) =>
|
.filter((value, index, arr) =>
|
||||||
arr.findIndex(m => MediaDTOUtils.equals(m, value)) === index);
|
arr.findIndex(m => MediaDTOUtils.equals(m, value)) === index);
|
||||||
|
|
||||||
this.Progress.Processed++;
|
this.Progress.Processed++;
|
||||||
// console.log(this.mediaList);
|
// console.log(this.mediaList);
|
||||||
@ -103,17 +106,16 @@ export class TopPickSendJob extends Job<{
|
|||||||
|
|
||||||
private async stepSending(): Promise<boolean> {
|
private async stepSending(): Promise<boolean> {
|
||||||
if (this.mediaList.length <= 0) {
|
if (this.mediaList.length <= 0) {
|
||||||
this.Progress.log('No photos found skipping e-mail sending.');
|
this.Progress.log('No photos found skipping sending.');
|
||||||
this.Progress.Skipped++;
|
this.Progress.Skipped++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.Progress.log('Sending emails of ' + this.mediaList.length + ' photos.');
|
const msgr = MessengerRepository.Instance.get(this.config.messenger);
|
||||||
const messenger = new EmailMediaMessenger();
|
if (!msgr) {
|
||||||
await messenger.sendMedia({
|
throw new Error('Can\t find "' + this.config.messenger + '" messenger.');
|
||||||
to: this.config.emailTo,
|
}
|
||||||
subject: this.config.emailSubject,
|
this.Progress.log('Sending ' + this.mediaList.length + ' photos.');
|
||||||
text: this.config.emailText
|
await msgr.send(this.config, this.mediaList);
|
||||||
}, this.mediaList);
|
|
||||||
this.Progress.Processed++;
|
this.Progress.Processed++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,40 @@
|
|||||||
import {createTransport, Transporter} from 'nodemailer';
|
import {createTransport, Transporter} from 'nodemailer';
|
||||||
import {MediaDTO, MediaDTOUtils} from '../../../common/entities/MediaDTO';
|
|
||||||
import {Config} from '../../../common/config/private/Config';
|
import {Config} from '../../../common/config/private/Config';
|
||||||
import {PhotoProcessing} from '../fileaccess/fileprocessing/PhotoProcessing';
|
|
||||||
import {ThumbnailSourceType} from '../fileaccess/PhotoWorker';
|
|
||||||
import {ProjectPath} from '../../ProjectPath';
|
|
||||||
import * as path from 'path';
|
|
||||||
import {PhotoMetadata} from '../../../common/entities/PhotoDTO';
|
import {PhotoMetadata} from '../../../common/entities/PhotoDTO';
|
||||||
import {Utils} from '../../../common/Utils';
|
import {MediaDTOWithThPath, Messenger} from './Messenger';
|
||||||
import {QueryParams} from '../../../common/QueryParams';
|
import {backendTexts} from '../../../common/BackendTexts';
|
||||||
|
import {DynamicConfig} from '../../../common/entities/DynamicConfig';
|
||||||
|
import {DefaultMessengers} from '../../../common/entities/job/JobDTO';
|
||||||
|
|
||||||
export class EmailMediaMessenger {
|
export class EmailMessenger extends Messenger<{
|
||||||
|
emailTo: string,
|
||||||
|
emailSubject: string,
|
||||||
|
emailText: string,
|
||||||
|
}> {
|
||||||
|
public readonly Name = DefaultMessengers[DefaultMessengers.Email];
|
||||||
|
public readonly ConfigTemplate: DynamicConfig[] = [{
|
||||||
|
id: 'emailTo',
|
||||||
|
type: 'string-array',
|
||||||
|
name: backendTexts.emailTo.name,
|
||||||
|
description: backendTexts.emailTo.description,
|
||||||
|
defaultValue: [],
|
||||||
|
}, {
|
||||||
|
id: 'emailSubject',
|
||||||
|
type: 'string',
|
||||||
|
name: backendTexts.emailSubject.name,
|
||||||
|
description: backendTexts.emailSubject.description,
|
||||||
|
defaultValue: 'Latest photos for you',
|
||||||
|
}, {
|
||||||
|
id: 'emailText',
|
||||||
|
type: 'string',
|
||||||
|
name: backendTexts.emailText.name,
|
||||||
|
description: backendTexts.emailText.description,
|
||||||
|
defaultValue: 'I hand picked these photos just for you:',
|
||||||
|
}];
|
||||||
transporter: Transporter;
|
transporter: Transporter;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this.transporter = createTransport({
|
this.transporter = createTransport({
|
||||||
host: Config.Messaging.Email.smtp.host,
|
host: Config.Messaging.Email.smtp.host,
|
||||||
port: Config.Messaging.Email.smtp.port,
|
port: Config.Messaging.Email.smtp.port,
|
||||||
@ -25,24 +47,16 @@ export class EmailMediaMessenger {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getThumbnail(m: MediaDTO) {
|
|
||||||
return await PhotoProcessing.generateThumbnail(
|
|
||||||
path.join(ProjectPath.ImageFolder, m.directory.path, m.directory.name, m.name),
|
|
||||||
Config.Media.Thumbnail.thumbnailSizes[0],
|
|
||||||
MediaDTOUtils.isPhoto(m) ? ThumbnailSourceType.Photo : ThumbnailSourceType.Video,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async sendMedia(mailSettings: {
|
protected async sendMedia(mailSettings: {
|
||||||
to: string,
|
emailTo: string,
|
||||||
subject: string,
|
emailSubject: string,
|
||||||
text: string
|
emailText: string
|
||||||
}, media: MediaDTO[]) {
|
}, media: MediaDTOWithThPath[]) {
|
||||||
|
|
||||||
const attachments = [];
|
const attachments = [];
|
||||||
const htmlStart = '<h1 style="text-align: center; margin-bottom: 2em">' + Config.Server.applicationTitle + '</h1>\n' +
|
const htmlStart = '<h1 style="text-align: center; margin-bottom: 2em">' + Config.Server.applicationTitle + '</h1>\n' +
|
||||||
'<h3>' + mailSettings.text + '</h3>\n' +
|
'<h3>' + mailSettings.emailText + '</h3>\n' +
|
||||||
'<table style="margin-left: auto; margin-right: auto;">\n' +
|
'<table style="margin-left: auto; margin-right: auto;">\n' +
|
||||||
' <tbody>\n';
|
' <tbody>\n';
|
||||||
const htmlEnd = ' </tr>\n' +
|
const htmlEnd = ' </tr>\n' +
|
||||||
@ -51,9 +65,6 @@ export class EmailMediaMessenger {
|
|||||||
let htmlMiddle = '';
|
let htmlMiddle = '';
|
||||||
const numberOfColumns = media.length >= 6 ? 3 : 2;
|
const numberOfColumns = media.length >= 6 ? 3 : 2;
|
||||||
for (let i = 0; i < media.length; ++i) {
|
for (let i = 0; i < media.length; ++i) {
|
||||||
const thPath = await this.getThumbnail(media[i]);
|
|
||||||
const linkUrl = Utils.concatUrls(Config.Server.publicUrl, '/gallery/', encodeURIComponent(path.join(media[i].directory.path, media[i].directory.name))) +
|
|
||||||
'?' + QueryParams.gallery.photo + '=' + encodeURIComponent(media[i].name);
|
|
||||||
const location = (media[i].metadata as PhotoMetadata).positionData?.country ?
|
const location = (media[i].metadata as PhotoMetadata).positionData?.country ?
|
||||||
(media[i].metadata as PhotoMetadata).positionData?.country :
|
(media[i].metadata as PhotoMetadata).positionData?.country :
|
||||||
((media[i].metadata as PhotoMetadata).positionData?.city ?
|
((media[i].metadata as PhotoMetadata).positionData?.city ?
|
||||||
@ -61,14 +72,14 @@ export class EmailMediaMessenger {
|
|||||||
const caption = (new Date(media[i].metadata.creationDate)).getFullYear() + (location ? ', ' + location : '');
|
const caption = (new Date(media[i].metadata.creationDate)).getFullYear() + (location ? ', ' + location : '');
|
||||||
attachments.push({
|
attachments.push({
|
||||||
filename: media[i].name,
|
filename: media[i].name,
|
||||||
path: thPath,
|
path: media[i].thumbnailPath,
|
||||||
cid: 'img' + i
|
cid: 'img' + i
|
||||||
});
|
});
|
||||||
if (i % numberOfColumns == 0) {
|
if (i % numberOfColumns == 0) {
|
||||||
htmlMiddle += '<tr>';
|
htmlMiddle += '<tr>';
|
||||||
}
|
}
|
||||||
htmlMiddle += '<td>\n' +
|
htmlMiddle += '<td>\n' +
|
||||||
' <a style="display: block;text-align: center;" href="' + linkUrl + '"><img alt="' + media[i].name + '" style="max-width: 200px; max-height: 150px; height:auto; width:auto;" src="cid:img' + i + '"/></a>\n' +
|
' <a style="display: block;text-align: center;" href="' + media[i].thumbnailUrl + '"><img alt="' + media[i].name + '" style="max-width: 200px; max-height: 150px; height:auto; width:auto;" src="cid:img' + i + '"/></a>\n' +
|
||||||
caption +
|
caption +
|
||||||
' </td>\n';
|
' </td>\n';
|
||||||
|
|
||||||
@ -79,8 +90,8 @@ export class EmailMediaMessenger {
|
|||||||
|
|
||||||
return await this.transporter.sendMail({
|
return await this.transporter.sendMail({
|
||||||
from: Config.Messaging.Email.emailFrom,
|
from: Config.Messaging.Email.emailFrom,
|
||||||
to: mailSettings.to,
|
to: mailSettings.emailTo,
|
||||||
subject: mailSettings.subject,
|
subject: mailSettings.emailSubject,
|
||||||
html: htmlStart + htmlMiddle + htmlEnd,
|
html: htmlStart + htmlMiddle + htmlEnd,
|
||||||
attachments: attachments
|
attachments: attachments
|
||||||
});
|
});
|
50
src/backend/model/messenger/Messenger.ts
Normal file
50
src/backend/model/messenger/Messenger.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import {MediaDTO, MediaDTOUtils} from '../../../common/entities/MediaDTO';
|
||||||
|
import {PhotoProcessing} from '../fileaccess/fileprocessing/PhotoProcessing';
|
||||||
|
import {ProjectPath} from '../../ProjectPath';
|
||||||
|
import {Config} from '../../../common/config/private/Config';
|
||||||
|
import {ThumbnailSourceType} from '../fileaccess/PhotoWorker';
|
||||||
|
import * as path from 'path';
|
||||||
|
import {Utils} from '../../../common/Utils';
|
||||||
|
import {QueryParams} from '../../../common/QueryParams';
|
||||||
|
import {DynamicConfig} from '../../../common/entities/DynamicConfig';
|
||||||
|
|
||||||
|
export interface MediaDTOWithThPath extends MediaDTO {
|
||||||
|
thumbnailPath: string;
|
||||||
|
thumbnailUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Messenger<C extends Record<string, unknown> = Record<string, unknown>> {
|
||||||
|
|
||||||
|
public abstract get Name(): string;
|
||||||
|
protected config: C;
|
||||||
|
public readonly ConfigTemplate: DynamicConfig[] = [];
|
||||||
|
|
||||||
|
private async getThumbnail(m: MediaDTO) {
|
||||||
|
return await PhotoProcessing.generateThumbnail(
|
||||||
|
path.join(ProjectPath.ImageFolder, m.directory.path, m.directory.name, m.name),
|
||||||
|
Config.Media.Thumbnail.thumbnailSizes[0],
|
||||||
|
MediaDTOUtils.isPhoto(m) ? ThumbnailSourceType.Photo : ThumbnailSourceType.Video,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async send(config: C, input: string | MediaDTO[] | unknown) {
|
||||||
|
if (Array.isArray(input) && input.length > 0
|
||||||
|
&& (input as MediaDTO[])[0]?.name
|
||||||
|
&& (input as MediaDTO[])[0]?.directory
|
||||||
|
&& (input as MediaDTO[])[0]?.metadata?.creationDate) {
|
||||||
|
const media = input as MediaDTOWithThPath[];
|
||||||
|
for (let i = 0; i < media.length; ++i) {
|
||||||
|
media[i].thumbnailPath = await this.getThumbnail(media[i]);
|
||||||
|
media[i].thumbnailUrl = Utils.concatUrls(Config.Server.publicUrl, '/gallery/', encodeURIComponent(path.join(media[i].directory.path, media[i].directory.name))) +
|
||||||
|
'?' + QueryParams.gallery.photo + '=' + encodeURIComponent(media[i].name);
|
||||||
|
}
|
||||||
|
return await this.sendMedia(config, media);
|
||||||
|
}
|
||||||
|
// TODO: implement other branches
|
||||||
|
throw new Error('Not yet implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract sendMedia(config: C, media: MediaDTOWithThPath[]): Promise<void> ;
|
||||||
|
}
|
34
src/backend/model/messenger/MessengerRepository.ts
Normal file
34
src/backend/model/messenger/MessengerRepository.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import {Messenger} from './Messenger';
|
||||||
|
import {EmailMessenger} from './EmailMessenger';
|
||||||
|
import {StdoutMessenger} from './StdoutMessenger';
|
||||||
|
|
||||||
|
export class MessengerRepository {
|
||||||
|
|
||||||
|
private static instance: MessengerRepository = null;
|
||||||
|
messengers: { [key: string]: Messenger } = {};
|
||||||
|
|
||||||
|
public static get Instance(): MessengerRepository {
|
||||||
|
if (MessengerRepository.instance == null) {
|
||||||
|
MessengerRepository.instance = new MessengerRepository();
|
||||||
|
}
|
||||||
|
return MessengerRepository.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAll(): Messenger[] {
|
||||||
|
return Object.values(this.messengers);
|
||||||
|
}
|
||||||
|
|
||||||
|
register(msgr: Messenger): void {
|
||||||
|
if (typeof this.messengers[msgr.Name] !== 'undefined') {
|
||||||
|
throw new Error('Messenger already exist:' + msgr.Name);
|
||||||
|
}
|
||||||
|
this.messengers[msgr.Name] = msgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(name: string): Messenger {
|
||||||
|
return this.messengers[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessengerRepository.Instance.register(new EmailMessenger());
|
||||||
|
MessengerRepository.Instance.register(new StdoutMessenger());
|
17
src/backend/model/messenger/StdoutMessenger.ts
Normal file
17
src/backend/model/messenger/StdoutMessenger.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import {MediaDTOWithThPath, Messenger} from './Messenger';
|
||||||
|
import {DynamicConfig} from '../../../common/entities/DynamicConfig';
|
||||||
|
import {DefaultMessengers} from '../../../common/entities/job/JobDTO';
|
||||||
|
|
||||||
|
export class StdoutMessenger extends Messenger {
|
||||||
|
public readonly Name = DefaultMessengers[DefaultMessengers.Stdout];
|
||||||
|
public readonly ConfigTemplate: DynamicConfig[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected async sendMedia(config: never, media: MediaDTOWithThPath[]) {
|
||||||
|
console.log(media.map(m => m.thumbnailPath));
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ export class AdminRouter {
|
|||||||
this.addGetStatistic(app);
|
this.addGetStatistic(app);
|
||||||
this.addGetDuplicates(app);
|
this.addGetDuplicates(app);
|
||||||
this.addJobs(app);
|
this.addJobs(app);
|
||||||
|
this.addMessengers(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static addGetStatistic(app: Express): void {
|
private static addGetStatistic(app: Express): void {
|
||||||
@ -32,6 +33,15 @@ export class AdminRouter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static addMessengers(app: Express): void {
|
||||||
|
app.get(
|
||||||
|
Config.Server.apiPath + '/admin/messengers/available',
|
||||||
|
AuthenticationMWs.authenticate,
|
||||||
|
AuthenticationMWs.authorise(UserRoles.Admin),
|
||||||
|
AdminMWs.getAvailableMessengers,
|
||||||
|
RenderingMWs.renderResult
|
||||||
|
);
|
||||||
|
}
|
||||||
private static addJobs(app: Express): void {
|
private static addJobs(app: Express): void {
|
||||||
app.get(
|
app.get(
|
||||||
Config.Server.apiPath + '/admin/jobs/available',
|
Config.Server.apiPath + '/admin/jobs/available',
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export type backendText = number;
|
export type backendText = number;
|
||||||
|
// keep the numbering sparse to support later addition
|
||||||
export const backendTexts = {
|
export const backendTexts = {
|
||||||
indexedFilesOnly: {name: 10, description: 12},
|
indexedFilesOnly: {name: 10, description: 12},
|
||||||
sizeToGenerate: {name: 20, description: 22},
|
sizeToGenerate: {name: 20, description: 22},
|
||||||
@ -6,6 +7,7 @@ export const backendTexts = {
|
|||||||
mediaPick: {name: 40, description: 42},
|
mediaPick: {name: 40, description: 42},
|
||||||
emailTo: {name: 70, description: 72},
|
emailTo: {name: 70, description: 72},
|
||||||
emailSubject: {name: 90, description: 92},
|
emailSubject: {name: 90, description: 92},
|
||||||
emailText: {name: 100, description: 102}
|
emailText: {name: 100, description: 102},
|
||||||
|
messenger: {name: 110,description: 112}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import {backendText} from '../BackendTexts';
|
import {backendText} from '../BackendTexts';
|
||||||
import {fieldType} from './job/JobDTO';
|
|
||||||
|
|
||||||
|
export type fieldType = 'string' | 'string-array' | 'number' | 'boolean' | 'number-array' | 'MediaPickDTO-array' | 'messenger';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamic configs are not part of the typeconfig maintained config.
|
* Dynamic configs are not part of the typeconfig maintained config.
|
||||||
@ -14,4 +17,5 @@ export interface DynamicConfig {
|
|||||||
description: backendText | string;
|
description: backendText | string;
|
||||||
type: fieldType;
|
type: fieldType;
|
||||||
defaultValue: unknown;
|
defaultValue: unknown;
|
||||||
|
validIf?: { configFiled: string, equalsValue: string }; // only shows this config if this predicate is true
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import {backendText} from '../../BackendTexts';
|
|
||||||
import {DynamicConfig} from '../DynamicConfig';
|
import {DynamicConfig} from '../DynamicConfig';
|
||||||
|
|
||||||
export type fieldType = 'string' | 'string-array' | 'number' | 'boolean' | 'number-array' | 'MediaPickDTO-array';
|
|
||||||
|
|
||||||
export enum DefaultsJobs {
|
export enum DefaultsJobs {
|
||||||
Indexing = 1,
|
Indexing = 1,
|
||||||
'Gallery Reset' = 2,
|
'Gallery Reset' = 2,
|
||||||
@ -19,6 +16,12 @@ export enum DefaultsJobs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export enum DefaultMessengers {
|
||||||
|
Email = 1,
|
||||||
|
Stdout = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface JobDTO {
|
export interface JobDTO {
|
||||||
Name: string;
|
Name: string;
|
||||||
ConfigTemplate: DynamicConfig[];
|
ConfigTemplate: DynamicConfig[];
|
||||||
|
@ -5,7 +5,10 @@ import {DefaultsJobs} from '../../../common/entities/job/JobDTO';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class BackendtextService {
|
export class BackendtextService {
|
||||||
|
|
||||||
public get(id: backendText): string {
|
public get(id: backendText | string): string {
|
||||||
|
if (typeof id === 'string') {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case backendTexts.sizeToGenerate.name:
|
case backendTexts.sizeToGenerate.name:
|
||||||
return $localize`Size to generate`;
|
return $localize`Size to generate`;
|
||||||
|
@ -3,9 +3,10 @@ import {BehaviorSubject} from 'rxjs';
|
|||||||
import {JobProgressDTO, JobProgressStates, OnTimerJobProgressDTO,} from '../../../../common/entities/job/JobProgressDTO';
|
import {JobProgressDTO, JobProgressStates, OnTimerJobProgressDTO,} from '../../../../common/entities/job/JobProgressDTO';
|
||||||
import {NetworkService} from '../../model/network/network.service';
|
import {NetworkService} from '../../model/network/network.service';
|
||||||
import {JobScheduleDTO} from '../../../../common/entities/job/JobScheduleDTO';
|
import {JobScheduleDTO} from '../../../../common/entities/job/JobScheduleDTO';
|
||||||
import {ConfigTemplateEntry, JobDTO, JobDTOUtils} from '../../../../common/entities/job/JobDTO';
|
import {JobDTO, JobDTOUtils} from '../../../../common/entities/job/JobDTO';
|
||||||
import {BackendtextService} from '../../model/backendtext.service';
|
import {BackendtextService} from '../../model/backendtext.service';
|
||||||
import {NotificationService} from '../../model/notification.service';
|
import {NotificationService} from '../../model/notification.service';
|
||||||
|
import {DynamicConfig} from '../../../../common/entities/DynamicConfig';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ScheduledJobsService {
|
export class ScheduledJobsService {
|
||||||
@ -13,6 +14,7 @@ export class ScheduledJobsService {
|
|||||||
public onJobFinish: EventEmitter<string> = new EventEmitter<string>();
|
public onJobFinish: EventEmitter<string> = new EventEmitter<string>();
|
||||||
timer: number = null;
|
timer: number = null;
|
||||||
public availableJobs: BehaviorSubject<JobDTO[]>;
|
public availableJobs: BehaviorSubject<JobDTO[]>;
|
||||||
|
public availableMessengers: BehaviorSubject<string[]>;
|
||||||
public jobStartingStopping: { [key: string]: boolean } = {};
|
public jobStartingStopping: { [key: string]: boolean } = {};
|
||||||
private subscribers = 0;
|
private subscribers = 0;
|
||||||
|
|
||||||
@ -23,6 +25,7 @@ export class ScheduledJobsService {
|
|||||||
) {
|
) {
|
||||||
this.progress = new BehaviorSubject({});
|
this.progress = new BehaviorSubject({});
|
||||||
this.availableJobs = new BehaviorSubject([]);
|
this.availableJobs = new BehaviorSubject([]);
|
||||||
|
this.availableMessengers = new BehaviorSubject([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +35,13 @@ export class ScheduledJobsService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getConfigTemplate(JobName: string): ConfigTemplateEntry[] {
|
public async getAvailableMessengers(): Promise<void> {
|
||||||
|
this.availableMessengers.next(
|
||||||
|
await this.networkService.getJson<string[]>('/admin/messengers/available')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConfigTemplate(JobName: string): DynamicConfig[] {
|
||||||
const job = this.availableJobs.value.find(
|
const job = this.availableJobs.value.find(
|
||||||
(t) => t.Name === JobName
|
(t) => t.Name === JobName
|
||||||
);
|
);
|
||||||
|
@ -170,6 +170,7 @@
|
|||||||
<div *ngFor="let configEntry of jobsService.getConfigTemplate(schedule.jobName)">
|
<div *ngFor="let configEntry of jobsService.getConfigTemplate(schedule.jobName)">
|
||||||
|
|
||||||
<div class="mb-1 row"
|
<div class="mb-1 row"
|
||||||
|
*ngIf="!configEntry.validIf || schedule.config[configEntry.validIf.configFiled] == configEntry.validIf.equalsValue"
|
||||||
[class.mb-3]="settingsService.configStyle == ConfigStyle.full">
|
[class.mb-3]="settingsService.configStyle == ConfigStyle.full">
|
||||||
<label class="col-md-2 control-label"
|
<label class="col-md-2 control-label"
|
||||||
[for]="configEntry.id+'_'+i">{{backendTextService.get(configEntry.name)}}</label>
|
[for]="configEntry.id+'_'+i">{{backendTextService.get(configEntry.name)}}</label>
|
||||||
@ -227,6 +228,18 @@
|
|||||||
placeholder="Search Query">
|
placeholder="Search Query">
|
||||||
</app-gallery-search-field>
|
</app-gallery-search-field>
|
||||||
|
|
||||||
|
<select
|
||||||
|
*ngSwitchCase="'messenger'"
|
||||||
|
[id]="configEntry.id+'_'+i"
|
||||||
|
[name]="configEntry.id+'_'+i"
|
||||||
|
(ngModelChange)="onChange($event)"
|
||||||
|
[(ngModel)]="schedule.config[configEntry.id]"
|
||||||
|
class="form-select">
|
||||||
|
<option *ngFor="let msg of jobsService.availableMessengers | async" [ngValue]="msg">{{msg}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
<ng-container *ngSwitchCase="'MediaPickDTO-array'">
|
<ng-container *ngSwitchCase="'MediaPickDTO-array'">
|
||||||
<ng-container *ngFor="let mp of AsMediaPickDTOArray(schedule.config[configEntry.id]); let j=index">
|
<ng-container *ngFor="let mp of AsMediaPickDTOArray(schedule.config[configEntry.id]); let j=index">
|
||||||
|
|
||||||
|
@ -108,6 +108,7 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.jobsService.subscribeToProgress();
|
this.jobsService.subscribeToProgress();
|
||||||
this.jobsService.getAvailableJobs().catch(console.error);
|
this.jobsService.getAvailableJobs().catch(console.error);
|
||||||
|
this.jobsService.getAvailableMessengers().catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
@ -128,7 +129,7 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
|
|||||||
schedule.config = schedule.config || {};
|
schedule.config = schedule.config || {};
|
||||||
if (job.ConfigTemplate) {
|
if (job.ConfigTemplate) {
|
||||||
job.ConfigTemplate.forEach(
|
job.ConfigTemplate.forEach(
|
||||||
(ct) => (schedule.config[ct.id] = ct.defaultValue)
|
(ct) => (schedule.config[ct.id] = ct.defaultValue as never)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,7 +217,7 @@ export class WorkflowComponent implements ControlValueAccessor, Validator, OnIni
|
|||||||
this.newSchedule.config = this.newSchedule.config || {};
|
this.newSchedule.config = this.newSchedule.config || {};
|
||||||
if (job.ConfigTemplate) {
|
if (job.ConfigTemplate) {
|
||||||
job.ConfigTemplate.forEach(
|
job.ConfigTemplate.forEach(
|
||||||
(ct) => (this.newSchedule.config[ct.id] = ct.defaultValue)
|
(ct) => (this.newSchedule.config[ct.id] = ct.defaultValue as never)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.jobModalQL.first.show();
|
this.jobModalQL.first.show();
|
||||||
|
Loading…
Reference in New Issue
Block a user