Files
Andrei 58f8093689 Rebrand from 'Redirect Intelligence v2' to 'URL Tracker Tool V2' throughout UI
- Updated all component headers and documentation
- Changed navbar and footer branding
- Updated homepage hero badge
- Modified page title in index.html
- Simplified footer text to 'Built with ❤️'
- Consistent V2 capitalization across all references
2025-08-19 19:12:23 +00:00

151 lines
6.9 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getNextMillis = exports.Repeat = void 0;
const tslib_1 = require("tslib");
const cron_parser_1 = require("cron-parser");
const crypto_1 = require("crypto");
const queue_base_1 = require("./queue-base");
class Repeat extends queue_base_1.QueueBase {
constructor(name, opts, Connection) {
super(name, opts, Connection);
this.repeatStrategy =
(opts.settings && opts.settings.repeatStrategy) || exports.getNextMillis;
this.repeatKeyHashAlgorithm =
(opts.settings && opts.settings.repeatKeyHashAlgorithm) || 'md5';
}
async addNextRepeatableJob(name, data, opts, skipCheckExists) {
var _a;
// HACK: This is a temporary fix to enable easy migration from bullmq <3.0.0
// to >= 3.0.0. It should be removed when moving to 4.x.
const repeatOpts = Object.assign({}, opts.repeat);
(_a = repeatOpts.pattern) !== null && _a !== void 0 ? _a : (repeatOpts.pattern = repeatOpts.cron);
delete repeatOpts.cron;
const prevMillis = opts.prevMillis || 0;
const currentCount = repeatOpts.count ? repeatOpts.count + 1 : 1;
if (typeof repeatOpts.limit !== 'undefined' &&
currentCount > repeatOpts.limit) {
return;
}
let now = Date.now();
if (!(typeof repeatOpts.endDate === undefined) &&
now > new Date(repeatOpts.endDate).getTime()) {
return;
}
now = prevMillis < now ? now : prevMillis;
const nextMillis = await this.repeatStrategy(now, repeatOpts, name);
const pattern = repeatOpts.pattern;
const hasImmediately = Boolean((repeatOpts.every || pattern) && repeatOpts.immediately);
const offset = hasImmediately ? now - nextMillis : undefined;
if (nextMillis) {
// We store the undecorated opts.jobId into the repeat options
if (!prevMillis && opts.jobId) {
repeatOpts.jobId = opts.jobId;
}
const repeatJobKey = getRepeatKey(name, repeatOpts);
let repeatableExists = true;
if (!skipCheckExists) {
// Check that the repeatable job hasn't been removed
// TODO: a lua script would be better here
const client = await this.client;
repeatableExists = !!(await client.zscore(this.keys.repeat, repeatJobKey));
}
const { immediately } = repeatOpts, filteredRepeatOpts = tslib_1.__rest(repeatOpts, ["immediately"]);
// The job could have been deleted since this check
if (repeatableExists) {
return this.createNextJob(name, nextMillis, repeatJobKey, Object.assign(Object.assign({}, opts), { repeat: Object.assign({ offset }, filteredRepeatOpts) }), data, currentCount, hasImmediately);
}
}
}
async createNextJob(name, nextMillis, repeatJobKey, opts, data, currentCount, hasImmediately) {
const client = await this.client;
//
// Generate unique job id for this iteration.
//
const jobId = this.getRepeatJobId(name, nextMillis, this.hash(repeatJobKey), opts.repeat.jobId);
const now = Date.now();
const delay = nextMillis + (opts.repeat.offset ? opts.repeat.offset : 0) - now;
const mergedOpts = Object.assign(Object.assign({}, opts), { jobId, delay: delay < 0 || hasImmediately ? 0 : delay, timestamp: now, prevMillis: nextMillis, repeatJobKey });
mergedOpts.repeat = Object.assign(Object.assign({}, opts.repeat), { count: currentCount });
await client.zadd(this.keys.repeat, nextMillis.toString(), repeatJobKey);
return this.Job.create(this, name, data, mergedOpts);
}
async removeRepeatable(name, repeat, jobId) {
const repeatJobKey = getRepeatKey(name, Object.assign(Object.assign({}, repeat), { jobId }));
const repeatJobId = this.getRepeatJobId(name, '', this.hash(repeatJobKey), jobId || repeat.jobId);
return this.scripts.removeRepeatable(repeatJobId, repeatJobKey);
}
async removeRepeatableByKey(repeatJobKey) {
const data = this.keyToData(repeatJobKey);
const repeatJobId = this.getRepeatJobId(data.name, '', this.hash(repeatJobKey), data.id);
return this.scripts.removeRepeatable(repeatJobId, repeatJobKey);
}
keyToData(key, next) {
const data = key.split(':');
const pattern = data.slice(4).join(':') || null;
return {
key,
name: data[0],
id: data[1] || null,
endDate: parseInt(data[2]) || null,
tz: data[3] || null,
pattern,
next,
};
}
async getRepeatableJobs(start = 0, end = -1, asc = false) {
const client = await this.client;
const key = this.keys.repeat;
const result = asc
? await client.zrange(key, start, end, 'WITHSCORES')
: await client.zrevrange(key, start, end, 'WITHSCORES');
const jobs = [];
for (let i = 0; i < result.length; i += 2) {
jobs.push(this.keyToData(result[i], parseInt(result[i + 1])));
}
return jobs;
}
async getRepeatableCount() {
const client = await this.client;
return client.zcard(this.toKey('repeat'));
}
hash(str) {
return (0, crypto_1.createHash)(this.repeatKeyHashAlgorithm).update(str).digest('hex');
}
getRepeatJobId(name, nextMillis, namespace, jobId) {
const checksum = this.hash(`${name}${jobId || ''}${namespace}`);
return `repeat:${checksum}:${nextMillis}`;
// return `repeat:${jobId || ''}:${name}:${namespace}:${nextMillis}`;
//return `repeat:${name}:${namespace}:${nextMillis}`;
}
}
exports.Repeat = Repeat;
function getRepeatKey(name, repeat) {
const endDate = repeat.endDate ? new Date(repeat.endDate).getTime() : '';
const tz = repeat.tz || '';
const pattern = repeat.pattern;
const suffix = (pattern ? pattern : String(repeat.every)) || '';
const jobId = repeat.jobId ? repeat.jobId : '';
return `${name}:${jobId}:${endDate}:${tz}:${suffix}`;
}
const getNextMillis = (millis, opts) => {
const pattern = opts.pattern;
if (pattern && opts.every) {
throw new Error('Both .pattern and .every options are defined for this repeatable job');
}
if (opts.every) {
return (Math.floor(millis / opts.every) * opts.every +
(opts.immediately ? 0 : opts.every));
}
const currentDate = opts.startDate && new Date(opts.startDate) > new Date(millis)
? new Date(opts.startDate)
: new Date(millis);
const interval = (0, cron_parser_1.parseExpression)(pattern, Object.assign(Object.assign({}, opts), { currentDate }));
try {
return interval.next().getTime();
}
catch (e) {
// Ignore error
}
};
exports.getNextMillis = getNextMillis;
//# sourceMappingURL=repeat.js.map