Skip to content

Commit

Permalink
more job info
Browse files Browse the repository at this point in the history
  • Loading branch information
Razzmatazzz committed Sep 22, 2024
1 parent e74476c commit 4802fda
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 57 deletions.
13 changes: 13 additions & 0 deletions src/tarkov-data-manager/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,18 @@ app.delete('/webhooks/:id', async (req, res) => {
});

app.get('/crons', async (req, res) => {
const runningJobs = jobs.schedules().filter(j => j.running);
let runningJobsDiv = '';
if (runningJobs.length > 0) {
runningJobsDiv = `
<div>
<div>Jobs currently running:</div>
<div>
${runningJobs.map(j => `<div>${j.name}: Started ${j.startDate}</div>`)}
</div>
</div>
`;
}
res.send(`${getHeader(req, {include: 'datatables'})}
<script src="/ansi_up.js"></script>
<script src="/crons.js"></script>
Expand All @@ -1244,6 +1256,7 @@ app.get('/crons', async (req, res) => {
<div>
Note: Jobs are scheduled in UTC. Your local time is <span class="timeoffset"></span> hours UTC.
</div>
${runningJobsDiv}
<table class="highlight main">
<thead>
<tr>
Expand Down
95 changes: 43 additions & 52 deletions src/tarkov-data-manager/jobs/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import cron from 'cron-validator';
import emitter from '../modules/emitter.mjs';
import discord from '../modules/webhook.mjs';

const defaultJobs = {
const defaultJobTriggers = {
'update-item-cache': '*/5 * * * *',
'game-data': '1-59/10 * * * *',
'update-barters': 'jobComplete_update-trader-prices',
Expand All @@ -30,7 +30,7 @@ const defaultJobs = {
};

// these jobs only run on the given schedule when not in dev mode
const nonDevJobs = {};
const nonDevJobTriggers = {};

// these jobs run at startup
const startupJobs = [
Expand All @@ -51,48 +51,42 @@ for (const file of jobFiles) {
await import(`./${file}`).then(jobClass => {
jobClasses[file.replace('.mjs', '')] = jobClass.default;
});
//const jobClass = require(`./${file}`);
//jobClasses[file.replace('.mjs', '')] = jobClass;
}

let allJobs = {
...defaultJobs
let jobTriggers = {
...defaultJobTriggers
};
try {
const customJobs = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, '..', 'settings', 'crons.json')));
for (const jobName of Object.keys(customJobs)) {
if (!jobClasses[jobName]) {
console.warn(`${jobName} is not a valid job; excluding from custom schedule`);
customJobs[jobName] = undefined;
continue;
}
jobTriggers[jobName] = customJobs[jobName];
}
allJobs = {
...defaultJobs,
...customJobs
};
} catch (error) {
if (error.code !== 'ENOENT') console.log(`Error parsing custom cron jobs`, error);
}
if (process.env.NODE_ENV !== 'dev') {
allJobs = {
...allJobs,
...nonDevJobs
jobTriggers = {
...jobTriggers,
...nonDevJobTriggers
};
}

const jobs = {};
const scheduledJobs = {};
const eventJobs = {};

const scheduleJob = function(name, cronSchedule) {
if (!jobClasses[name]) {
console.log(`Can't schedule ${name}; not a valid job`);
if (!jobs[name]) {
throw new Error(`Can't schedule ${name}; not a valid job`);
}
if (scheduledJobs[name]) {
scheduledJobs[name].cancel();
if (jobs[name].cronTrigger) {
jobs[name].cronTrigger.cancel();
}
if (eventJobs[name]) {
emitter.off(eventJobs[name].event, eventJobs[name].listener);
if (jobs[name].eventTrigger) {
emitter.off(jobs[name].eventTrigger.name, jobs[name].eventTrigger.listener);
}
if (!cronSchedule) {
return;
Expand All @@ -119,14 +113,11 @@ const scheduleJob = function(name, cronSchedule) {
console.timeEnd(name);
};
if (isCron) {
const job = schedule.scheduleJob(cronSchedule, jobFunction);
jobs[name].cronSchedule = cronSchedule;

scheduledJobs[name] = job;
jobs[name].cronTrigger = schedule.scheduleJob(cronSchedule, jobFunction);

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
.
} else {
emitter.on(cronSchedule, jobFunction);
eventJobs[name] = {
event: cronSchedule,
jobs[name].eventTrigger = {

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
.
name: cronSchedule,
listener: jobFunction,
};
}
Expand All @@ -139,9 +130,9 @@ const jobManager = {
return true;
}*/

for (const jobName in allJobs) {
for (const jobName in jobTriggers) {
try {
scheduleJob(jobName, allJobs[jobName]);
scheduleJob(jobName, jobTriggers[jobName]);
} catch (error) {
console.log(`Error setting up ${jobName} job`, error);
}
Expand Down Expand Up @@ -216,37 +207,37 @@ const jobManager = {
if (ignoreJobs.includes(jobName)) {
continue;
}
const jobResult = {
jobResults.push({
name: jobName,
schedule: allJobs[jobName] || '',
schedule: jobTriggers[jobName] ?? '',
lastRun: jobManager.lastRun(jobName),
nextRun: false,
nextRun: jobs[jobName].cronTrigger?.nextInvocation() ?? false,
running: jobs[jobName]?.running,
};
if (scheduledJobs[jobName]) {
jobResult.nextRun = scheduledJobs[jobName].nextInvocation();
}
jobResults.push(jobResult);
startDate: jobs[jobName].startDate,
});
}
return jobResults;
},
setSchedule: (jobName, cronSchedule) => {
if (!cronSchedule) {
cronSchedule = undefined;
setSchedule: (jobName, jobSchedule) => {
if (!jobSchedule) {
jobSchedule = undefined;
}
if (cronSchedule === 'default') {
if (!defaultJobs[jobName]) {
cronSchedule = undefined;
if (jobSchedule === 'default') {
if (!defaultJobTriggers[jobName]) {
jobSchedule = undefined;
} else {
cronSchedule = defaultJobs[jobName];
jobSchedule = defaultJobTriggers[jobName];
}
}
scheduleJob(jobName, cronSchedule);
allJobs[jobName] = cronSchedule;
scheduleJob(jobName, jobSchedule);
jobTriggers[jobName] = jobSchedule;

Check failure

Code scanning / CodeQL

Remote property injection High

A property name to write to depends on a
user-provided value
.
const customJobs = {};
for (const job in allJobs) {
if (allJobs[job] !== defaultJobs[job]) {
customJobs[job] = allJobs[job];
for (const jobName in jobTriggers) {
if (!jobs[jobName]) {
continue;
}
if (jobTriggers[jobName] !== defaultJobTriggers[jobName]) {
customJobs[jobName] = jobTriggers[jobName];
}
}
fs.writeFileSync(path.join(import.meta.dirname, '..', 'settings', 'crons.json'), JSON.stringify(customJobs, null, 4));
Expand All @@ -255,8 +246,8 @@ const jobManager = {
if (!jobs[jobName]) {
return Promise.reject(new Error(`${jobName} is not a valid job`));
}
if (scheduledJobs[jobName]) {
const scheduledJob = scheduledJobs[jobName];
if (jobs[jobName].cronTrigger) {
const scheduledJob = jobs[jobName].cronTrigger;
const nextInvocation = scheduledJob.nextInvocation();
if (scheduledJob && bumpSchedule && nextInvocation) {const nextRunMinusFive = nextInvocation.toDate();
nextRunMinusFive.setMinutes(nextRunMinusFive.getMinutes() - 5);
Expand All @@ -265,7 +256,7 @@ const jobManager = {
}
}
jobs[jobName].nextInvocation = nextInvocation?.toDate();
} else if (eventJobs[jobName]) {
} else if (jobs[jobName].eventTrigger) {
if (jobs[jobName].lastCompletion) {
const elapsed = new Date().getTime() - jobs[jobName].lastCompletion.getTime();
jobs[jobName].nextInvocation = new Date(new Date().getTime() + elapsed);
Expand Down
13 changes: 8 additions & 5 deletions src/tarkov-data-manager/modules/data-job.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class DataJob {
'gameModes',
'lastCompletion',
'loadLocales',
'cronTrigger',
'eventTrigger',
...options.saveFields,
];
this.writeFolder = 'dumps';
Expand Down Expand Up @@ -90,22 +92,23 @@ class DataJob {
logger: this.logger,
});
}
if (options && options.parent) {
if (options?.parent) {
this.logger.parentLogger = options.parent.logger;
}
if (this.running) {
if (options && options.parent) {
if (options?.parent) {
for (let parent = options.parent; parent; parent = parent.parent) {
if (parent.name === this.name) {
return Promise.reject(new Error(`Job ${this.name} is a parent of ${options.parent.name}, so ${options.parent.name} cannot run it`));
}
}
this.parent = options.parent;
this.logger.log(`${this.name} is already running; waiting for completion`);
return this.running;
}
this.logger.log(`${this.name} is already running; waiting for completion`);
return this.running;
return Promise.reject(new Error(`Job ${this.name} already running since ${this.startDate}`));
}
if (options && options.parent) {
if (options?.parent) {
this.parent = options.parent;
}
this.discordAlertQueue = [];
Expand Down

0 comments on commit 4802fda

Please sign in to comment.