iobroker.time-tracker 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +386 -0
- package/admin/i18n/de.json +34 -0
- package/admin/i18n/en.json +1 -0
- package/admin/i18n/es.json +34 -0
- package/admin/i18n/fr.json +34 -0
- package/admin/i18n/it.json +34 -0
- package/admin/i18n/nl.json +34 -0
- package/admin/i18n/pl.json +34 -0
- package/admin/i18n/pt.json +34 -0
- package/admin/i18n/ru.json +34 -0
- package/admin/i18n/uk.json +34 -0
- package/admin/i18n/zh-cn.json +34 -0
- package/admin/jsonConfig.json +266 -0
- package/admin/time-tracker.png +0 -0
- package/build/lib/adapter/automation.js +100 -0
- package/build/lib/adapter/automation.js.map +7 -0
- package/build/lib/adapter/commands.js +160 -0
- package/build/lib/adapter/commands.js.map +7 -0
- package/build/lib/adapter/messages.js +207 -0
- package/build/lib/adapter/messages.js.map +7 -0
- package/build/lib/adapter/presence.js +129 -0
- package/build/lib/adapter/presence.js.map +7 -0
- package/build/lib/adapter/states.js +330 -0
- package/build/lib/adapter/states.js.map +7 -0
- package/build/lib/adapter/triggers.js +92 -0
- package/build/lib/adapter/triggers.js.map +7 -0
- package/build/lib/db/database.js +96 -0
- package/build/lib/db/database.js.map +7 -0
- package/build/lib/db/migrations.js +603 -0
- package/build/lib/db/migrations.js.map +7 -0
- package/build/lib/db/repositories/absences.js +432 -0
- package/build/lib/db/repositories/absences.js.map +7 -0
- package/build/lib/db/repositories/audit.js +90 -0
- package/build/lib/db/repositories/audit.js.map +7 -0
- package/build/lib/db/repositories/automations.js +326 -0
- package/build/lib/db/repositories/automations.js.map +7 -0
- package/build/lib/db/repositories/entries.js +335 -0
- package/build/lib/db/repositories/entries.js.map +7 -0
- package/build/lib/db/repositories/holidays.js +156 -0
- package/build/lib/db/repositories/holidays.js.map +7 -0
- package/build/lib/db/repositories/payouts.js +221 -0
- package/build/lib/db/repositories/payouts.js.map +7 -0
- package/build/lib/db/repositories/rfid.js +264 -0
- package/build/lib/db/repositories/rfid.js.map +7 -0
- package/build/lib/db/repositories/rules.js +356 -0
- package/build/lib/db/repositories/rules.js.map +7 -0
- package/build/lib/db/repositories/settings.js +91 -0
- package/build/lib/db/repositories/settings.js.map +7 -0
- package/build/lib/db/repositories/terminals.js +243 -0
- package/build/lib/db/repositories/terminals.js.map +7 -0
- package/build/lib/db/repositories/triggers.js +240 -0
- package/build/lib/db/repositories/triggers.js.map +7 -0
- package/build/lib/db/repositories/users.js +491 -0
- package/build/lib/db/repositories/users.js.map +7 -0
- package/build/lib/db/seed.js +180 -0
- package/build/lib/db/seed.js.map +7 -0
- package/build/lib/domain/avatar.js +63 -0
- package/build/lib/domain/avatar.js.map +7 -0
- package/build/lib/domain/breaks.js +70 -0
- package/build/lib/domain/breaks.js.map +7 -0
- package/build/lib/domain/calculation.js +150 -0
- package/build/lib/domain/calculation.js.map +7 -0
- package/build/lib/domain/holidays.js +113 -0
- package/build/lib/domain/holidays.js.map +7 -0
- package/build/lib/domain/punch.js +99 -0
- package/build/lib/domain/punch.js.map +7 -0
- package/build/lib/domain/target.js +89 -0
- package/build/lib/domain/target.js.map +7 -0
- package/build/lib/errors.js +56 -0
- package/build/lib/errors.js.map +7 -0
- package/build/lib/reports/labels.js +358 -0
- package/build/lib/reports/labels.js.map +7 -0
- package/build/lib/reports/pdf.js +308 -0
- package/build/lib/reports/pdf.js.map +7 -0
- package/build/lib/reports/types.js +32 -0
- package/build/lib/reports/types.js.map +7 -0
- package/build/lib/reports/xls.js +211 -0
- package/build/lib/reports/xls.js.map +7 -0
- package/build/lib/services/aggregation.js +495 -0
- package/build/lib/services/aggregation.js.map +7 -0
- package/build/lib/services/auth.js +381 -0
- package/build/lib/services/auth.js.map +7 -0
- package/build/lib/services/backup.js +372 -0
- package/build/lib/services/backup.js.map +7 -0
- package/build/lib/services/closing.js +106 -0
- package/build/lib/services/closing.js.map +7 -0
- package/build/lib/services/sessionSecret.js +100 -0
- package/build/lib/services/sessionSecret.js.map +7 -0
- package/build/lib/services/sync.js +278 -0
- package/build/lib/services/sync.js.map +7 -0
- package/build/lib/util/time.js +125 -0
- package/build/lib/util/time.js.map +7 -0
- package/build/lib/web/api.js +2589 -0
- package/build/lib/web/api.js.map +7 -0
- package/build/lib/web/cookies.js +80 -0
- package/build/lib/web/cookies.js.map +7 -0
- package/build/lib/web/events.js +50 -0
- package/build/lib/web/events.js.map +7 -0
- package/build/lib/web/pin-guard.js +86 -0
- package/build/lib/web/pin-guard.js.map +7 -0
- package/build/lib/web/problem.js +129 -0
- package/build/lib/web/problem.js.map +7 -0
- package/build/lib/web/rate-limit.js +65 -0
- package/build/lib/web/rate-limit.js.map +7 -0
- package/build/lib/web/router.js +274 -0
- package/build/lib/web/router.js.map +7 -0
- package/build/lib/web/server.js +189 -0
- package/build/lib/web/server.js.map +7 -0
- package/build/lib/web/static.js +151 -0
- package/build/lib/web/static.js.map +7 -0
- package/build/lib/web/stream.js +135 -0
- package/build/lib/web/stream.js.map +7 -0
- package/build/main.js +986 -0
- package/build/main.js.map +7 -0
- package/io-package.json +221 -0
- package/package.json +131 -0
- package/src-pwa/src/i18n/de.json +359 -0
- package/src-pwa/src/i18n/en.json +359 -0
- package/src-pwa/src/i18n/es.json +359 -0
- package/src-pwa/src/i18n/fr.json +359 -0
- package/src-pwa/src/i18n/index.ts +96 -0
- package/src-pwa/src/i18n/it.json +359 -0
- package/src-pwa/src/i18n/nl.json +359 -0
- package/src-pwa/src/i18n/pl.json +359 -0
- package/src-pwa/src/i18n/pt.json +359 -0
- package/src-pwa/src/i18n/ru.json +359 -0
- package/src-pwa/src/i18n/uk.json +359 -0
- package/src-pwa/src/i18n/zh-cn.json +359 -0
- package/www/assets/index-QI3wrEvW.js +119 -0
- package/www/assets/index-QI3wrEvW.js.map +1 -0
- package/www/assets/workbox-window.prod.es5-Bd17z0YL.js +3 -0
- package/www/assets/workbox-window.prod.es5-Bd17z0YL.js.map +1 -0
- package/www/favicon.svg +16 -0
- package/www/icon-192.png +0 -0
- package/www/icon-512.png +0 -0
- package/www/index.html +38 -0
- package/www/manifest.webmanifest +1 -0
- package/www/person.png +0 -0
- package/www/sw.js +3 -0
- package/www/sw.js.map +1 -0
- package/www/workbox-9c191d2f.js +3 -0
- package/www/workbox-9c191d2f.js.map +1 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var automations_exports = {};
|
|
20
|
+
__export(automations_exports, {
|
|
21
|
+
ALL_WEEKDAYS: () => ALL_WEEKDAYS,
|
|
22
|
+
createAutomationsRepository: () => createAutomationsRepository,
|
|
23
|
+
mapAutomationRuleRow: () => mapAutomationRuleRow,
|
|
24
|
+
mapAutomationRunRow: () => mapAutomationRunRow,
|
|
25
|
+
maskToWeekdays: () => maskToWeekdays,
|
|
26
|
+
weekdaysToMask: () => weekdaysToMask
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(automations_exports);
|
|
29
|
+
var import_errors = require("../../errors");
|
|
30
|
+
var import_audit = require("./audit");
|
|
31
|
+
const RULE_COLUMNS = "id, label, kind, user_id, at_minute, after_minutes, weekdays, repeat, is_active";
|
|
32
|
+
const RUN_COLUMNS = "rule_id, user_id, period, fired_at, action";
|
|
33
|
+
const AUDITED_FIELDS = [
|
|
34
|
+
"label",
|
|
35
|
+
"kind",
|
|
36
|
+
"userId",
|
|
37
|
+
"atMinute",
|
|
38
|
+
"afterMinutes",
|
|
39
|
+
"repeat",
|
|
40
|
+
"isActive"
|
|
41
|
+
];
|
|
42
|
+
const KINDS = ["clockIn", "clockOut", "missingPunch", "breakReminder"];
|
|
43
|
+
const TIMED_KINDS = ["clockIn", "clockOut", "missingPunch"];
|
|
44
|
+
function mapAutomationRuleRow(row) {
|
|
45
|
+
var _a;
|
|
46
|
+
return {
|
|
47
|
+
id: row.id,
|
|
48
|
+
label: row.label,
|
|
49
|
+
kind: KINDS.includes(row.kind) ? row.kind : "clockOut",
|
|
50
|
+
userId: row.user_id,
|
|
51
|
+
atMinute: row.at_minute,
|
|
52
|
+
afterMinutes: row.after_minutes,
|
|
53
|
+
// a row written before the migration has no selection: every day is the sensible default
|
|
54
|
+
weekdays: maskToWeekdays((_a = row.weekdays) != null ? _a : 127),
|
|
55
|
+
repeat: row.repeat === "week" ? "week" : "day",
|
|
56
|
+
isActive: row.is_active !== 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function mapAutomationRunRow(row) {
|
|
60
|
+
return {
|
|
61
|
+
ruleId: row.rule_id,
|
|
62
|
+
userId: row.user_id,
|
|
63
|
+
period: row.period,
|
|
64
|
+
firedAt: row.fired_at,
|
|
65
|
+
action: row.action
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function requireKind(value) {
|
|
69
|
+
if (!KINDS.includes(value)) {
|
|
70
|
+
throw new import_errors.ValidationError(`kind must be one of ${KINDS.join(", ")} (got ${value})`);
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
function requireWindow(kind, atMinute, afterMinutes) {
|
|
75
|
+
if (TIMED_KINDS.includes(kind)) {
|
|
76
|
+
if (atMinute === null || !Number.isInteger(atMinute) || atMinute < 0 || atMinute > 1439) {
|
|
77
|
+
throw new import_errors.ValidationError(`atMinute must be a minute of the day between 0 and 1439 (got ${atMinute})`);
|
|
78
|
+
}
|
|
79
|
+
return { atMinute, afterMinutes: null };
|
|
80
|
+
}
|
|
81
|
+
if (afterMinutes === null || !Number.isInteger(afterMinutes) || afterMinutes < 1 || afterMinutes > 1440) {
|
|
82
|
+
throw new import_errors.ValidationError(`afterMinutes must be between 1 and 1440 (got ${afterMinutes})`);
|
|
83
|
+
}
|
|
84
|
+
return { atMinute: null, afterMinutes };
|
|
85
|
+
}
|
|
86
|
+
const ALL_WEEKDAYS = [1, 2, 3, 4, 5, 6, 7];
|
|
87
|
+
function maskToWeekdays(mask) {
|
|
88
|
+
const days = [];
|
|
89
|
+
for (let day = 1; day <= 7; day += 1) {
|
|
90
|
+
if ((mask & 1 << day - 1) !== 0) {
|
|
91
|
+
days.push(day);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return days.length > 0 ? days : [...ALL_WEEKDAYS];
|
|
95
|
+
}
|
|
96
|
+
function weekdaysToMask(days) {
|
|
97
|
+
let mask = 0;
|
|
98
|
+
for (const day of days) {
|
|
99
|
+
mask |= 1 << day - 1;
|
|
100
|
+
}
|
|
101
|
+
return mask;
|
|
102
|
+
}
|
|
103
|
+
function requireWeekdays(value, fallback) {
|
|
104
|
+
const days = value != null ? value : fallback;
|
|
105
|
+
if (!Array.isArray(days) || days.length === 0) {
|
|
106
|
+
throw new import_errors.ValidationError("weekdays must name at least one day (1 = Monday \u2026 7 = Sunday)");
|
|
107
|
+
}
|
|
108
|
+
for (const day of days) {
|
|
109
|
+
if (!Number.isInteger(day) || day < 1 || day > 7) {
|
|
110
|
+
throw new import_errors.ValidationError(`weekdays must be ISO days 1..7 (got ${day})`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return [...new Set(days)].sort((left, right) => left - right);
|
|
114
|
+
}
|
|
115
|
+
function requireRepeat(value, fallback) {
|
|
116
|
+
const repeat = value != null ? value : fallback;
|
|
117
|
+
if (repeat !== "day" && repeat !== "week") {
|
|
118
|
+
throw new import_errors.ValidationError(`repeat must be "day" or "week" (got ${String(value)})`);
|
|
119
|
+
}
|
|
120
|
+
return repeat;
|
|
121
|
+
}
|
|
122
|
+
function createAutomationsRepository(db) {
|
|
123
|
+
const selectById = db.prepare(`SELECT ${RULE_COLUMNS} FROM automation_rules WHERE id = ?`);
|
|
124
|
+
const selectAll = db.prepare(`SELECT ${RULE_COLUMNS} FROM automation_rules ORDER BY id`);
|
|
125
|
+
const selectActive = db.prepare(`SELECT ${RULE_COLUMNS} FROM automation_rules WHERE is_active = 1 ORDER BY id`);
|
|
126
|
+
const insertRule = db.prepare(
|
|
127
|
+
`INSERT INTO automation_rules (label, kind, user_id, at_minute, after_minutes, weekdays, repeat, is_active, created_at, updated_at)
|
|
128
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
129
|
+
);
|
|
130
|
+
const updateRule = db.prepare(
|
|
131
|
+
`UPDATE automation_rules
|
|
132
|
+
SET label = ?, kind = ?, user_id = ?, at_minute = ?, after_minutes = ?, weekdays = ?, repeat = ?,
|
|
133
|
+
is_active = ?, updated_at = ?
|
|
134
|
+
WHERE id = ?`
|
|
135
|
+
);
|
|
136
|
+
const deleteRule = db.prepare("DELETE FROM automation_rules WHERE id = ?");
|
|
137
|
+
const deleteRunsOfRule = db.prepare("DELETE FROM automation_runs WHERE rule_id = ?");
|
|
138
|
+
const selectRuns = db.prepare(
|
|
139
|
+
`SELECT ${RUN_COLUMNS} FROM automation_runs
|
|
140
|
+
WHERE rule_id = ? AND (? IS NULL OR period = ?)
|
|
141
|
+
ORDER BY period DESC, rule_id LIMIT ?`
|
|
142
|
+
);
|
|
143
|
+
const selectAllRuns = db.prepare(
|
|
144
|
+
`SELECT ${RUN_COLUMNS} FROM automation_runs ORDER BY fired_at DESC, rule_id LIMIT ?`
|
|
145
|
+
);
|
|
146
|
+
const selectRun = db.prepare(
|
|
147
|
+
"SELECT 1 AS found FROM automation_runs WHERE rule_id = ? AND user_id = ? AND period = ?"
|
|
148
|
+
);
|
|
149
|
+
const insertRun = db.prepare(
|
|
150
|
+
`INSERT OR IGNORE INTO automation_runs (rule_id, user_id, period, fired_at, action)
|
|
151
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
152
|
+
);
|
|
153
|
+
const deleteRun = db.prepare("DELETE FROM automation_runs WHERE rule_id = ? AND user_id = ? AND period = ?");
|
|
154
|
+
const read = (id) => {
|
|
155
|
+
const row = selectById.get(id);
|
|
156
|
+
return row ? mapAutomationRuleRow(row) : null;
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
list(options) {
|
|
160
|
+
const rows = ((options == null ? void 0 : options.includeInactive) === true ? selectAll : selectActive).all();
|
|
161
|
+
return rows.map(mapAutomationRuleRow);
|
|
162
|
+
},
|
|
163
|
+
findById: read,
|
|
164
|
+
save(input) {
|
|
165
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
166
|
+
const kind = requireKind(input.kind);
|
|
167
|
+
const window = requireWindow(kind, (_a = input.atMinute) != null ? _a : null, (_b = input.afterMinutes) != null ? _b : null);
|
|
168
|
+
const label = ((_c = input.label) != null ? _c : "").trim() || null;
|
|
169
|
+
const userId = (_d = input.userId) != null ? _d : null;
|
|
170
|
+
if (userId !== null && (!Number.isInteger(userId) || userId <= 0)) {
|
|
171
|
+
throw new import_errors.ValidationError(`userId must reference an existing employee (got ${input.userId})`);
|
|
172
|
+
}
|
|
173
|
+
const isActive = input.isActive !== false;
|
|
174
|
+
const now = (_e = input.now) != null ? _e : Math.floor(Date.now() / 1e3);
|
|
175
|
+
const current = input.id === void 0 ? null : read(input.id);
|
|
176
|
+
if (input.id !== void 0 && !current) {
|
|
177
|
+
throw new import_errors.NotFoundError(`automation rule ${input.id} not found`);
|
|
178
|
+
}
|
|
179
|
+
const weekdays = requireWeekdays(input.weekdays, (_f = current == null ? void 0 : current.weekdays) != null ? _f : ALL_WEEKDAYS);
|
|
180
|
+
const repeat = requireRepeat(input.repeat, (_g = current == null ? void 0 : current.repeat) != null ? _g : "day");
|
|
181
|
+
const next = {
|
|
182
|
+
id: (_h = current == null ? void 0 : current.id) != null ? _h : 0,
|
|
183
|
+
label,
|
|
184
|
+
kind,
|
|
185
|
+
userId,
|
|
186
|
+
atMinute: window.atMinute,
|
|
187
|
+
afterMinutes: window.afterMinutes,
|
|
188
|
+
weekdays,
|
|
189
|
+
repeat,
|
|
190
|
+
isActive
|
|
191
|
+
};
|
|
192
|
+
const changes = current ? (0, import_audit.diffFields)(
|
|
193
|
+
current,
|
|
194
|
+
next,
|
|
195
|
+
AUDITED_FIELDS
|
|
196
|
+
) : null;
|
|
197
|
+
if (changes && weekdaysToMask((_i = current == null ? void 0 : current.weekdays) != null ? _i : ALL_WEEKDAYS) !== weekdaysToMask(weekdays)) {
|
|
198
|
+
changes.weekdays = { old: (_j = current == null ? void 0 : current.weekdays) != null ? _j : [], new: weekdays };
|
|
199
|
+
}
|
|
200
|
+
if (current && Object.keys(changes != null ? changes : {}).length === 0) {
|
|
201
|
+
return current;
|
|
202
|
+
}
|
|
203
|
+
let ruleId = (_k = current == null ? void 0 : current.id) != null ? _k : 0;
|
|
204
|
+
const run = db.transaction(() => {
|
|
205
|
+
var _a2;
|
|
206
|
+
if (current) {
|
|
207
|
+
updateRule.run(
|
|
208
|
+
label,
|
|
209
|
+
kind,
|
|
210
|
+
userId,
|
|
211
|
+
window.atMinute,
|
|
212
|
+
window.afterMinutes,
|
|
213
|
+
weekdaysToMask(weekdays),
|
|
214
|
+
repeat,
|
|
215
|
+
isActive ? 1 : 0,
|
|
216
|
+
now,
|
|
217
|
+
current.id
|
|
218
|
+
);
|
|
219
|
+
} else {
|
|
220
|
+
ruleId = Number(
|
|
221
|
+
insertRule.run(
|
|
222
|
+
label,
|
|
223
|
+
kind,
|
|
224
|
+
userId,
|
|
225
|
+
window.atMinute,
|
|
226
|
+
window.afterMinutes,
|
|
227
|
+
weekdaysToMask(weekdays),
|
|
228
|
+
repeat,
|
|
229
|
+
isActive ? 1 : 0,
|
|
230
|
+
now,
|
|
231
|
+
now
|
|
232
|
+
).lastInsertRowid
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
(0, import_audit.writeAuditLog)(db, {
|
|
236
|
+
atUtc: now,
|
|
237
|
+
actorId: input.actorId,
|
|
238
|
+
action: current ? "automation_rule.update" : "automation_rule.create",
|
|
239
|
+
entity: "automation_rule",
|
|
240
|
+
entityId: ruleId,
|
|
241
|
+
detail: current ? { changes } : {
|
|
242
|
+
kind,
|
|
243
|
+
userId,
|
|
244
|
+
atMinute: window.atMinute,
|
|
245
|
+
afterMinutes: window.afterMinutes,
|
|
246
|
+
weekdays,
|
|
247
|
+
repeat,
|
|
248
|
+
isActive
|
|
249
|
+
},
|
|
250
|
+
ip: (_a2 = input.actorIp) != null ? _a2 : null
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
run();
|
|
254
|
+
const saved = read(ruleId);
|
|
255
|
+
if (!saved) {
|
|
256
|
+
throw new Error(`automation rule ${ruleId} disappeared right after the write`);
|
|
257
|
+
}
|
|
258
|
+
return saved;
|
|
259
|
+
},
|
|
260
|
+
remove(input) {
|
|
261
|
+
var _a;
|
|
262
|
+
const current = read(input.id);
|
|
263
|
+
if (!current) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
const now = (_a = input.now) != null ? _a : Math.floor(Date.now() / 1e3);
|
|
267
|
+
const run = db.transaction(() => {
|
|
268
|
+
var _a2;
|
|
269
|
+
deleteRunsOfRule.run(input.id);
|
|
270
|
+
deleteRule.run(input.id);
|
|
271
|
+
(0, import_audit.writeAuditLog)(db, {
|
|
272
|
+
atUtc: now,
|
|
273
|
+
actorId: input.actorId,
|
|
274
|
+
action: "automation_rule.delete",
|
|
275
|
+
entity: "automation_rule",
|
|
276
|
+
entityId: input.id,
|
|
277
|
+
detail: { kind: current.kind, userId: current.userId, label: current.label },
|
|
278
|
+
ip: (_a2 = input.actorIp) != null ? _a2 : null
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
run();
|
|
282
|
+
return true;
|
|
283
|
+
},
|
|
284
|
+
runs(input) {
|
|
285
|
+
var _a, _b, _c;
|
|
286
|
+
const limit = Math.min(Math.max((_a = input == null ? void 0 : input.limit) != null ? _a : 50, 1), 500);
|
|
287
|
+
if ((input == null ? void 0 : input.ruleId) === void 0) {
|
|
288
|
+
return selectAllRuns.all(limit).map(mapAutomationRunRow);
|
|
289
|
+
}
|
|
290
|
+
const rows = selectRuns.all(
|
|
291
|
+
input.ruleId,
|
|
292
|
+
(_b = input.period) != null ? _b : null,
|
|
293
|
+
(_c = input.period) != null ? _c : null,
|
|
294
|
+
limit
|
|
295
|
+
);
|
|
296
|
+
return rows.map(mapAutomationRunRow);
|
|
297
|
+
},
|
|
298
|
+
hasRun(input) {
|
|
299
|
+
return selectRun.get(input.ruleId, input.userId, input.period) !== void 0;
|
|
300
|
+
},
|
|
301
|
+
recordRun(input) {
|
|
302
|
+
var _a;
|
|
303
|
+
const result = insertRun.run(
|
|
304
|
+
input.ruleId,
|
|
305
|
+
input.userId,
|
|
306
|
+
input.period,
|
|
307
|
+
(_a = input.now) != null ? _a : Math.floor(Date.now() / 1e3),
|
|
308
|
+
input.action
|
|
309
|
+
);
|
|
310
|
+
return result.changes > 0;
|
|
311
|
+
},
|
|
312
|
+
forgetRun(input) {
|
|
313
|
+
return deleteRun.run(input.ruleId, input.userId, input.period).changes > 0;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
318
|
+
0 && (module.exports = {
|
|
319
|
+
ALL_WEEKDAYS,
|
|
320
|
+
createAutomationsRepository,
|
|
321
|
+
mapAutomationRuleRow,
|
|
322
|
+
mapAutomationRunRow,
|
|
323
|
+
maskToWeekdays,
|
|
324
|
+
weekdaysToMask
|
|
325
|
+
});
|
|
326
|
+
//# sourceMappingURL=automations.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/db/repositories/automations.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Automation rules (`automation_rules`, `automation_runs`).\n *\n * The rules of the adapter itself \u2014 not of a foreign state like the trigger rules. Three kinds are supported:\n *\n * - `clockOut`: at a certain local time the adapter punches out an employee who is still clocked in. The punch is\n * written like any other one (`source: \"api\"`, note `auto.clock_out`), so it appears in the day, in the report\n * and in the audit trail \u2014 the employee can correct it afterwards like any other punch.\n * - `missingPunch`: the same moment, but the adapter only reports it. Nothing is written, so a shop that wants to\n * decide case by case gets a warning instead of a silent correction.\n * - `breakReminder`: an employee whose running work block reached the configured length gets a reminder \u2014 after a\n * break, the block starts again.\n *\n * `user_id` is `NULL` for a company rule and set for a rule of one employee. A rule runs at most once per employee\n * and local date: `automation_runs` holds that decision, which also gives the administration a small log (`action`\n * and `fired_at`) to look at.\n */\n\nimport type { Db } from \"../database\";\nimport { NotFoundError, ValidationError } from \"../../errors\";\nimport { diffFields, writeAuditLog } from \"./audit\";\n\n/** Kinds an automation rule can have. */\nexport type AutomationKind = \"clockIn\" | \"clockOut\" | \"missingPunch\" | \"breakReminder\";\n\n/** How often a rule may act for one employee. */\nexport type AutomationRepeat = \"day\" | \"week\";\n\n/** An automation rule as it is stored. */\nexport interface AutomationRuleRecord {\n\t/** Primary key */\n\tid: number;\n\t/** Free-form label, shown in the administration */\n\tlabel: string | null;\n\t/** What the rule does */\n\tkind: AutomationKind;\n\t/** Employee the rule applies to, `null` for every employee */\n\tuserId: number | null;\n\t/** Minute of the local day the rule acts at (kinds `clockOut` and `missingPunch`) */\n\tatMinute: number | null;\n\t/** Length of the running work block in minutes (kind `breakReminder`) */\n\tafterMinutes: number | null;\n\t/** Days of the week the rule runs on, ISO numbers 1 (Monday) to 7 (Sunday) */\n\tweekdays: number[];\n\t/** Whether the rule may act once a day or once an ISO week per employee */\n\trepeat: AutomationRepeat;\n\t/** Inactive rules are ignored but keep their history */\n\tisActive: boolean;\n}\n\n/** Input for creating or changing a rule. */\nexport interface SaveAutomationRuleInput {\n\t/** Id of an existing rule, omitted when a new rule is created */\n\tid?: number;\n\t/** Free-form label */\n\tlabel?: string | null;\n\t/** What the rule does */\n\tkind: AutomationKind;\n\t/** Employee the rule applies to, `null` or omitted for every employee */\n\tuserId?: number | null;\n\t/** Minute of the local day (kinds `clockOut` and `missingPunch`) */\n\tatMinute?: number | null;\n\t/** Length of the running work block in minutes (kind `breakReminder`) */\n\tafterMinutes?: number | null;\n\t/** Days of the week the rule runs on (ISO 1..7), omitted keeps the stored selection */\n\tweekdays?: number[];\n\t/** How often the rule may act, omitted keeps the stored value */\n\trepeat?: AutomationRepeat;\n\t/** `false` disables the rule without deleting it */\n\tisActive?: boolean;\n\t/** Who changes the rule */\n\tactorId: number;\n\t/** Client IP address of the actor */\n\tactorIp?: string | null;\n\t/** Instant of the change, defaults to now */\n\tnow?: number;\n}\n\n/** One run of a rule. */\nexport interface AutomationRunRecord {\n\t/** Rule that ran */\n\truleId: number;\n\t/** Employee it ran for */\n\tuserId: number;\n\t/** Period it ran in: the local date, or the ISO week (`2026-W38`) for a weekly rule */\n\tperiod: string;\n\t/** Instant it ran */\n\tfiredAt: number;\n\t/** Short description of what happened */\n\taction: string;\n}\n\n/** Automation rule storage operations. */\nexport interface AutomationsRepository {\n\t/** All rules, optionally including the disabled ones */\n\tlist(options?: { includeInactive?: boolean }): AutomationRuleRecord[];\n\t/** Reads one rule */\n\tfindById(id: number): AutomationRuleRecord | null;\n\t/** Creates or updates a rule */\n\tsave(input: SaveAutomationRuleInput): AutomationRuleRecord;\n\t/** Deletes a rule (its runs go with it) */\n\tremove(input: { id: number; actorId: number; actorIp?: string | null; now?: number }): boolean;\n\t/** Runs of the rules, newest first */\n\truns(input?: { ruleId?: number; period?: string; limit?: number }): AutomationRunRecord[];\n\t/** True when the rule already ran for that employee in that period */\n\thasRun(input: { ruleId: number; userId: number; period: string }): boolean;\n\t/** Notes a run; a second call for the same rule, employee and period changes nothing and returns `false` */\n\trecordRun(input: { ruleId: number; userId: number; period: string; action: string; now?: number }): boolean;\n\t/** Forgets a run again \u2014 used when an action failed, so the next check can retry it */\n\tforgetRun(input: { ruleId: number; userId: number; period: string }): boolean;\n}\n\n/** Raw database row of a rule. */\ninterface AutomationRuleRow {\n\tid: number;\n\tlabel: string | null;\n\tkind: string;\n\tuser_id: number | null;\n\tat_minute: number | null;\n\tafter_minutes: number | null;\n\tweekdays: number;\n\trepeat: string;\n\tis_active: number;\n}\n\n/** Raw database row of a run. */\ninterface AutomationRunRow {\n\trule_id: number;\n\tuser_id: number;\n\tperiod: string;\n\tfired_at: number;\n\taction: string;\n}\n\nconst RULE_COLUMNS = \"id, label, kind, user_id, at_minute, after_minutes, weekdays, repeat, is_active\";\nconst RUN_COLUMNS = \"rule_id, user_id, period, fired_at, action\";\n\n/**\n * Fields of a rule that are compared for the audit trail.\n *\n * `weekdays` is not in here: it is a list, and the comparison of the audit trail works on single values. The saved\n * selection is written to the audit detail separately.\n */\nconst AUDITED_FIELDS: (keyof AutomationRuleRecord)[] = [\n\t\"label\",\n\t\"kind\",\n\t\"userId\",\n\t\"atMinute\",\n\t\"afterMinutes\",\n\t\"repeat\",\n\t\"isActive\",\n];\n\n/** Kinds a rule may use. */\nconst KINDS: AutomationKind[] = [\"clockIn\", \"clockOut\", \"missingPunch\", \"breakReminder\"];\n\n/** Kinds that act at a fixed local time. */\nconst TIMED_KINDS: AutomationKind[] = [\"clockIn\", \"clockOut\", \"missingPunch\"];\n\n/**\n * Maps a database row to a rule record.\n *\n * @param row - raw database row\n * @returns rule record\n */\nexport function mapAutomationRuleRow(row: AutomationRuleRow): AutomationRuleRecord {\n\treturn {\n\t\tid: row.id,\n\t\tlabel: row.label,\n\t\tkind: KINDS.includes(row.kind as AutomationKind) ? (row.kind as AutomationKind) : \"clockOut\",\n\t\tuserId: row.user_id,\n\t\tatMinute: row.at_minute,\n\t\tafterMinutes: row.after_minutes,\n\t\t// a row written before the migration has no selection: every day is the sensible default\n\t\tweekdays: maskToWeekdays(row.weekdays ?? 127),\n\t\trepeat: row.repeat === \"week\" ? \"week\" : \"day\",\n\t\tisActive: row.is_active !== 0,\n\t};\n}\n\n/**\n * Maps a database row to a run record.\n *\n * @param row - raw database row\n * @returns run record\n */\nexport function mapAutomationRunRow(row: AutomationRunRow): AutomationRunRecord {\n\treturn {\n\t\truleId: row.rule_id,\n\t\tuserId: row.user_id,\n\t\tperiod: row.period,\n\t\tfiredAt: row.fired_at,\n\t\taction: row.action,\n\t};\n}\n\n/**\n * Validates the kind of a rule.\n *\n * @param value - kind from the request\n * @returns the validated kind\n */\nfunction requireKind(value: string): AutomationKind {\n\tif (!KINDS.includes(value as AutomationKind)) {\n\t\tthrow new ValidationError(`kind must be one of ${KINDS.join(\", \")} (got ${value})`);\n\t}\n\treturn value as AutomationKind;\n}\n\n/**\n * Validates the time of a rule: a timed kind needs a minute of the day, the reminder needs a length.\n *\n * @param kind - kind of the rule\n * @param atMinute - minute of the local day\n * @param afterMinutes - length of the running work block\n * @returns the validated values\n */\nfunction requireWindow(\n\tkind: AutomationKind,\n\tatMinute: number | null,\n\tafterMinutes: number | null,\n): { atMinute: number | null; afterMinutes: number | null } {\n\tif (TIMED_KINDS.includes(kind)) {\n\t\tif (atMinute === null || !Number.isInteger(atMinute) || atMinute < 0 || atMinute > 1439) {\n\t\t\tthrow new ValidationError(`atMinute must be a minute of the day between 0 and 1439 (got ${atMinute})`);\n\t\t}\n\t\treturn { atMinute, afterMinutes: null };\n\t}\n\n\tif (afterMinutes === null || !Number.isInteger(afterMinutes) || afterMinutes < 1 || afterMinutes > 1440) {\n\t\tthrow new ValidationError(`afterMinutes must be between 1 and 1440 (got ${afterMinutes})`);\n\t}\n\treturn { atMinute: null, afterMinutes };\n}\n\n/** Days a rule runs on when nothing else is chosen. */\nexport const ALL_WEEKDAYS: number[] = [1, 2, 3, 4, 5, 6, 7];\n\n/**\n * Turns the stored bitmask into ISO weekdays.\n *\n * @param mask - bits 0..6, bit 0 is Monday\n * @returns the days, at least every day when the mask is empty\n */\nexport function maskToWeekdays(mask: number): number[] {\n\tconst days: number[] = [];\n\tfor (let day = 1; day <= 7; day += 1) {\n\t\tif ((mask & (1 << (day - 1))) !== 0) {\n\t\t\tdays.push(day);\n\t\t}\n\t}\n\treturn days.length > 0 ? days : [...ALL_WEEKDAYS];\n}\n\n/**\n * Turns ISO weekdays into the stored bitmask.\n *\n * @param days - the days, ISO numbers 1..7\n * @returns the mask\n */\nexport function weekdaysToMask(days: number[]): number {\n\tlet mask = 0;\n\tfor (const day of days) {\n\t\tmask |= 1 << (day - 1);\n\t}\n\treturn mask;\n}\n\n/**\n * Validates the weekday selection of a rule.\n *\n * @param value - selection from the request, `undefined` keeps the stored one\n * @param fallback - the stored selection\n * @returns the validated, sorted selection\n */\nfunction requireWeekdays(value: number[] | undefined, fallback: number[]): number[] {\n\tconst days = value ?? fallback;\n\tif (!Array.isArray(days) || days.length === 0) {\n\t\tthrow new ValidationError(\"weekdays must name at least one day (1 = Monday \u2026 7 = Sunday)\");\n\t}\n\tfor (const day of days) {\n\t\tif (!Number.isInteger(day) || day < 1 || day > 7) {\n\t\t\tthrow new ValidationError(`weekdays must be ISO days 1..7 (got ${day})`);\n\t\t}\n\t}\n\treturn [...new Set(days)].sort((left, right) => left - right);\n}\n\n/**\n * Validates how often a rule may act.\n *\n * @param value - value from the request, `undefined` keeps the stored one\n * @param fallback - the stored value\n * @returns the validated value\n */\nfunction requireRepeat(value: AutomationRepeat | undefined, fallback: AutomationRepeat): AutomationRepeat {\n\tconst repeat = value ?? fallback;\n\tif (repeat !== \"day\" && repeat !== \"week\") {\n\t\t// the type already rules it out, this is for a caller that is not TypeScript\n\t\tthrow new ValidationError(`repeat must be \"day\" or \"week\" (got ${String(value)})`);\n\t}\n\treturn repeat;\n}\n\n/**\n * Creates the automation rule repository.\n *\n * @param db - open database handle\n * @returns repository instance\n */\nexport function createAutomationsRepository(db: Db): AutomationsRepository {\n\tconst selectById = db.prepare(`SELECT ${RULE_COLUMNS} FROM automation_rules WHERE id = ?`);\n\tconst selectAll = db.prepare(`SELECT ${RULE_COLUMNS} FROM automation_rules ORDER BY id`);\n\tconst selectActive = db.prepare(`SELECT ${RULE_COLUMNS} FROM automation_rules WHERE is_active = 1 ORDER BY id`);\n\tconst insertRule = db.prepare(\n\t\t`INSERT INTO automation_rules (label, kind, user_id, at_minute, after_minutes, weekdays, repeat, is_active, created_at, updated_at)\n\t\t VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t);\n\tconst updateRule = db.prepare(\n\t\t`UPDATE automation_rules\n\t\t SET label = ?, kind = ?, user_id = ?, at_minute = ?, after_minutes = ?, weekdays = ?, repeat = ?,\n\t\t is_active = ?, updated_at = ?\n\t\t WHERE id = ?`,\n\t);\n\tconst deleteRule = db.prepare(\"DELETE FROM automation_rules WHERE id = ?\");\n\t// the runs are deleted with their rule: the rebuilt table of migration 18 has no foreign key for that\n\tconst deleteRunsOfRule = db.prepare(\"DELETE FROM automation_runs WHERE rule_id = ?\");\n\tconst selectRuns = db.prepare(\n\t\t`SELECT ${RUN_COLUMNS} FROM automation_runs\n\t\t WHERE rule_id = ? AND (? IS NULL OR period = ?)\n\t\t ORDER BY period DESC, rule_id LIMIT ?`,\n\t);\n\tconst selectAllRuns = db.prepare(\n\t\t`SELECT ${RUN_COLUMNS} FROM automation_runs ORDER BY fired_at DESC, rule_id LIMIT ?`,\n\t);\n\tconst selectRun = db.prepare(\n\t\t\"SELECT 1 AS found FROM automation_runs WHERE rule_id = ? AND user_id = ? AND period = ?\",\n\t);\n\tconst insertRun = db.prepare(\n\t\t`INSERT OR IGNORE INTO automation_runs (rule_id, user_id, period, fired_at, action)\n\t\t VALUES (?, ?, ?, ?, ?)`,\n\t);\n\tconst deleteRun = db.prepare(\"DELETE FROM automation_runs WHERE rule_id = ? AND user_id = ? AND period = ?\");\n\n\t/**\n\t * Reads one rule.\n\t *\n\t * @param id - rule id\n\t * @returns the rule or `null`\n\t */\n\tconst read = (id: number): AutomationRuleRecord | null => {\n\t\tconst row = selectById.get(id) as AutomationRuleRow | undefined;\n\t\treturn row ? mapAutomationRuleRow(row) : null;\n\t};\n\n\treturn {\n\t\tlist(options?: { includeInactive?: boolean }): AutomationRuleRecord[] {\n\t\t\tconst rows = (options?.includeInactive === true ? selectAll : selectActive).all() as AutomationRuleRow[];\n\t\t\treturn rows.map(mapAutomationRuleRow);\n\t\t},\n\n\t\tfindById: read,\n\n\t\tsave(input: SaveAutomationRuleInput): AutomationRuleRecord {\n\t\t\tconst kind = requireKind(input.kind);\n\t\t\tconst window = requireWindow(kind, input.atMinute ?? null, input.afterMinutes ?? null);\n\t\t\tconst label = (input.label ?? \"\").trim() || null;\n\t\t\tconst userId = input.userId ?? null;\n\t\t\tif (userId !== null && (!Number.isInteger(userId) || userId <= 0)) {\n\t\t\t\tthrow new ValidationError(`userId must reference an existing employee (got ${input.userId})`);\n\t\t\t}\n\t\t\tconst isActive = input.isActive !== false;\n\t\t\tconst now = input.now ?? Math.floor(Date.now() / 1000);\n\n\t\t\tconst current = input.id === undefined ? null : read(input.id);\n\t\t\tif (input.id !== undefined && !current) {\n\t\t\t\tthrow new NotFoundError(`automation rule ${input.id} not found`);\n\t\t\t}\n\t\t\tconst weekdays = requireWeekdays(input.weekdays, current?.weekdays ?? ALL_WEEKDAYS);\n\t\t\tconst repeat = requireRepeat(input.repeat, current?.repeat ?? \"day\");\n\n\t\t\tconst next: AutomationRuleRecord = {\n\t\t\t\tid: current?.id ?? 0,\n\t\t\t\tlabel,\n\t\t\t\tkind,\n\t\t\t\tuserId,\n\t\t\t\tatMinute: window.atMinute,\n\t\t\t\tafterMinutes: window.afterMinutes,\n\t\t\t\tweekdays,\n\t\t\t\trepeat,\n\t\t\t\tisActive,\n\t\t\t};\n\t\t\tconst changes = current\n\t\t\t\t? diffFields(\n\t\t\t\t\t\tcurrent as unknown as Record<string, unknown>,\n\t\t\t\t\t\tnext as unknown as Record<string, unknown>,\n\t\t\t\t\t\tAUDITED_FIELDS as unknown as (keyof Record<string, unknown>)[],\n\t\t\t\t\t)\n\t\t\t\t: null;\n\t\t\t// the days are a list, so they are compared by their mask \u2014 that is also what the audit trail shows\n\t\t\tif (changes && weekdaysToMask(current?.weekdays ?? ALL_WEEKDAYS) !== weekdaysToMask(weekdays)) {\n\t\t\t\tchanges.weekdays = { old: current?.weekdays ?? [], new: weekdays };\n\t\t\t}\n\t\t\tif (current && Object.keys(changes ?? {}).length === 0) {\n\t\t\t\treturn current;\n\t\t\t}\n\n\t\t\tlet ruleId = current?.id ?? 0;\n\t\t\tconst run = db.transaction((): void => {\n\t\t\t\tif (current) {\n\t\t\t\t\tupdateRule.run(\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tkind,\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\twindow.atMinute,\n\t\t\t\t\t\twindow.afterMinutes,\n\t\t\t\t\t\tweekdaysToMask(weekdays),\n\t\t\t\t\t\trepeat,\n\t\t\t\t\t\tisActive ? 1 : 0,\n\t\t\t\t\t\tnow,\n\t\t\t\t\t\tcurrent.id,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\truleId = Number(\n\t\t\t\t\t\tinsertRule.run(\n\t\t\t\t\t\t\tlabel,\n\t\t\t\t\t\t\tkind,\n\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\twindow.atMinute,\n\t\t\t\t\t\t\twindow.afterMinutes,\n\t\t\t\t\t\t\tweekdaysToMask(weekdays),\n\t\t\t\t\t\t\trepeat,\n\t\t\t\t\t\t\tisActive ? 1 : 0,\n\t\t\t\t\t\t\tnow,\n\t\t\t\t\t\t\tnow,\n\t\t\t\t\t\t).lastInsertRowid,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\twriteAuditLog(db, {\n\t\t\t\t\tatUtc: now,\n\t\t\t\t\tactorId: input.actorId,\n\t\t\t\t\taction: current ? \"automation_rule.update\" : \"automation_rule.create\",\n\t\t\t\t\tentity: \"automation_rule\",\n\t\t\t\t\tentityId: ruleId,\n\t\t\t\t\tdetail: current\n\t\t\t\t\t\t? { changes }\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tkind,\n\t\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\t\tatMinute: window.atMinute,\n\t\t\t\t\t\t\t\tafterMinutes: window.afterMinutes,\n\t\t\t\t\t\t\t\tweekdays,\n\t\t\t\t\t\t\t\trepeat,\n\t\t\t\t\t\t\t\tisActive,\n\t\t\t\t\t\t\t},\n\t\t\t\t\tip: input.actorIp ?? null,\n\t\t\t\t});\n\t\t\t});\n\t\t\trun();\n\n\t\t\tconst saved = read(ruleId);\n\t\t\tif (!saved) {\n\t\t\t\tthrow new Error(`automation rule ${ruleId} disappeared right after the write`);\n\t\t\t}\n\t\t\treturn saved;\n\t\t},\n\n\t\tremove(input: { id: number; actorId: number; actorIp?: string | null; now?: number }): boolean {\n\t\t\tconst current = read(input.id);\n\t\t\tif (!current) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tconst now = input.now ?? Math.floor(Date.now() / 1000);\n\t\t\tconst run = db.transaction((): void => {\n\t\t\t\tdeleteRunsOfRule.run(input.id);\n\t\t\t\tdeleteRule.run(input.id);\n\t\t\t\twriteAuditLog(db, {\n\t\t\t\t\tatUtc: now,\n\t\t\t\t\tactorId: input.actorId,\n\t\t\t\t\taction: \"automation_rule.delete\",\n\t\t\t\t\tentity: \"automation_rule\",\n\t\t\t\t\tentityId: input.id,\n\t\t\t\t\tdetail: { kind: current.kind, userId: current.userId, label: current.label },\n\t\t\t\t\tip: input.actorIp ?? null,\n\t\t\t\t});\n\t\t\t});\n\t\t\trun();\n\t\t\treturn true;\n\t\t},\n\n\t\truns(input?: { ruleId?: number; period?: string; limit?: number }): AutomationRunRecord[] {\n\t\t\tconst limit = Math.min(Math.max(input?.limit ?? 50, 1), 500);\n\t\t\tif (input?.ruleId === undefined) {\n\t\t\t\treturn (selectAllRuns.all(limit) as AutomationRunRow[]).map(mapAutomationRunRow);\n\t\t\t}\n\t\t\tconst rows = selectRuns.all(\n\t\t\t\tinput.ruleId,\n\t\t\t\tinput.period ?? null,\n\t\t\t\tinput.period ?? null,\n\t\t\t\tlimit,\n\t\t\t) as AutomationRunRow[];\n\t\t\treturn rows.map(mapAutomationRunRow);\n\t\t},\n\n\t\thasRun(input: { ruleId: number; userId: number; period: string }): boolean {\n\t\t\treturn selectRun.get(input.ruleId, input.userId, input.period) !== undefined;\n\t\t},\n\n\t\trecordRun(input: { ruleId: number; userId: number; period: string; action: string; now?: number }): boolean {\n\t\t\tconst result = insertRun.run(\n\t\t\t\tinput.ruleId,\n\t\t\t\tinput.userId,\n\t\t\t\tinput.period,\n\t\t\t\tinput.now ?? Math.floor(Date.now() / 1000),\n\t\t\t\tinput.action,\n\t\t\t);\n\t\t\treturn result.changes > 0;\n\t\t},\n\n\t\tforgetRun(input: { ruleId: number; userId: number; period: string }): boolean {\n\t\t\treturn deleteRun.run(input.ruleId, input.userId, input.period).changes > 0;\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["_a"]
|
|
7
|
+
}
|