@mtakla/cronops 0.1.1-rc6 → 0.1.1

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/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  # CronOps
2
2
 
3
- [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://github.com/mtakla/cronops/blob/master/LICENSE)
4
- [![Coverage Status](https://img.shields.io/badge/coverage-95%25-green)](https://img.shields.io/badge/coverage-95%25-green)
3
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://github.com/mtakla/cronops/blob/main/LICENSE)
4
+ [![Coverage Status](https://img.shields.io/badge/Coverage-95%25-green)](https://img.shields.io/badge/coverage-95%25-green)
5
+ [![Docs](https://img.shields.io/badge/TypeDoc-docs-blue)](https://mtakla.github.io/cronops/)
5
6
  [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Donate-orange.svg)](https://www.buymeacoffee.com/nevereven)
6
7
 
7
8
  **CronOps** is a lightweight, cron-based file management and system task scheduler for containerized environments. It automates copying, moving, archiving, and cleaning up files across mounted volumes — keeping your storage tidy, enabling seamless file exchange between containerized services, and triggering regular tasks in your development, integration or production environments.
8
9
 
9
- > [!WARNING]
10
- > This project is in early BETA. Production use is not yet recommened!
10
+ **WARNING**
11
+ > This project is under active development. Production use is not yet recommended.
11
12
 
12
- ## 💡 Why CronOps?
13
+ ## Why CronOps?
13
14
 
14
15
  In containerized workflows, files often accumulate in volumes: downloads, logs, temporary exports, backups. CronOps acts as your **digital janitor**, running scheduled jobs that:
15
16
 
@@ -47,6 +48,8 @@ CronOps is built and optimized to run as a Docker container itself.
47
48
 
48
49
  ```sh
49
50
  docker run \
51
+ --name cronops \
52
+ -p 8083:8083 \
50
53
  -v ./config:/config \
51
54
  -v ./data:/io/source \
52
55
  -v ./data:/io/target \
@@ -82,8 +85,8 @@ target:
82
85
 
83
86
  Now you can add more job configuration files to `./config/jobs`. For detailes, see [job configuration](#job-configuration) section below.
84
87
 
85
- > [!NOTE]
86
- > You do not need to restart the server after changing job files. The server identifies any changes and will hot reload the configuration. If a job configuration is invalid, an appropriate message will appear in the docker logs and the specific job will not be scheduled.
88
+ 🛈 **Note**
89
+ > You don't need to restart the server after changing job files. The server identifies any changes and will hot reload the configuration. If a job configuration is invalid, an appropriate message will appear in the docker logs and the specific job will not be scheduled.
87
90
 
88
91
  ### Using Docker Compose
89
92
 
@@ -118,43 +121,52 @@ docker compose pull && docker compose up -d
118
121
 
119
122
  in the same directory where `compose.yaml` has been created.
120
123
 
121
- ### Enable admin Web-API
124
+ ### Admin Web-API
122
125
 
123
126
  To enable **admin Web-API**, just set `CROPS_API_KEY` environment variable. Details, see [Configuration](#configuration) section below.
124
127
 
125
128
 
126
- ### Manual installation
129
+ ## Manual installation
127
130
 
128
131
  This requires [Node.js](https://nodejs.org/) (>= v24) to be installed on your server.
129
132
 
130
- First step is to create an empty CronOps app directory with a `.env` file that contains your configuration settings, e.g.:
133
+ To install & start CronOps, type:
134
+
131
135
 
132
- ```ini
133
- CROPS_SOURCE_ROOT=./data # change as you like
134
- CROPS_TARGET_ROOT=./data # change as you like
135
- CROPS_CONFIG_DIR=./config # default is ~/.cronops/config
136
- CROPS_LOG_DIR=./logs # default is ~/.cronops/logs
136
+ ```sh
137
+ npx @mtakla/cronops
137
138
  ```
138
139
 
139
- To install & start CronOps, type:
140
+ For configuration, create an empty folder with an `.env` file that contains your config settings (see [Configuration](#configuration) section below).
141
+
142
+ ```dotenv
143
+ CROPS_CONFIG_DIR=./config
144
+ CROPS_TARGET_ROOT=./data
145
+ CROPS_SOURCE_ROOT=./data
146
+ ```
140
147
 
141
- ```bash
148
+ To start CronOps with
149
+
150
+ ```sh
142
151
  npx @dotenvx/dotenvx run -- npx @mtakla/cronops
143
152
  ```
144
153
 
145
154
  This will ...
146
155
 
147
- - download the latest version of dotenvx & cronops
148
- - load the environment settings defined in the `.env` file
149
- - start the cronops service with the loaded environment settings
150
- - create config directory in `./config` if it doesn't exist
151
- - create logs directory in `./logs` if it doesn't exist
156
+ - download the latest version of **dotenvx** and **cronops**
157
+ - load environment settings defined in the `.env` file
158
+ - create job config directory in `./config` with some example jobs
159
+ - starts the CronOps service
160
+ - the **example job** `[example job]` is active by default and scheduled to run every 5 seconds:
161
+ - the job will move files found in `./data/inbox` to `./data/outbox`
162
+ - in addition, all files moved to `./data/outbox` will be automatically deleted after 30 seconds
163
+
152
164
 
153
165
  You can now add job configuration files to `./config/jobs` directory. Each YAML file in this directory defines a job. The server will hot reload when job files are added, modified, or removed.
154
166
 
155
- ### Use in your code
167
+ ## Use in your code
156
168
 
157
- Install CronOps in your code using npm
169
+ Install CronOps in your project using npm
158
170
 
159
171
  ```
160
172
  npm install @mtakla/cronops --save
@@ -193,36 +205,38 @@ runner.onFinished(() => {
193
205
  console.log("job finished!");
194
206
  });
195
207
 
196
- runner.onError(() => {
197
- console.log("job finished!");
208
+ runner.onError((err) => {
209
+ console.log(`job failed with ${err.message}`);
198
210
  });
199
211
 
200
212
  // finally schedule job
201
213
  runner.schedule();
202
214
  ```
203
215
 
216
+ For more details, see the [TypeDoc documentation](https://mtakla.github.io/cronops/)
217
+
204
218
 
205
219
  ## Configuration
206
220
 
207
221
  The CronOps service can be configured with the following environment variables:
208
222
 
209
- | ENV | Description | Docker defaults |
210
- | --------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------- |
211
- | `CROPS_SOURCE_ROOT` | Path to primary source directory | `/io/source` |
212
- | `CROPS_TARGET_ROOT` | Path to primary target directory | `/io/target` |
213
- | `CROPS_SOURCE_2_ROOT` | Path to secondary source directory | `/io/source2` |
214
- | `CROPS_TARGET_2_ROOT` | Path to secondary target directory | `/io/target2` |
215
- | `CROPS_SOURCE_3_ROOT` | Path to tertiary source directory | `/io/source3` |
216
- | `CROPS_TARGET_3_ROOT` | Path to tertiary target directory | `/io/target3` |
217
- | `CROPS_CONFIG_DIR` | Path to the config directory where job files are located | `/config` |
218
- | `CROPS_TEMP_DIR` | Path to temporary folder used for dry-run mode | `/data/temp` |
219
- | `CROPS_LOG_DIR` | Path to directory where job logs and file history are stored | `/data/logs` |
220
- | `CROPS_HOST` | Host address for the Admin API server | `0.0.0.0` |
221
- | `CROPS_PORT` | Port for the Admin API server | `8083` |
222
- | `CROPS_EXEC_SHELL` | (*Optional*) Default shell for `exec` actions. Can be `false`, `true`, or path | `false` |
223
- | `CROPS_API_KEY` | (*Optional*) API key to secure admin API endpoints. Must be a hex‑encoded 256‑bit secret (e.g. 'openssl rand -hex 32') | - |
224
- | `CROPS_BASE_URL` | (*Optional*) Base URL for admin API and OpenAPI docs | - |
225
- | `TZ` | (*Optional*) Timezone for cron scheduling (standard timezone format) | `UTC` |
223
+ | ENV | Description | Docker defaults |
224
+ | --------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------- |
225
+ | `CROPS_SOURCE_ROOT` | Path to primary source directory | `/io/source` |
226
+ | `CROPS_TARGET_ROOT` | Path to primary target directory | `/io/target` |
227
+ | `CROPS_SOURCE_2_ROOT` | Path to secondary source directory | `/io/source2` |
228
+ | `CROPS_TARGET_2_ROOT` | Path to secondary target directory | `/io/target2` |
229
+ | `CROPS_SOURCE_3_ROOT` | Path to tertiary source directory | `/io/source3` |
230
+ | `CROPS_TARGET_3_ROOT` | Path to tertiary target directory | `/io/target3` |
231
+ | `CROPS_CONFIG_DIR` | Path to the config directory where job files are located | `/config` |
232
+ | `CROPS_TEMP_DIR` | Path to temporary folder used for dry-run mode | `/data/temp` |
233
+ | `CROPS_LOG_DIR` | Path to directory where job logs and file history are stored | `/data/logs` |
234
+ | `CROPS_HOST` | Host address for the admin API server | `0.0.0.0` |
235
+ | `CROPS_PORT` | Port for the admin API server | `8083` |
236
+ | `CROPS_EXEC_SHELL` | (*Optional*) Default shell for `exec` actions. Can be `false` (no shell), `true` (default shell), or path like `/bin/bash` | `false` |
237
+ | `CROPS_API_KEY` | (*Optional*) API key to secure admin API endpoints. Must be a hex‑encoded 256‑bit secret (e.g. 'openssl rand -hex 32') | - |
238
+ | `CROPS_BASE_URL` | (*Optional*) Base URL for admin API and OpenAPI docs | - |
239
+ | `TZ` | (*Optional*) Timezone for cron scheduling (standard timezone format) | `UTC` |
226
240
 
227
241
 
228
242
  ## Job Configuration
@@ -248,9 +262,8 @@ dry_run: true
248
262
  enabled: false
249
263
  ```
250
264
 
251
- > [!NOTE]
252
- You can change the job configuration at any time and the server will hot reload and schedule the new job configuration.
253
- Be aware that once the job config has been changed, active running tasks will be terminated and the job will be rescheduled
265
+ 🛈 **Note**
266
+ > You can change the job configuration at any time and the server will hot reload and schedule the new job configuration. Be aware that once the job config has been changed, active running tasks will be (gracefully) terminated and the job will be rescheduled
254
267
 
255
268
  ### Job Actions
256
269
 
@@ -267,10 +280,10 @@ CronOps supports 5 different job actions:
267
280
 
268
281
  - **`exec`** - Execute a command or script. Use with `command`, `args`, `shell`, and `env` properties
269
282
 
270
- > [!TIP]
271
- > **Path References**: Use `$1`, `$2`, or `$3` in job paths to reference configured root directories.
272
- > For source paths, these map to `CROPS_SOURCE_ROOT`, `CROPS_SOURCE_2_ROOT`, and `CROPS_SOURCE_3_ROOT`.
273
- > For target paths, they map to `CROPS_TARGET_ROOT`, `CROPS_TARGET_2_ROOT`, and `CROPS_TARGET_3_ROOT`.
283
+ 💡 **Tip**
284
+ > Use `$1`, `$2`, or `$3` in job paths to refer to the configured roots.
285
+ > - **Source:** `$1` `CROPS_SOURCE_ROOT`, `$2` → `CROPS_SOURCE_2_ROOT`, `$3` `CROPS_SOURCE_3_ROOT`
286
+ > - **Target:** `$1` `CROPS_TARGET_ROOT`, `$2` → `CROPS_TARGET_2_ROOT`, `$3` `CROPS_TARGET_3_ROOT`
274
287
 
275
288
  ### Job Configuration examples
276
289
 
@@ -362,7 +375,7 @@ For jobs of action type `exec` the following environment variables are available
362
375
  | `CROPS_TEMP_DIR` | absolute path to the configured temp directory |
363
376
  | `CROPS_LOG_DIR` | absolute path to the configured log directory |
364
377
  | `CROPS_DRY_RUN` | "true", if dry_run mode is enabled |
365
- | `CROPS_VERBOSE` | "true", if dry_run mode is enabled |
378
+ | `CROPS_VERBOSE` | "true", if verbose mode is enabled |
366
379
 
367
380
 
368
381
  If the exec action is configured to run on selected `source` files:
@@ -402,16 +415,16 @@ If the exec action is configured to run on selected `source` files:
402
415
 
403
416
 
404
417
 
405
- ## Security considerations & Trouble shooting
418
+ ## Security considerations
406
419
 
407
- > [!NOTE]
420
+ 🛈 **Note**
408
421
  > It is strongly advised against accessing or modifying the data directly on the host system within Docker's internal volume storage path (typically `/var/lib/docker/volumes/`).
409
422
 
410
- > [!WARNING]
423
+ **WARNING**
411
424
  > **Hazardous Misconfiguration**
412
425
  >
413
- > By default, CronOps runs as user/group **1000:1000** to follow a security‑first principle.
414
- > You *can* run it as root by setting `PUID=0` and `PGID=0`, but **this is dangerous**.
426
+ > By default, the CronOps docker container runs as user/group **1000:1000** to follow a security‑first principle.
427
+ > You *can* run it as root by setting `PUID=0` and `PGID=0`, but **this is not recommended and can be dangerous**.
415
428
  >
416
429
  > When running as root, bind‑mounted host volumes (source/target directories) may map to critical system paths on the host (e.g. `/etc`, `/var`).
417
430
  > This creates a **high‑risk security scenario**:
@@ -12,4 +12,4 @@ source:
12
12
  - "**/*.log"
13
13
  target:
14
14
  dir: /outbox
15
- retention: "20s"
15
+ retention: "22s"
@@ -12,19 +12,144 @@ export declare const openapi: {
12
12
  bearerFormat: string;
13
13
  };
14
14
  };
15
+ parameters: {
16
+ JobId: {
17
+ name: string;
18
+ in: string;
19
+ required: boolean;
20
+ schema: {
21
+ type: string;
22
+ };
23
+ };
24
+ };
25
+ schemas: {
26
+ Job: {
27
+ type: string;
28
+ additionalProperties: boolean;
29
+ properties: {
30
+ id: {
31
+ type: string;
32
+ example: string;
33
+ };
34
+ cron: {
35
+ type: string;
36
+ minLength: number;
37
+ example: string;
38
+ };
39
+ action: {
40
+ type: string;
41
+ enum: string[];
42
+ example: string;
43
+ };
44
+ command: {
45
+ type: string;
46
+ };
47
+ shell: {
48
+ anyOf: ({
49
+ type: string;
50
+ minLength?: undefined;
51
+ } | {
52
+ type: string;
53
+ minLength: number;
54
+ })[];
55
+ };
56
+ args: {
57
+ type: string;
58
+ items: {
59
+ type: string;
60
+ minLength: number;
61
+ };
62
+ minItems: number;
63
+ };
64
+ env: {
65
+ type: string;
66
+ propertyNames: {
67
+ type: string;
68
+ pattern: string;
69
+ };
70
+ additionalProperties: {
71
+ type: string;
72
+ };
73
+ };
74
+ source: {
75
+ type: string;
76
+ additionalProperties: boolean;
77
+ properties: {
78
+ dir: {
79
+ type: string;
80
+ minLength: number;
81
+ };
82
+ includes: {
83
+ type: string;
84
+ items: {
85
+ type: string;
86
+ minLength: number;
87
+ };
88
+ minItems: number;
89
+ };
90
+ excludes: {
91
+ type: string;
92
+ items: {
93
+ type: string;
94
+ minLength: number;
95
+ };
96
+ minItems: number;
97
+ };
98
+ };
99
+ };
100
+ target: {
101
+ type: string;
102
+ additionalProperties: boolean;
103
+ properties: {
104
+ dir: {
105
+ type: string;
106
+ minLength: number;
107
+ };
108
+ archive_name: {
109
+ type: string;
110
+ minLength: number;
111
+ };
112
+ permissions: {
113
+ type: string;
114
+ additionalProperties: boolean;
115
+ properties: {
116
+ owner: {
117
+ type: string;
118
+ minLength: number;
119
+ };
120
+ file_mode: {
121
+ type: string;
122
+ minLength: number;
123
+ };
124
+ dir_mode: {
125
+ type: string;
126
+ minLength: number;
127
+ };
128
+ };
129
+ };
130
+ retention: {
131
+ type: string;
132
+ minLength: number;
133
+ };
134
+ };
135
+ };
136
+ dry_run: {
137
+ type: string;
138
+ };
139
+ verbose: {
140
+ type: string;
141
+ };
142
+ enabled: {
143
+ type: string;
144
+ };
145
+ };
146
+ };
147
+ };
15
148
  };
16
- tags: ({
149
+ tags: {
17
150
  name: string;
18
151
  description: string;
19
- externalDocs?: never;
20
- } | {
21
- name: string;
22
- description: string;
23
- externalDocs: {
24
- description: string;
25
- url: string;
26
- };
27
- })[];
152
+ }[];
28
153
  paths: {
29
154
  "/health": {
30
155
  get: {
@@ -83,17 +208,78 @@ export declare const openapi: {
83
208
  };
84
209
  };
85
210
  };
86
- "/api/jobs/trigger/{jobId}": {
87
- post: {
211
+ "/api/jobs": {
212
+ get: {
213
+ summary: string;
214
+ tags: string[];
215
+ responses: {
216
+ "200": {
217
+ description: string;
218
+ content: {
219
+ "application/json": {
220
+ schema: {
221
+ type: string;
222
+ items: {
223
+ $ref: string;
224
+ };
225
+ };
226
+ };
227
+ };
228
+ };
229
+ };
230
+ };
231
+ };
232
+ "/api/jobs/{jobId}": {
233
+ get: {
88
234
  summary: string;
89
235
  tags: string[];
90
236
  parameters: {
91
- name: string;
92
- in: string;
93
- required: boolean;
94
- schema: {
95
- type: string;
237
+ $ref: string;
238
+ }[];
239
+ responses: {
240
+ "200": {
241
+ description: string;
242
+ content: {
243
+ "application/json": {
244
+ schema: {
245
+ $ref: string;
246
+ };
247
+ };
248
+ };
249
+ };
250
+ "404": {
251
+ description: string;
252
+ };
253
+ };
254
+ };
255
+ };
256
+ "/api/status": {
257
+ get: {
258
+ summary: string;
259
+ tags: string[];
260
+ responses: {
261
+ "200": {
262
+ description: string;
263
+ content: {
264
+ "application/json": {
265
+ schema: {
266
+ type: string;
267
+ items: {
268
+ $ref: string;
269
+ };
270
+ };
271
+ };
272
+ };
96
273
  };
274
+ };
275
+ };
276
+ };
277
+ "/api/trigger/{jobId}": {
278
+ post: {
279
+ summary: string;
280
+ tags: string[];
281
+ parameters: {
282
+ $ref: string;
97
283
  }[];
98
284
  responses: {
99
285
  "200": {
@@ -123,18 +309,10 @@ export declare const openapi: {
123
309
  };
124
310
  };
125
311
  };
126
- "/api/jobs/pause/{jobId}": {
312
+ "/api/pause": {
127
313
  post: {
128
314
  summary: string;
129
315
  tags: string[];
130
- parameters: {
131
- name: string;
132
- in: string;
133
- required: boolean;
134
- schema: {
135
- type: string;
136
- };
137
- }[];
138
316
  responses: {
139
317
  "200": {
140
318
  description: string;
@@ -147,9 +325,9 @@ export declare const openapi: {
147
325
  type: string;
148
326
  example: boolean;
149
327
  };
150
- jobId: {
328
+ jobs: {
151
329
  type: string;
152
- example: string;
330
+ example: number;
153
331
  };
154
332
  };
155
333
  };
@@ -159,17 +337,12 @@ export declare const openapi: {
159
337
  };
160
338
  };
161
339
  };
162
- "/api/jobs/resume/{jobId}": {
340
+ "/api/pause/job/{jobId}": {
163
341
  post: {
164
342
  summary: string;
165
343
  tags: string[];
166
344
  parameters: {
167
- name: string;
168
- in: string;
169
- required: boolean;
170
- schema: {
171
- type: string;
172
- };
345
+ $ref: string;
173
346
  }[];
174
347
  responses: {
175
348
  "200": {
@@ -179,7 +352,7 @@ export declare const openapi: {
179
352
  schema: {
180
353
  type: string;
181
354
  properties: {
182
- resumed: {
355
+ paused: {
183
356
  type: string;
184
357
  example: boolean;
185
358
  };
@@ -195,10 +368,13 @@ export declare const openapi: {
195
368
  };
196
369
  };
197
370
  };
198
- "/api/jobs/pause/": {
371
+ "/api/resume/job/{jobId}": {
199
372
  post: {
200
373
  summary: string;
201
374
  tags: string[];
375
+ parameters: {
376
+ $ref: string;
377
+ }[];
202
378
  responses: {
203
379
  "200": {
204
380
  description: string;
@@ -207,13 +383,13 @@ export declare const openapi: {
207
383
  schema: {
208
384
  type: string;
209
385
  properties: {
210
- paused: {
386
+ resumed: {
211
387
  type: string;
212
388
  example: boolean;
213
389
  };
214
- jobs: {
390
+ jobId: {
215
391
  type: string;
216
- example: number;
392
+ example: string;
217
393
  };
218
394
  };
219
395
  };
@@ -223,7 +399,7 @@ export declare const openapi: {
223
399
  };
224
400
  };
225
401
  };
226
- "/api/jobs/resume/": {
402
+ "/api/resume": {
227
403
  post: {
228
404
  summary: string;
229
405
  tags: string[];