@mtakla/cronops 0.1.1-rc4 → 0.1.1-rc5
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/dist/api/openapi.d.ts +261 -0
- package/dist/api/webapi.d.ts +7 -0
- package/dist/errors/JobError.d.ts +5 -0
- package/dist/handlers/AbstractHandler.d.ts +23 -0
- package/dist/handlers/ExecHandler.d.ts +14 -0
- package/dist/handlers/FileArchiveHandler.d.ts +7 -0
- package/dist/handlers/FileCopyHandler.d.ts +7 -0
- package/dist/handlers/FileDeleteHandler.d.ts +7 -0
- package/dist/handlers/FileMoveHandler.d.ts +7 -0
- package/dist/index.d.ts +17 -0
- package/dist/models/FileHistoryModel.d.ts +16 -0
- package/dist/models/JobModel.d.ts +21 -0
- package/dist/models/JobRunnerContext.d.ts +22 -0
- package/dist/models/JobRunnerResult.d.ts +12 -0
- package/dist/models/JobRunnerSetup.d.ts +26 -0
- package/dist/models/PermissionModel.d.ts +7 -0
- package/dist/server.d.ts +2 -0
- package/dist/tasks/AbstractTask.d.ts +19 -0
- package/dist/tasks/JobLoader.d.ts +15 -0
- package/dist/tasks/JobRunner.d.ts +19 -0
- package/dist/tasks/JobScheduler.d.ts +31 -0
- package/dist/tests/loadtest.d.ts +1 -0
- package/dist/types/Config.types.d.ts +34 -0
- package/dist/types/Options.types.d.ts +34 -0
- package/dist/types/Task.types.d.ts +64 -0
- package/package.json +25 -12
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
export declare const openapi: {
|
|
2
|
+
openapi: string;
|
|
3
|
+
info: {
|
|
4
|
+
title: string;
|
|
5
|
+
version: string;
|
|
6
|
+
};
|
|
7
|
+
components: {
|
|
8
|
+
securitySchemes: {
|
|
9
|
+
ApiKeyBearer: {
|
|
10
|
+
type: string;
|
|
11
|
+
scheme: string;
|
|
12
|
+
bearerFormat: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
tags: ({
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
externalDocs?: never;
|
|
20
|
+
} | {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
externalDocs: {
|
|
24
|
+
description: string;
|
|
25
|
+
url: string;
|
|
26
|
+
};
|
|
27
|
+
})[];
|
|
28
|
+
paths: {
|
|
29
|
+
"/health": {
|
|
30
|
+
get: {
|
|
31
|
+
summary: string;
|
|
32
|
+
tags: string[];
|
|
33
|
+
security: never[];
|
|
34
|
+
responses: {
|
|
35
|
+
"200": {
|
|
36
|
+
description: string;
|
|
37
|
+
content: {
|
|
38
|
+
"application/json": {
|
|
39
|
+
schema: {
|
|
40
|
+
type: string;
|
|
41
|
+
properties: {
|
|
42
|
+
status: {
|
|
43
|
+
type: string;
|
|
44
|
+
example: string;
|
|
45
|
+
};
|
|
46
|
+
active_jobs: {
|
|
47
|
+
type: string;
|
|
48
|
+
example: number;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
required: string[];
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
"/docs": {
|
|
60
|
+
get: {
|
|
61
|
+
summary: string;
|
|
62
|
+
tags: string[];
|
|
63
|
+
security: never[];
|
|
64
|
+
responses: {
|
|
65
|
+
"200": {
|
|
66
|
+
description: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
"/openapi.json": {
|
|
72
|
+
get: {
|
|
73
|
+
summary: string;
|
|
74
|
+
tags: string[];
|
|
75
|
+
security: never[];
|
|
76
|
+
responses: {
|
|
77
|
+
"200": {
|
|
78
|
+
description: string;
|
|
79
|
+
content: {
|
|
80
|
+
"application/json": {};
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
"/api/jobs/trigger/{jobId}": {
|
|
87
|
+
post: {
|
|
88
|
+
summary: string;
|
|
89
|
+
tags: string[];
|
|
90
|
+
parameters: {
|
|
91
|
+
name: string;
|
|
92
|
+
in: string;
|
|
93
|
+
required: boolean;
|
|
94
|
+
schema: {
|
|
95
|
+
type: string;
|
|
96
|
+
};
|
|
97
|
+
}[];
|
|
98
|
+
responses: {
|
|
99
|
+
"200": {
|
|
100
|
+
description: string;
|
|
101
|
+
content: {
|
|
102
|
+
"application/json": {
|
|
103
|
+
schema: {
|
|
104
|
+
type: string;
|
|
105
|
+
properties: {
|
|
106
|
+
triggered: {
|
|
107
|
+
type: string;
|
|
108
|
+
example: boolean;
|
|
109
|
+
};
|
|
110
|
+
jobId: {
|
|
111
|
+
type: string;
|
|
112
|
+
example: string;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
required: string[];
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
"404": {
|
|
121
|
+
description: string;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
"/api/jobs/pause/{jobId}": {
|
|
127
|
+
post: {
|
|
128
|
+
summary: string;
|
|
129
|
+
tags: string[];
|
|
130
|
+
parameters: {
|
|
131
|
+
name: string;
|
|
132
|
+
in: string;
|
|
133
|
+
required: boolean;
|
|
134
|
+
schema: {
|
|
135
|
+
type: string;
|
|
136
|
+
};
|
|
137
|
+
}[];
|
|
138
|
+
responses: {
|
|
139
|
+
"200": {
|
|
140
|
+
description: string;
|
|
141
|
+
content: {
|
|
142
|
+
"application/json": {
|
|
143
|
+
schema: {
|
|
144
|
+
type: string;
|
|
145
|
+
properties: {
|
|
146
|
+
paused: {
|
|
147
|
+
type: string;
|
|
148
|
+
example: boolean;
|
|
149
|
+
};
|
|
150
|
+
jobId: {
|
|
151
|
+
type: string;
|
|
152
|
+
example: string;
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
"/api/jobs/resume/{jobId}": {
|
|
163
|
+
post: {
|
|
164
|
+
summary: string;
|
|
165
|
+
tags: string[];
|
|
166
|
+
parameters: {
|
|
167
|
+
name: string;
|
|
168
|
+
in: string;
|
|
169
|
+
required: boolean;
|
|
170
|
+
schema: {
|
|
171
|
+
type: string;
|
|
172
|
+
};
|
|
173
|
+
}[];
|
|
174
|
+
responses: {
|
|
175
|
+
"200": {
|
|
176
|
+
description: string;
|
|
177
|
+
content: {
|
|
178
|
+
"application/json": {
|
|
179
|
+
schema: {
|
|
180
|
+
type: string;
|
|
181
|
+
properties: {
|
|
182
|
+
resumed: {
|
|
183
|
+
type: string;
|
|
184
|
+
example: boolean;
|
|
185
|
+
};
|
|
186
|
+
jobId: {
|
|
187
|
+
type: string;
|
|
188
|
+
example: string;
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
"/api/jobs/pause/": {
|
|
199
|
+
post: {
|
|
200
|
+
summary: string;
|
|
201
|
+
tags: string[];
|
|
202
|
+
responses: {
|
|
203
|
+
"200": {
|
|
204
|
+
description: string;
|
|
205
|
+
content: {
|
|
206
|
+
"application/json": {
|
|
207
|
+
schema: {
|
|
208
|
+
type: string;
|
|
209
|
+
properties: {
|
|
210
|
+
paused: {
|
|
211
|
+
type: string;
|
|
212
|
+
example: boolean;
|
|
213
|
+
};
|
|
214
|
+
jobs: {
|
|
215
|
+
type: string;
|
|
216
|
+
example: number;
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
"/api/jobs/resume/": {
|
|
227
|
+
post: {
|
|
228
|
+
summary: string;
|
|
229
|
+
tags: string[];
|
|
230
|
+
responses: {
|
|
231
|
+
"200": {
|
|
232
|
+
description: string;
|
|
233
|
+
content: {
|
|
234
|
+
"application/json": {
|
|
235
|
+
schema: {
|
|
236
|
+
type: string;
|
|
237
|
+
properties: {
|
|
238
|
+
resumed: {
|
|
239
|
+
type: string;
|
|
240
|
+
example: boolean;
|
|
241
|
+
};
|
|
242
|
+
jobs: {
|
|
243
|
+
type: string;
|
|
244
|
+
example: number;
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
servers: {
|
|
256
|
+
url: string;
|
|
257
|
+
}[];
|
|
258
|
+
security: {
|
|
259
|
+
ApiKeyBearer: never[];
|
|
260
|
+
}[];
|
|
261
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JobRunnerSetup } from "../models/JobRunnerSetup.js";
|
|
2
|
+
import type { ActionHandler, SourceFile, RunnerContext, FileHistory } from "../types/Task.types.js";
|
|
3
|
+
import type { Job } from "../types/Config.types.js";
|
|
4
|
+
export declare abstract class AbstractHandler implements ActionHandler {
|
|
5
|
+
protected setup: JobRunnerSetup;
|
|
6
|
+
constructor(setup: JobRunnerSetup);
|
|
7
|
+
validateJob(_job: Job): void;
|
|
8
|
+
process(_ctx: RunnerContext): Promise<void>;
|
|
9
|
+
processFiles(_ctx: RunnerContext, _entries: string[], _fileHistory: FileHistory): Promise<void>;
|
|
10
|
+
protected assertSourceConfigExists(job: Job): void;
|
|
11
|
+
protected assertTargetConfigExists(job: Job): void;
|
|
12
|
+
protected assertSourceDirExist(job: Job): void;
|
|
13
|
+
protected processSources(ctx: RunnerContext, entries: string[], fileHistory?: FileHistory, processor?: (ctx: RunnerContext, entry: SourceFile, fileHistory?: FileHistory) => Promise<void>): Promise<void>;
|
|
14
|
+
protected copyOrMoveFile(ctx: RunnerContext, entry: SourceFile, fileHistory?: FileHistory): Promise<void>;
|
|
15
|
+
protected deleteFile(ctx: RunnerContext, { sourcePath }: SourceFile): Promise<void>;
|
|
16
|
+
protected createArchive(ctx: RunnerContext, entries: string[], fileHistory: FileHistory): Promise<void>;
|
|
17
|
+
protected deleteEmptySourceDirs(ctx: RunnerContext): Promise<void>;
|
|
18
|
+
protected createTargetDirs(ctx: RunnerContext, entries: string[]): Promise<void>;
|
|
19
|
+
protected setTargetDirPermissions(ctx: RunnerContext): Promise<void>;
|
|
20
|
+
protected cleanup(ctx: RunnerContext, fileHistory: FileHistory): Promise<void>;
|
|
21
|
+
private setTargetFilePermissions;
|
|
22
|
+
private getFolderPermissionPromises;
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AbstractHandler } from "./AbstractHandler.js";
|
|
2
|
+
import type { ActionHandler, FileHistory, RunnerContext, SourceFile } from "../types/Task.types.js";
|
|
3
|
+
import type { Job } from "../types/Config.types.js";
|
|
4
|
+
export declare class ExecHandler extends AbstractHandler implements ActionHandler {
|
|
5
|
+
validateJob(job: Job): void;
|
|
6
|
+
process(ctx: RunnerContext): Promise<void>;
|
|
7
|
+
processFiles(ctx: RunnerContext, entries: string[], fileHistory: FileHistory): Promise<void>;
|
|
8
|
+
protected exec(ctx: RunnerContext, entry?: SourceFile): Promise<void>;
|
|
9
|
+
protected createVars(ctx: RunnerContext, entry?: SourceFile): {
|
|
10
|
+
vars: Record<string, string>;
|
|
11
|
+
env: Record<string, string>;
|
|
12
|
+
};
|
|
13
|
+
protected resolveVars(str: string, vars: Record<string, string>): string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Job } from "../types/Config.types.js";
|
|
2
|
+
import type { ActionHandler, FileHistory, RunnerContext } from "../types/Task.types.js";
|
|
3
|
+
import { AbstractHandler } from "./AbstractHandler.js";
|
|
4
|
+
export declare class FileArchiveHandler extends AbstractHandler implements ActionHandler {
|
|
5
|
+
validateJob(job: Job): void;
|
|
6
|
+
processFiles(ctx: RunnerContext, entries: string[], fileHistory: FileHistory): Promise<void>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ActionHandler, RunnerContext, FileHistory } from "../types/Task.types.js";
|
|
2
|
+
import { AbstractHandler } from "./AbstractHandler.js";
|
|
3
|
+
import type { Job } from "../types/Config.types.js";
|
|
4
|
+
export declare class FileCopyHandler extends AbstractHandler implements ActionHandler {
|
|
5
|
+
validateJob(job: Job): void;
|
|
6
|
+
processFiles(ctx: RunnerContext, entries: string[], fileHistory: FileHistory): Promise<void>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Job } from "../types/Config.types.js";
|
|
2
|
+
import type { ActionHandler, FileHistory, RunnerContext } from "../types/Task.types.js";
|
|
3
|
+
import { AbstractHandler } from "./AbstractHandler.js";
|
|
4
|
+
export declare class FileDeleteHandler extends AbstractHandler implements ActionHandler {
|
|
5
|
+
validateJob(_job: Job): void;
|
|
6
|
+
processFiles(ctx: RunnerContext, entries: string[], fileHistory: FileHistory): Promise<void>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ActionHandler, FileHistory, RunnerContext } from "../types/Task.types.js";
|
|
2
|
+
import { AbstractHandler } from "./AbstractHandler.js";
|
|
3
|
+
import type { Job } from "../types/Config.types.js";
|
|
4
|
+
export declare class FileMoveHandler extends AbstractHandler implements ActionHandler {
|
|
5
|
+
validateJob(job: Job): void;
|
|
6
|
+
processFiles(ctx: RunnerContext, entries: string[], fileHistory: FileHistory): Promise<void>;
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { JobScheduler } from "./tasks/JobScheduler.js";
|
|
2
|
+
import { JobLoader } from "./tasks/JobLoader.js";
|
|
3
|
+
import { JobRunner } from "./tasks/JobRunner.js";
|
|
4
|
+
import type { Job } from "./types/Config.types.js";
|
|
5
|
+
import type { LoaderOptions, RunnerOptions } from "./types/Options.types.js";
|
|
6
|
+
export type { LoaderOptions, RunnerOptions } from "./types/Options.types.js";
|
|
7
|
+
export type { RunnerResult } from "./types/Task.types.js";
|
|
8
|
+
export type { Job } from "./types/Config.types.js";
|
|
9
|
+
export declare function createJobLoader(options?: LoaderOptions): JobLoader;
|
|
10
|
+
export declare function createJobScheduler(options?: RunnerOptions): JobScheduler;
|
|
11
|
+
export declare function createJobRunner(job: Job, options?: RunnerOptions): JobRunner;
|
|
12
|
+
declare const _default: {
|
|
13
|
+
createJobLoader: typeof createJobLoader;
|
|
14
|
+
createJobScheduler: typeof createJobScheduler;
|
|
15
|
+
createJobRunner: typeof createJobRunner;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FileHistory, FileHistoryData } from "../types/Task.types";
|
|
2
|
+
export declare class FileHistoryModel implements FileHistory {
|
|
3
|
+
data: FileHistoryData;
|
|
4
|
+
changed: boolean;
|
|
5
|
+
private included;
|
|
6
|
+
private outdated;
|
|
7
|
+
constructor(data?: FileHistoryData);
|
|
8
|
+
updateSourceEntry(path: string, entry: [number, number]): {
|
|
9
|
+
changed: boolean;
|
|
10
|
+
added: boolean;
|
|
11
|
+
};
|
|
12
|
+
addTargetEntry(path: string, entry: [number, number]): void;
|
|
13
|
+
markTargetOutdated(path: string): void;
|
|
14
|
+
cleanup(): string[];
|
|
15
|
+
private _removeEntry;
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Job, JobAction, JobSource, JobTarget } from "../types/Config.types.js";
|
|
2
|
+
export declare class JobModel {
|
|
3
|
+
id: string;
|
|
4
|
+
action: JobAction;
|
|
5
|
+
command: string;
|
|
6
|
+
shell: boolean | string | undefined;
|
|
7
|
+
args: string[];
|
|
8
|
+
env: Record<string, string>;
|
|
9
|
+
cron: string;
|
|
10
|
+
source: JobSource;
|
|
11
|
+
target: JobTarget;
|
|
12
|
+
dry_run: boolean;
|
|
13
|
+
verbose: boolean;
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
constructor(data: Job, config?: {});
|
|
16
|
+
get sourceIncludes(): string[];
|
|
17
|
+
get sourceExcludes(): string[];
|
|
18
|
+
get targetArchiveName(): string;
|
|
19
|
+
get targetPermissions(): string;
|
|
20
|
+
private resolveDatePattern;
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RunnerContext, RunnerResult } from "../types/Task.types.js";
|
|
2
|
+
import type { JobModel } from "./JobModel.js";
|
|
3
|
+
import { EventEmitter } from "node:events";
|
|
4
|
+
import { PermissionModel } from "./PermissionModel.js";
|
|
5
|
+
import type { JobRunnerSetup } from "./JobRunnerSetup.js";
|
|
6
|
+
export declare class JobRunnerContext implements RunnerContext {
|
|
7
|
+
readonly job: JobModel;
|
|
8
|
+
readonly result: RunnerResult;
|
|
9
|
+
readonly startTime: number;
|
|
10
|
+
readonly sourceDir: string;
|
|
11
|
+
readonly targetDir: string;
|
|
12
|
+
readonly sourceDirs: Set<string>;
|
|
13
|
+
readonly targetDirs: Set<string>;
|
|
14
|
+
readonly targetPermissions: PermissionModel;
|
|
15
|
+
private events;
|
|
16
|
+
private logFd;
|
|
17
|
+
constructor(setup: JobRunnerSetup, job: JobModel, events?: EventEmitter, logFd?: number);
|
|
18
|
+
getLogFd(): number;
|
|
19
|
+
writeLog(msg: string): void;
|
|
20
|
+
processError(error: Error): void;
|
|
21
|
+
processActivity(action: string, path?: string, count?: number): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RunnerResult } from "../types/Task.types.js";
|
|
2
|
+
export declare class JobRunnerResult implements RunnerResult {
|
|
3
|
+
copied: number;
|
|
4
|
+
deleted: number;
|
|
5
|
+
archived: number;
|
|
6
|
+
executed: number;
|
|
7
|
+
pruned: number;
|
|
8
|
+
errors: number;
|
|
9
|
+
startTime: number;
|
|
10
|
+
endTime: number;
|
|
11
|
+
get durationMs(): number;
|
|
12
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type RunnerOptions } from "../types/Options.types.js";
|
|
2
|
+
import { type Job, type JobAction } from "../types/Config.types.js";
|
|
3
|
+
import type { AbstractHandler } from "../handlers/AbstractHandler.js";
|
|
4
|
+
export declare class JobRunnerSetup implements RunnerOptions {
|
|
5
|
+
readonly sourceRoot: string;
|
|
6
|
+
readonly targetRoot: string;
|
|
7
|
+
readonly source2Root: string;
|
|
8
|
+
readonly target2Root: string;
|
|
9
|
+
readonly source3Root: string;
|
|
10
|
+
readonly target3Root: string;
|
|
11
|
+
readonly shell: string | boolean;
|
|
12
|
+
readonly configDir: string;
|
|
13
|
+
readonly tempDir: string;
|
|
14
|
+
readonly logDir: string;
|
|
15
|
+
readonly scriptDir: string;
|
|
16
|
+
private sourceRootDirs;
|
|
17
|
+
private targetRootDirs;
|
|
18
|
+
private handlerMap;
|
|
19
|
+
constructor(options?: RunnerOptions);
|
|
20
|
+
resolveSourceDir(relPath?: string): string;
|
|
21
|
+
resolveTargetDir(relPath?: string): string;
|
|
22
|
+
getActionHandler(action: JobAction): AbstractHandler;
|
|
23
|
+
validateJob(job: Job): void;
|
|
24
|
+
private _validateDir;
|
|
25
|
+
private _resolveDir;
|
|
26
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ScheduledTask } from "node-cron";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import type { Task } from "../types/Task.types.js";
|
|
4
|
+
export declare abstract class AbstractTask<T> implements Task {
|
|
5
|
+
protected cronTask: ScheduledTask;
|
|
6
|
+
protected events: EventEmitter<any>;
|
|
7
|
+
protected errorCount: number;
|
|
8
|
+
private isRunning;
|
|
9
|
+
constructor(cronStr?: string);
|
|
10
|
+
protected abstract run(): Promise<T>;
|
|
11
|
+
schedule(runImmediately?: boolean): void;
|
|
12
|
+
unschedule(): void;
|
|
13
|
+
execute(cb?: (result: T) => void): void;
|
|
14
|
+
onScheduled(cb: () => void): void;
|
|
15
|
+
onStarted(cb: () => void): void;
|
|
16
|
+
onFinished<T>(cb: (result: T) => void): void;
|
|
17
|
+
onError(cb: (error: Error) => void): void;
|
|
18
|
+
gracefulTerminate(timeout?: number): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AbstractTask } from "./AbstractTask.js";
|
|
2
|
+
import { type LoaderOptions } from "../types/Options.types.js";
|
|
3
|
+
import { type Job } from "../types/Config.types.js";
|
|
4
|
+
export declare class JobLoader extends AbstractTask<Job[]> {
|
|
5
|
+
configDir: string;
|
|
6
|
+
private firstRun;
|
|
7
|
+
private jobHistory;
|
|
8
|
+
constructor(options?: LoaderOptions);
|
|
9
|
+
protected run(): Promise<Job[]>;
|
|
10
|
+
loadJobs(): Promise<Job[]>;
|
|
11
|
+
onceLoaded(cb: (jobs: Job[]) => void): void;
|
|
12
|
+
onLoadingError(cb: (jobId: string, message: string) => void): void;
|
|
13
|
+
onJobLoaded(cb: (job: Job, isReload: boolean) => void): void;
|
|
14
|
+
onJobDeleted(cb: (jobId: string) => void): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AbstractTask } from "./AbstractTask.js";
|
|
2
|
+
import { JobRunnerResult } from "../models/JobRunnerResult.js";
|
|
3
|
+
import type { JobModel } from "../models/JobModel.js";
|
|
4
|
+
import type { JobRunnerSetup } from "../models/JobRunnerSetup.js";
|
|
5
|
+
import type { RunnerResult, FileHistory } from "../types/Task.types.js";
|
|
6
|
+
import type { Job } from "../types/Config.types.js";
|
|
7
|
+
export declare class JobRunner extends AbstractTask<RunnerResult> {
|
|
8
|
+
job: JobModel;
|
|
9
|
+
setup: JobRunnerSetup;
|
|
10
|
+
constructor(job: JobModel, setup: JobRunnerSetup);
|
|
11
|
+
onActivity(cb: (action: string, path: string, count: number) => void): void;
|
|
12
|
+
runJob(): Promise<JobRunnerResult>;
|
|
13
|
+
protected run(): Promise<JobRunnerResult>;
|
|
14
|
+
protected loadFileHistory(job: Job): Promise<FileHistory>;
|
|
15
|
+
protected saveFileHistory(job: Job, fileHistory: FileHistory): Promise<void>;
|
|
16
|
+
protected initLog(job: Job): number;
|
|
17
|
+
protected closeLog(fd: number, startTime: number, err?: Error): void;
|
|
18
|
+
protected renameLog(job: Job, tag: string): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { JobRunner } from "./JobRunner.js";
|
|
2
|
+
import { JobRunnerSetup } from "../models/JobRunnerSetup.js";
|
|
3
|
+
import { AbstractTask } from "./AbstractTask.js";
|
|
4
|
+
import type { Job } from "../types/Config.types.js";
|
|
5
|
+
import type { RunnerResult } from "../types/Task.types.js";
|
|
6
|
+
import type { RunnerOptions } from "../types/Options.types.js";
|
|
7
|
+
export declare class JobScheduler extends AbstractTask<void> {
|
|
8
|
+
protected runnerSetup: JobRunnerSetup;
|
|
9
|
+
protected runnerMap: Map<string, JobRunner>;
|
|
10
|
+
private changed;
|
|
11
|
+
private isReload;
|
|
12
|
+
constructor(options?: RunnerOptions);
|
|
13
|
+
get scheduledJobs(): number;
|
|
14
|
+
get tempDir(): string;
|
|
15
|
+
protected run(): Promise<void>;
|
|
16
|
+
unscheduleAll(): void;
|
|
17
|
+
scheduleJobs(jobs: Job[], cb?: (count: number) => void): void;
|
|
18
|
+
scheduleJob(job: Job, defaults?: {}): void;
|
|
19
|
+
unscheduleJob(jobId: string): void;
|
|
20
|
+
isJobScheduled(jobId: string): boolean;
|
|
21
|
+
executeJob(jobId: string): void;
|
|
22
|
+
validateJob(job: Job): void;
|
|
23
|
+
getScheduledJobs(): Job[];
|
|
24
|
+
gracefulTerminate(timeout?: number): Promise<void>;
|
|
25
|
+
onChanged(cb: (initialConfig: boolean) => void): void;
|
|
26
|
+
onJobScheduled(cb: (job: Job, rescheduled: boolean) => void): void;
|
|
27
|
+
onJobStarted(cb: (job: Job) => void): void;
|
|
28
|
+
onJobFinished(cb: (job: Job, stat: RunnerResult) => void): void;
|
|
29
|
+
onJobActivity(cb: (job: Job, activity: string, path: string, count: number) => void): void;
|
|
30
|
+
onJobError(cb: (job: Job, err: Error) => void): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const JobSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodOptional<z.ZodString>;
|
|
4
|
+
cron: z.ZodOptional<z.ZodString>;
|
|
5
|
+
action: z.ZodLiteral<"exec" | "call" | "copy" | "move" | "delete" | "archive">;
|
|
6
|
+
command: z.ZodOptional<z.ZodString>;
|
|
7
|
+
shell: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
8
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
9
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
10
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
11
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
12
|
+
includes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
excludes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
14
|
+
}, z.core.$strict>>;
|
|
15
|
+
target: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
17
|
+
archive_name: z.ZodOptional<z.ZodString>;
|
|
18
|
+
permissions: z.ZodOptional<z.ZodObject<{
|
|
19
|
+
owner: z.ZodOptional<z.ZodString>;
|
|
20
|
+
file_mode: z.ZodOptional<z.ZodString>;
|
|
21
|
+
dir_mode: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strict>>;
|
|
23
|
+
retention: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$strict>>;
|
|
25
|
+
dry_run: z.ZodOptional<z.ZodBoolean>;
|
|
26
|
+
verbose: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
+
}, z.core.$strict>;
|
|
29
|
+
export type Job = {
|
|
30
|
+
id: string;
|
|
31
|
+
} & z.infer<typeof JobSchema>;
|
|
32
|
+
export type JobAction = z.infer<typeof JobSchema.shape.action>;
|
|
33
|
+
export type JobSource = z.infer<typeof JobSchema.shape.source>;
|
|
34
|
+
export type JobTarget = z.infer<typeof JobSchema.shape.target>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type RunnerOptions = {
|
|
2
|
+
configDir?: string;
|
|
3
|
+
tempDir?: string;
|
|
4
|
+
logDir?: string;
|
|
5
|
+
sourceRoot?: string;
|
|
6
|
+
source2Root?: string;
|
|
7
|
+
source3Root?: string;
|
|
8
|
+
targetRoot?: string;
|
|
9
|
+
target2Root?: string;
|
|
10
|
+
target3Root?: string;
|
|
11
|
+
shell?: string | boolean;
|
|
12
|
+
};
|
|
13
|
+
export type LoaderOptions = {
|
|
14
|
+
configDir?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare const ENV: {
|
|
17
|
+
readonly CONFIG_DIR: "CROPS_CONFIG_DIR";
|
|
18
|
+
readonly TEMP_DIR: "CROPS_TEMP_DIR";
|
|
19
|
+
readonly LOG_DIR: "CROPS_LOG_DIR";
|
|
20
|
+
readonly SOURCE_ROOT: "CROPS_SOURCE_ROOT";
|
|
21
|
+
readonly TARGET_ROOT: "CROPS_TARGET_ROOT";
|
|
22
|
+
readonly SOURCE_2_ROOT: "CROPS_SOURCE_2_ROOT";
|
|
23
|
+
readonly TARGET_2_ROOT: "CROPS_TARGET_2_ROOT";
|
|
24
|
+
readonly SOURCE_3_ROOT: "CROPS_SOURCE_3_ROOT";
|
|
25
|
+
readonly TARGET_3_ROOT: "CROPS_TARGET_3_ROOT";
|
|
26
|
+
readonly EXEC_SHELL: "CROPS_EXEC_SHELL";
|
|
27
|
+
readonly PLIMIT_SPAWN: "CROPS_PLIMIT_SPAWN";
|
|
28
|
+
readonly PLIMIT_FS: "CROPS_PLIMIT_FS";
|
|
29
|
+
readonly API_KEY: "CROPS_API_KEY";
|
|
30
|
+
readonly BASE_URL: "CROPS_BASE_URL";
|
|
31
|
+
readonly HOST: "CROPS_HOST";
|
|
32
|
+
readonly PORT: "CROPS_PORT";
|
|
33
|
+
readonly TZ: "TZ";
|
|
34
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Stats } from "fs-extra";
|
|
2
|
+
import type { Job } from "./Config.types.js";
|
|
3
|
+
import type { JobModel } from "../models/JobModel.js";
|
|
4
|
+
import type { PermissionModel } from "../models/PermissionModel.js";
|
|
5
|
+
export type RunnerResult = {
|
|
6
|
+
copied: number;
|
|
7
|
+
deleted: number;
|
|
8
|
+
archived: number;
|
|
9
|
+
executed: number;
|
|
10
|
+
pruned: number;
|
|
11
|
+
errors: number;
|
|
12
|
+
startTime: number;
|
|
13
|
+
endTime: number;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
};
|
|
16
|
+
export type SourceFile = {
|
|
17
|
+
sourceEntry: string;
|
|
18
|
+
sourcePath: string;
|
|
19
|
+
stats: Stats;
|
|
20
|
+
};
|
|
21
|
+
export type FileHistoryData = {
|
|
22
|
+
source: Record<string, [number, number]>;
|
|
23
|
+
target: Record<string, [number, number]>;
|
|
24
|
+
};
|
|
25
|
+
export type FileHistory = {
|
|
26
|
+
data: FileHistoryData;
|
|
27
|
+
changed: boolean;
|
|
28
|
+
updateSourceEntry(path: string, [mtime, atime]: [number, number]): {
|
|
29
|
+
changed: boolean;
|
|
30
|
+
added: boolean;
|
|
31
|
+
};
|
|
32
|
+
addTargetEntry(path: string, [mtime, atime]: [number, number]): void;
|
|
33
|
+
markTargetOutdated(path: string): void;
|
|
34
|
+
cleanup(): string[];
|
|
35
|
+
};
|
|
36
|
+
export interface Task {
|
|
37
|
+
schedule(runImmediately?: boolean): void;
|
|
38
|
+
unschedule(): void;
|
|
39
|
+
execute(cb: () => void): void;
|
|
40
|
+
onScheduled(cb: () => void): void;
|
|
41
|
+
onStarted(cb: () => void): void;
|
|
42
|
+
onFinished<T>(cb: (result: T) => void): void;
|
|
43
|
+
onError(cb: (error: Error) => void): void;
|
|
44
|
+
gracefulTerminate(timeout: number): void;
|
|
45
|
+
}
|
|
46
|
+
export interface RunnerContext {
|
|
47
|
+
job: JobModel;
|
|
48
|
+
result: RunnerResult;
|
|
49
|
+
startTime: number;
|
|
50
|
+
sourceDir: string;
|
|
51
|
+
sourceDirs: Set<string>;
|
|
52
|
+
targetDir: string;
|
|
53
|
+
targetDirs: Set<string>;
|
|
54
|
+
targetPermissions: PermissionModel;
|
|
55
|
+
getLogFd(): number;
|
|
56
|
+
writeLog(message: string): void;
|
|
57
|
+
processActivity(action: string, path?: string, count?: number): void;
|
|
58
|
+
processError(error: Error): void;
|
|
59
|
+
}
|
|
60
|
+
export interface ActionHandler {
|
|
61
|
+
validateJob(Job: Job): void;
|
|
62
|
+
process(ctx: RunnerContext): Promise<void>;
|
|
63
|
+
processFiles(ctx: RunnerContext, entries: string[], fileHistory: FileHistory): Promise<void>;
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mtakla/cronops",
|
|
3
|
-
"version": "0.1.1-
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1-rc5",
|
|
4
|
+
"description": "Automated cross-container file lifecycle management with cron scheduling",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"author": "nevereven",
|
|
6
|
+
"author": "mtakla@nevereven",
|
|
7
7
|
"license": "ISC",
|
|
8
|
+
"funding": {
|
|
9
|
+
"type": "buymeacoffee",
|
|
10
|
+
"url": "https://www.buymeacoffee.com/nevereven"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/mtakla/cronops#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/mtakla/cronops.git"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/mtakla/cronops/issues"
|
|
19
|
+
},
|
|
8
20
|
"bin": {
|
|
9
|
-
"cronops": "dist/server.js"
|
|
21
|
+
"cronops": "./dist/server.js"
|
|
10
22
|
},
|
|
11
23
|
"main": "./dist/server.js",
|
|
12
24
|
"types": "./dist/index.d.ts",
|
|
13
25
|
"engines": {
|
|
14
|
-
"node": ">=24"
|
|
26
|
+
"node": ">=24.0.0",
|
|
27
|
+
"npm": ">=10.0.0"
|
|
15
28
|
},
|
|
16
29
|
"keywords": [
|
|
17
30
|
"cron",
|
|
@@ -26,7 +39,7 @@
|
|
|
26
39
|
"lifecycle",
|
|
27
40
|
"cross-container"
|
|
28
41
|
],
|
|
29
|
-
"
|
|
42
|
+
"exports": {
|
|
30
43
|
".": {
|
|
31
44
|
"types": "./dist/index.d.ts",
|
|
32
45
|
"import": "./dist/index.js"
|
|
@@ -42,15 +55,15 @@
|
|
|
42
55
|
"chalk": "5.6.2",
|
|
43
56
|
"date-fns": "4.1.0",
|
|
44
57
|
"fast-glob": "3.3.3",
|
|
58
|
+
"fastify": "5.7.4",
|
|
45
59
|
"figlet": "1.10.0",
|
|
46
60
|
"fs-extra": "11.3.3",
|
|
47
|
-
"p-limit": "7.3.0",
|
|
48
61
|
"node-cron": "4.2.1",
|
|
62
|
+
"p-limit": "7.3.0",
|
|
49
63
|
"parse-duration": "2.1.5",
|
|
50
64
|
"tar-fs": "3.1.1",
|
|
51
65
|
"yaml": "2.8.2",
|
|
52
|
-
"zod": "4.3.6"
|
|
53
|
-
"fastify": "5.7.4"
|
|
66
|
+
"zod": "4.3.6"
|
|
54
67
|
},
|
|
55
68
|
"devDependencies": {
|
|
56
69
|
"@biomejs/biome": "^2.3.6",
|
|
@@ -59,9 +72,8 @@
|
|
|
59
72
|
"@types/shell-quote": "^1.7.5",
|
|
60
73
|
"@types/tar-fs": "2.0.4",
|
|
61
74
|
"@vitest/coverage-v8": "^4.0.14",
|
|
62
|
-
"typescript": "^5.9.3",
|
|
63
75
|
"typedoc": "^0.28.16",
|
|
64
|
-
"
|
|
76
|
+
"typescript": "^5.9.3",
|
|
65
77
|
"vitest": "^4.0.14"
|
|
66
78
|
},
|
|
67
79
|
"scripts": {
|
|
@@ -72,8 +84,9 @@
|
|
|
72
84
|
"coverage": "vitest run --coverage",
|
|
73
85
|
"loadtest": "tsc && node ./dist/tests/loadtest.js",
|
|
74
86
|
"clean": "rm -rf dist",
|
|
75
|
-
"build": "tsc",
|
|
76
87
|
"dev": "tsc && node ./dist/server.js",
|
|
88
|
+
"build": "tsc",
|
|
89
|
+
"build:types": "tsc --declaration",
|
|
77
90
|
"docker:build": "docker build --load --target app -t ghcr.io/mtakla/cronops:latest .",
|
|
78
91
|
"docker:build-docs": "docker build --load --target docs -t ghcr.io/mtakla/cronops-docs:latest ."
|
|
79
92
|
}
|